ECS refactor
This commit is contained in:
parent
87f5c2f842
commit
b2212a279c
16 changed files with 598 additions and 200 deletions
29
internal/game/components/common.go
Normal file
29
internal/game/components/common.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package components
|
||||
|
||||
import "code.haedhutner.dev/mvv/LastMUD/internal/game/ecs"
|
||||
|
||||
const (
|
||||
TypeName ecs.ComponentType = iota
|
||||
TypeDescription
|
||||
TypePlayerState
|
||||
TypeInRoom
|
||||
TypeNeighbors
|
||||
TypeIsRoom
|
||||
TypeIsPlayer
|
||||
)
|
||||
|
||||
type NameComponent struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (c NameComponent) Type() ecs.ComponentType {
|
||||
return TypeName
|
||||
}
|
||||
|
||||
type DescriptionComponent struct {
|
||||
Description string
|
||||
}
|
||||
|
||||
func (c DescriptionComponent) Type() ecs.ComponentType {
|
||||
return TypeDescription
|
||||
}
|
35
internal/game/components/player.go
Normal file
35
internal/game/components/player.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
package components
|
||||
|
||||
import "code.haedhutner.dev/mvv/LastMUD/internal/game/ecs"
|
||||
|
||||
type PlayerState = byte
|
||||
|
||||
const (
|
||||
PlayerStateJoining PlayerState = iota
|
||||
PlayerStateLoggingIn
|
||||
PlayerStateRegistering
|
||||
PlayerStatePlaying
|
||||
PlayerStateLeaving
|
||||
)
|
||||
|
||||
type PlayerStateComponent struct {
|
||||
State PlayerState
|
||||
}
|
||||
|
||||
func (c PlayerStateComponent) Type() ecs.ComponentType {
|
||||
return TypePlayerState
|
||||
}
|
||||
|
||||
type InRoomComponent struct {
|
||||
Room ecs.Entity
|
||||
}
|
||||
|
||||
func (c InRoomComponent) Type() ecs.ComponentType {
|
||||
return TypeInRoom
|
||||
}
|
||||
|
||||
type IsPlayerComponent struct{}
|
||||
|
||||
func (c IsPlayerComponent) Type() ecs.ComponentType {
|
||||
return TypeIsPlayer
|
||||
}
|
18
internal/game/components/room.go
Normal file
18
internal/game/components/room.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package components
|
||||
|
||||
import "code.haedhutner.dev/mvv/LastMUD/internal/game/ecs"
|
||||
|
||||
type IsRoomComponent struct {
|
||||
}
|
||||
|
||||
func (c IsRoomComponent) Type() ecs.ComponentType {
|
||||
return TypeIsRoom
|
||||
}
|
||||
|
||||
type NeighborsComponent struct {
|
||||
North, South, East, West ecs.Entity
|
||||
}
|
||||
|
||||
func (c NeighborsComponent) Type() ecs.ComponentType {
|
||||
return TypeNeighbors
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package ecs
|
||||
|
||||
type PlayerState = byte
|
||||
|
||||
const (
|
||||
PlayerStateJoining PlayerState = iota
|
||||
PlayerStateLoggingIn
|
||||
PlayerStateRegistering
|
||||
PlayerStatePlaying
|
||||
PlayerStateLeaving
|
||||
)
|
||||
|
||||
type PlayerStateComponent struct {
|
||||
State PlayerState
|
||||
}
|
||||
|
||||
type NameComponent struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type DescriptionComponent struct {
|
||||
Description string
|
||||
}
|
||||
|
||||
type InRoomComponent struct {
|
||||
InRoom Entity
|
||||
}
|
||||
|
||||
type NeighboringRoomsComponent struct {
|
||||
North, South, East, West Entity
|
||||
}
|
196
internal/game/ecs/ecs.go
Normal file
196
internal/game/ecs/ecs.go
Normal file
|
@ -0,0 +1,196 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Entity uuid.UUID
|
||||
|
||||
func CreateEntity(uuid uuid.UUID) Entity {
|
||||
return Entity(uuid)
|
||||
}
|
||||
|
||||
func NewEntity() Entity {
|
||||
return Entity(uuid.New())
|
||||
}
|
||||
|
||||
func NilEntity() Entity {
|
||||
return Entity(uuid.Nil)
|
||||
}
|
||||
|
||||
func (e Entity) AsUUID() uuid.UUID {
|
||||
return uuid.UUID(e)
|
||||
}
|
||||
|
||||
type ComponentType int16
|
||||
|
||||
type Resource string
|
||||
|
||||
type Component interface {
|
||||
Type() ComponentType
|
||||
}
|
||||
|
||||
type ComponentStorage[T Component] struct {
|
||||
forType ComponentType
|
||||
storage map[Entity]T
|
||||
}
|
||||
|
||||
func CreateComponentStorage[T Component](forType ComponentType) *ComponentStorage[T] {
|
||||
return &ComponentStorage[T]{
|
||||
forType: forType,
|
||||
storage: map[Entity]T{},
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *ComponentStorage[T]) ComponentType() ComponentType {
|
||||
return cs.forType
|
||||
}
|
||||
|
||||
func (cs *ComponentStorage[T]) Set(e Entity, component T) {
|
||||
cs.storage[e] = component
|
||||
}
|
||||
|
||||
func (cs *ComponentStorage[T]) Get(e Entity) (component T, ok bool) {
|
||||
component, ok = cs.storage[e]
|
||||
return
|
||||
}
|
||||
|
||||
func (cs *ComponentStorage[T]) Delete(e Entity) {
|
||||
delete(cs.storage, e)
|
||||
}
|
||||
|
||||
func (cs *ComponentStorage[T]) All() map[Entity]T {
|
||||
return cs.storage
|
||||
}
|
||||
|
||||
type System struct {
|
||||
name string
|
||||
priority int
|
||||
work func(world *World, delta time.Duration) (err error)
|
||||
}
|
||||
|
||||
func CreateSystem(name string, priority int, work func(world *World, delta time.Duration) (err error)) *System {
|
||||
return &System{
|
||||
name: name,
|
||||
priority: priority,
|
||||
work: work,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *System) Priority() int {
|
||||
return s.priority
|
||||
}
|
||||
|
||||
func (s *System) DoWork(world *World, delta time.Duration) {
|
||||
err := s.work(world, delta)
|
||||
|
||||
if err != nil {
|
||||
logging.Error("Error in system '", s.name, "': ", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type World struct {
|
||||
systems []*System
|
||||
componentsByType map[ComponentType]any
|
||||
componentsByEntity map[Entity]map[ComponentType]any
|
||||
resources map[Resource]any
|
||||
}
|
||||
|
||||
func CreateWorld() (world *World) {
|
||||
world = &World{
|
||||
systems: []*System{},
|
||||
componentsByType: map[ComponentType]any{},
|
||||
componentsByEntity: map[Entity]map[ComponentType]any{}, // TODO: Can't figure out use-case right now
|
||||
resources: map[Resource]any{},
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (w *World) Tick(delta time.Duration) {
|
||||
for _, s := range w.systems {
|
||||
s.DoWork(w, delta)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteEntity(world *World, entity Entity) {
|
||||
for _, s := range world.componentsByType {
|
||||
storage, ok := s.(*ComponentStorage[Component])
|
||||
|
||||
if ok {
|
||||
storage.Delete(entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SetResource(world *World, r Resource, val any) {
|
||||
world.resources[r] = val
|
||||
}
|
||||
|
||||
func GetResource[T any](world *World, r Resource) (res T, err error) {
|
||||
val, ok := world.resources[r]
|
||||
|
||||
if !ok {
|
||||
err = newECSError("Resource '", r, "' not found.")
|
||||
return
|
||||
}
|
||||
|
||||
res, ok = val.(T)
|
||||
|
||||
if !ok {
|
||||
err = newECSError("Incompatible type for resource '", r, "'")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func RemoveResource(world *World, r Resource) {
|
||||
delete(world.resources, r)
|
||||
}
|
||||
|
||||
func RegisterComponent[T Component](world *World, compType ComponentType) {
|
||||
world.componentsByType[compType] = CreateComponentStorage[T](compType)
|
||||
}
|
||||
|
||||
func SetComponent[T Component](world *World, entity Entity, component T) {
|
||||
compStorage := world.componentsByType[component.Type()].(*ComponentStorage[T])
|
||||
compStorage.Set(entity, component)
|
||||
|
||||
// if _, ok := world.componentsByEntity[entity]; !ok {
|
||||
// world.componentsByEntity[entity] = map[ComponentType]any{}
|
||||
// }
|
||||
|
||||
// world.componentsByEntity[entity][component.Type()] = component
|
||||
}
|
||||
|
||||
func GetComponent[T Component](world *World, entity Entity) (component T, exists bool) {
|
||||
storage := GetComponentStorage[T](world)
|
||||
|
||||
return storage.Get(entity)
|
||||
}
|
||||
|
||||
func DeleteComponent[T Component](world *World, entity Entity) {
|
||||
storage := GetComponentStorage[T](world)
|
||||
|
||||
storage.Delete(entity)
|
||||
}
|
||||
|
||||
func GetComponentStorage[T Component](world *World) (compStorage *ComponentStorage[T]) {
|
||||
var zero T
|
||||
|
||||
return world.componentsByType[zero.Type()].(*ComponentStorage[T])
|
||||
}
|
||||
|
||||
func RegisterSystem(world *World, s *System) {
|
||||
world.systems = append(world.systems, s)
|
||||
slices.SortFunc(
|
||||
world.systems,
|
||||
func(a, b *System) int {
|
||||
return a.priority - b.priority
|
||||
},
|
||||
)
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package ecs
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
type Entity uuid.UUID
|
||||
|
||||
type Room struct {
|
||||
Entity
|
||||
NameComponent
|
||||
DescriptionComponent
|
||||
NeighboringRoomsComponent
|
||||
}
|
||||
|
||||
type Player struct {
|
||||
Entity
|
||||
PlayerStateComponent
|
||||
NameComponent
|
||||
InRoomComponent
|
||||
}
|
23
internal/game/ecs/error.go
Normal file
23
internal/game/ecs/error.go
Normal file
|
@ -0,0 +1,23 @@
|
|||
package ecs
|
||||
|
||||
import "fmt"
|
||||
|
||||
type ecsError struct {
|
||||
err string
|
||||
}
|
||||
|
||||
func newECSError(v ...any) *ecsError {
|
||||
return &ecsError{
|
||||
err: fmt.Sprint(v...),
|
||||
}
|
||||
}
|
||||
|
||||
func newFormattedECSError(format string, v ...any) *ecsError {
|
||||
return &ecsError{
|
||||
err: fmt.Sprintf(format, v...),
|
||||
}
|
||||
}
|
||||
|
||||
func (err *ecsError) Error() string {
|
||||
return err.err
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
package ecs
|
|
@ -1,15 +0,0 @@
|
|||
package ecs
|
||||
|
||||
type World struct {
|
||||
Players []*Player
|
||||
Rooms []*Room
|
||||
DefaultRoom *Room
|
||||
}
|
||||
|
||||
func CreateWorld() *World {
|
||||
world := &World{
|
||||
Players: []*Player{},
|
||||
Rooms: []*Room{},
|
||||
}
|
||||
|
||||
}
|
39
internal/game/entities.go
Normal file
39
internal/game/entities.go
Normal file
|
@ -0,0 +1,39 @@
|
|||
package game
|
||||
|
||||
import (
|
||||
"code.haedhutner.dev/mvv/LastMUD/internal/game/components"
|
||||
"code.haedhutner.dev/mvv/LastMUD/internal/game/ecs"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func CreatePlayer(world *ecs.World, id uuid.UUID, state components.PlayerState) (entity ecs.Entity, err error) {
|
||||
entity = ecs.CreateEntity(id)
|
||||
|
||||
defaultRoom, err := ecs.GetResource[ecs.Entity](world, ResourceDefaultRoom)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ecs.SetComponent(world, entity, components.PlayerStateComponent{State: state})
|
||||
ecs.SetComponent(world, entity, components.NameComponent{Name: id.String()})
|
||||
ecs.SetComponent(world, entity, components.InRoomComponent{Room: defaultRoom})
|
||||
ecs.SetComponent(world, entity, components.IsPlayerComponent{})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func CreateRoom(
|
||||
world *ecs.World,
|
||||
name, description string,
|
||||
north, south, east, west ecs.Entity,
|
||||
) ecs.Entity {
|
||||
entity := ecs.NewEntity()
|
||||
|
||||
ecs.SetComponent(world, entity, components.IsRoomComponent{})
|
||||
ecs.SetComponent(world, entity, components.NameComponent{Name: name})
|
||||
ecs.SetComponent(world, entity, components.DescriptionComponent{Description: description})
|
||||
ecs.SetComponent(world, entity, components.NeighborsComponent{North: north, South: south, East: east, West: west})
|
||||
|
||||
return entity
|
||||
}
|
|
@ -4,6 +4,8 @@ import (
|
|||
"time"
|
||||
|
||||
"code.haedhutner.dev/mvv/LastMUD/internal/game/command"
|
||||
"code.haedhutner.dev/mvv/LastMUD/internal/game/components"
|
||||
"code.haedhutner.dev/mvv/LastMUD/internal/game/ecs"
|
||||
"code.haedhutner.dev/mvv/LastMUD/internal/logging"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
@ -23,10 +25,14 @@ func (pje *PlayerJoinEvent) Type() EventType {
|
|||
}
|
||||
|
||||
func (pje *PlayerJoinEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
||||
p := CreateJoiningPlayer(pje.connectionId)
|
||||
game.world.AddPlayerToDefaultRoom(p)
|
||||
game.enqeueOutput(game.CreateOutput(p.Identity(), []byte("Welcome to LastMUD!")))
|
||||
game.enqeueOutput(game.CreateOutput(p.Identity(), []byte("Please enter your name:")))
|
||||
p, err := CreatePlayer(game.world.World, pje.connectionId, components.PlayerStateJoining)
|
||||
|
||||
if err != nil {
|
||||
logging.Error("Unabled to create player: ", err)
|
||||
}
|
||||
|
||||
game.enqeueOutput(game.CreateOutput(p.AsUUID(), []byte("Welcome to LastMUD!")))
|
||||
game.enqeueOutput(game.CreateOutput(p.AsUUID(), []byte("Please enter your name:")))
|
||||
}
|
||||
|
||||
type PlayerLeaveEvent struct {
|
||||
|
@ -44,7 +50,7 @@ func (ple *PlayerLeaveEvent) Type() EventType {
|
|||
}
|
||||
|
||||
func (ple *PlayerLeaveEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
||||
game.world.RemovePlayerById(ple.connectionId)
|
||||
ecs.DeleteEntity(game.world.World, ecs.CreateEntity(ple.connectionId))
|
||||
}
|
||||
|
||||
type PlayerCommandEvent struct {
|
||||
|
@ -72,8 +78,6 @@ func (pce *PlayerCommandEvent) Type() EventType {
|
|||
}
|
||||
|
||||
func (pce *PlayerCommandEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
||||
player := game.world.FindPlayerById(pce.connectionId)
|
||||
|
||||
if player == nil {
|
||||
logging.Error("Unable to handle player command from player with id", pce.connectionId, ": Player does not exist")
|
||||
return
|
||||
|
@ -82,7 +86,7 @@ func (pce *PlayerCommandEvent) Handle(game *LastMUDGame, delta time.Duration) {
|
|||
event := pce.parseCommandIntoEvent(game, player)
|
||||
}
|
||||
|
||||
func (pce *PlayerCommandEvent) parseCommandIntoEvent(game *LastMUDGame, player *Player) GameEvent {
|
||||
func (pce *PlayerCommandEvent) parseCommandIntoEvent(game *LastMUDGame, player ecs.Entity) GameEvent {
|
||||
switch pce.command.Command().Definition().Name() {
|
||||
case SayCommand:
|
||||
speech, err := pce.command.Command().Parameters()[0].AsString()
|
||||
|
|
|
@ -41,7 +41,7 @@ type LastMUDGame struct {
|
|||
|
||||
cmdRegistry *command.CommandRegistry
|
||||
|
||||
world *World
|
||||
world *GameWorld
|
||||
|
||||
eventBus *EventBus
|
||||
|
||||
|
@ -54,7 +54,7 @@ func CreateGame(ctx context.Context, wg *sync.WaitGroup) (game *LastMUDGame) {
|
|||
ctx: ctx,
|
||||
eventBus: CreateEventBus(MaxEnqueuedGameEventsPerTick),
|
||||
output: make(chan GameOutput, MaxEnqueuedOutputPerTick),
|
||||
world: CreateWorld(),
|
||||
world: CreateGameWorld(),
|
||||
}
|
||||
|
||||
game.cmdRegistry = game.CreateGameCommandRegistry()
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
package game
|
||||
|
||||
import "github.com/google/uuid"
|
||||
// import "github.com/google/uuid"
|
||||
|
||||
type Player struct {
|
||||
id uuid.UUID
|
||||
// type Player struct {
|
||||
// id uuid.UUID
|
||||
|
||||
state PlayerState
|
||||
// state PlayerState
|
||||
|
||||
currentRoom *Room
|
||||
}
|
||||
// currentRoom *Room
|
||||
// }
|
||||
|
||||
func CreateJoiningPlayer(identity uuid.UUID) *Player {
|
||||
return &Player{
|
||||
id: identity,
|
||||
state: PlayerStateJoining,
|
||||
currentRoom: nil,
|
||||
}
|
||||
}
|
||||
// func CreateJoiningPlayer(identity uuid.UUID) *Player {
|
||||
// return &Player{
|
||||
// id: identity,
|
||||
// state: PlayerStateJoining,
|
||||
// currentRoom: nil,
|
||||
// }
|
||||
// }
|
||||
|
||||
func CreatePlayer(identity uuid.UUID, state PlayerState, room *Room) *Player {
|
||||
return &Player{
|
||||
id: identity,
|
||||
state: state,
|
||||
currentRoom: room,
|
||||
}
|
||||
}
|
||||
// func CreatePlayer(identity uuid.UUID, state PlayerState, room *Room) *Player {
|
||||
// return &Player{
|
||||
// id: identity,
|
||||
// state: state,
|
||||
// currentRoom: room,
|
||||
// }
|
||||
// }
|
||||
|
||||
func (p *Player) Identity() uuid.UUID {
|
||||
return p.id
|
||||
}
|
||||
// func (p *Player) Identity() uuid.UUID {
|
||||
// return p.id
|
||||
// }
|
||||
|
||||
func (p *Player) SetRoom(r *Room) {
|
||||
p.currentRoom = r
|
||||
}
|
||||
// func (p *Player) SetRoom(r *Room) {
|
||||
// p.currentRoom = r
|
||||
// }
|
||||
|
||||
func (p *Player) CurrentRoom() *Room {
|
||||
return p.currentRoom
|
||||
}
|
||||
// func (p *Player) CurrentRoom() *Room {
|
||||
// return p.currentRoom
|
||||
// }
|
||||
|
|
|
@ -1,51 +1,51 @@
|
|||
package game
|
||||
|
||||
import "github.com/google/uuid"
|
||||
// import "github.com/google/uuid"
|
||||
|
||||
type RoomPlayer interface {
|
||||
Identity() uuid.UUID
|
||||
SetRoom(room *Room)
|
||||
}
|
||||
// type RoomPlayer interface {
|
||||
// Identity() uuid.UUID
|
||||
// SetRoom(room *Room)
|
||||
// }
|
||||
|
||||
type Room struct {
|
||||
world *World
|
||||
// type Room struct {
|
||||
// world *World
|
||||
|
||||
North *Room
|
||||
South *Room
|
||||
East *Room
|
||||
West *Room
|
||||
// North *Room
|
||||
// South *Room
|
||||
// East *Room
|
||||
// West *Room
|
||||
|
||||
Name string
|
||||
Description string
|
||||
// Name string
|
||||
// Description string
|
||||
|
||||
players map[uuid.UUID]RoomPlayer
|
||||
}
|
||||
// players map[uuid.UUID]RoomPlayer
|
||||
// }
|
||||
|
||||
func CreateRoom(world *World, name, description string) *Room {
|
||||
return &Room{
|
||||
world: world,
|
||||
Name: name,
|
||||
Description: description,
|
||||
players: map[uuid.UUID]RoomPlayer{},
|
||||
}
|
||||
}
|
||||
// func CreateRoom(world *World, name, description string) *Room {
|
||||
// return &Room{
|
||||
// world: world,
|
||||
// Name: name,
|
||||
// Description: description,
|
||||
// players: map[uuid.UUID]RoomPlayer{},
|
||||
// }
|
||||
// }
|
||||
|
||||
func (r *Room) PlayerJoinRoom(player RoomPlayer) (err error) {
|
||||
r.players[player.Identity()] = player
|
||||
// func (r *Room) PlayerJoinRoom(player RoomPlayer) (err error) {
|
||||
// r.players[player.Identity()] = player
|
||||
|
||||
return
|
||||
}
|
||||
// return
|
||||
// }
|
||||
|
||||
func (r *Room) PlayerLeaveRoom(player RoomPlayer) (err error) {
|
||||
delete(r.players, player.Identity())
|
||||
// func (r *Room) PlayerLeaveRoom(player RoomPlayer) (err error) {
|
||||
// delete(r.players, player.Identity())
|
||||
|
||||
return
|
||||
}
|
||||
// return
|
||||
// }
|
||||
|
||||
func (r *Room) Players() map[uuid.UUID]RoomPlayer {
|
||||
return r.players
|
||||
}
|
||||
// func (r *Room) Players() map[uuid.UUID]RoomPlayer {
|
||||
// return r.players
|
||||
// }
|
||||
|
||||
func (r *Room) World() *World {
|
||||
return r.world
|
||||
}
|
||||
// func (r *Room) World() *World {
|
||||
// return r.world
|
||||
// }
|
||||
|
|
1
internal/game/systems.go
Normal file
1
internal/game/systems.go
Normal file
|
@ -0,0 +1 @@
|
|||
package game
|
|
@ -1,80 +1,199 @@
|
|||
package game
|
||||
|
||||
import "github.com/google/uuid"
|
||||
import (
|
||||
"code.haedhutner.dev/mvv/LastMUD/internal/game/components"
|
||||
"code.haedhutner.dev/mvv/LastMUD/internal/game/ecs"
|
||||
)
|
||||
|
||||
type World struct {
|
||||
rooms []*Room
|
||||
players map[uuid.UUID]*Player
|
||||
defaultRoom *Room
|
||||
const (
|
||||
ResourceDefaultRoom ecs.Resource = "world:room:default"
|
||||
)
|
||||
|
||||
type GameWorld struct {
|
||||
*ecs.World
|
||||
}
|
||||
|
||||
func CreateWorld() (world *World) {
|
||||
world = &World{
|
||||
players: map[uuid.UUID]*Player{},
|
||||
func CreateGameWorld() (gw *GameWorld) {
|
||||
gw = &GameWorld{
|
||||
World: ecs.CreateWorld(),
|
||||
}
|
||||
|
||||
forest := CreateRoom(world, "Forest", "A dense, misty forest stretches endlessly, its towering trees whispering secrets through rustling leaves. Sunbeams filter through the canopy, dappling the mossy ground with golden light.")
|
||||
cabin := CreateRoom(world, "Wooden Cabin", "The cabin’s interior is cozy and rustic, with wooden beams overhead and a stone fireplace crackling warmly. A wool rug lies on creaky floorboards, and shelves brim with books, mugs, and old lanterns.")
|
||||
lake := CreateRoom(world, "Ethermere Lake", "Ethermire Lake lies shrouded in mist, its dark, still waters reflecting a sky perpetually overcast. Whispers ride the wind, and strange lights flicker beneath the surface, never breaking it.")
|
||||
graveyard := CreateRoom(world, "Graveyard", "An overgrown graveyard shrouded in fog, with cracked headstones and leaning statues. The wind sighs through dead trees, and unseen footsteps echo faintly among the mossy graves.")
|
||||
chapel := CreateRoom(world, "Chapel of the Hollow Light", "This ruined chapel leans under ivy and age. Faint light filters through shattered stained glass, casting broken rainbows across dust-choked pews and a long-silent altar.")
|
||||
forest := CreateRoom(
|
||||
gw.World,
|
||||
"Forest",
|
||||
"A dense, misty forest stretches endlessly, its towering trees whispering secrets through rustling leaves. Sunbeams filter through the canopy, dappling the mossy ground with golden light.",
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
)
|
||||
|
||||
forest.North = cabin
|
||||
forest.South = graveyard
|
||||
forest.East = lake
|
||||
forest.West = chapel
|
||||
ecs.SetResource(gw.World, ResourceDefaultRoom, forest)
|
||||
|
||||
cabin.South = forest
|
||||
cabin.West = chapel
|
||||
cabin.East = lake
|
||||
cabin := CreateRoom(
|
||||
gw.World,
|
||||
"Wooden Cabin",
|
||||
"The cabin’s interior is cozy and rustic, with wooden beams overhead and a stone fireplace crackling warmly. A wool rug lies on creaky floorboards, and shelves brim with books, mugs, and old lanterns.",
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
)
|
||||
|
||||
chapel.North = cabin
|
||||
chapel.South = graveyard
|
||||
chapel.East = forest
|
||||
lake := CreateRoom(
|
||||
gw.World,
|
||||
"Ethermere Lake",
|
||||
"Ethermire Lake lies shrouded in mist, its dark, still waters reflecting a sky perpetually overcast. Whispers ride the wind, and strange lights flicker beneath the surface, never breaking it.",
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
)
|
||||
|
||||
lake.West = forest
|
||||
lake.North = cabin
|
||||
lake.South = graveyard
|
||||
graveyard := CreateRoom(
|
||||
gw.World,
|
||||
"Graveyard",
|
||||
"An overgrown graveyard shrouded in fog, with cracked headstones and leaning statues. The wind sighs through dead trees, and unseen footsteps echo faintly among the mossy graves.",
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
)
|
||||
|
||||
graveyard.North = forest
|
||||
graveyard.West = chapel
|
||||
graveyard.East = lake
|
||||
chapel := CreateRoom(
|
||||
gw.World,
|
||||
"Chapel of the Hollow Light",
|
||||
"This ruined chapel leans under ivy and age. Faint light filters through shattered stained glass, casting broken rainbows across dust-choked pews and a long-silent altar.",
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
ecs.NilEntity(),
|
||||
)
|
||||
|
||||
world.rooms = []*Room{
|
||||
forest,
|
||||
cabin,
|
||||
lake,
|
||||
graveyard,
|
||||
chapel,
|
||||
}
|
||||
ecs.SetComponent(gw.World, forest, components.NeighborsComponent{
|
||||
North: cabin,
|
||||
South: graveyard,
|
||||
East: lake,
|
||||
West: chapel,
|
||||
})
|
||||
|
||||
world.defaultRoom = forest
|
||||
ecs.SetComponent(gw.World, cabin, components.NeighborsComponent{
|
||||
South: graveyard,
|
||||
West: chapel,
|
||||
East: lake,
|
||||
})
|
||||
|
||||
ecs.SetComponent(gw.World, chapel, components.NeighborsComponent{
|
||||
North: cabin,
|
||||
South: graveyard,
|
||||
East: forest,
|
||||
})
|
||||
|
||||
ecs.SetComponent(gw.World, lake, components.NeighborsComponent{
|
||||
West: forest,
|
||||
North: cabin,
|
||||
South: graveyard,
|
||||
})
|
||||
|
||||
ecs.SetComponent(gw.World, graveyard, components.NeighborsComponent{
|
||||
North: forest,
|
||||
West: chapel,
|
||||
East: lake,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (w *World) AddPlayerToDefaultRoom(p *Player) {
|
||||
w.players[p.Identity()] = p
|
||||
w.defaultRoom.PlayerJoinRoom(p)
|
||||
p.SetRoom(w.defaultRoom)
|
||||
}
|
||||
// type World struct {
|
||||
// // rooms []*Room
|
||||
// // players map[uuid.UUID]*Player
|
||||
// // defaultRoom *Room
|
||||
|
||||
func (w *World) RemovePlayerById(id uuid.UUID) {
|
||||
p, ok := w.players[id]
|
||||
// entities []Entity
|
||||
// systems []*System
|
||||
// components map[ComponentType]any
|
||||
// }
|
||||
|
||||
if ok {
|
||||
p.currentRoom.PlayerLeaveRoom(p)
|
||||
delete(w.players, id)
|
||||
return
|
||||
}
|
||||
}
|
||||
// func CreateWorld() (world *World) {
|
||||
// world = &World{
|
||||
// entities: []Entity{},
|
||||
// systems: []*System{},
|
||||
// components: map[ComponentType]any{},
|
||||
// }
|
||||
// // world = &World{
|
||||
// // players: map[uuid.UUID]*Player{},
|
||||
// // }
|
||||
|
||||
func (w *World) FindPlayerById(id uuid.UUID) *Player {
|
||||
p, ok := w.players[id]
|
||||
// // forest := CreateRoom(world, "Forest", "A dense, misty forest stretches endlessly, its towering trees whispering secrets through rustling leaves. Sunbeams filter through the canopy, dappling the mossy ground with golden light.")
|
||||
// // cabin := CreateRoom(world, "Wooden Cabin", "The cabin’s interior is cozy and rustic, with wooden beams overhead and a stone fireplace crackling warmly. A wool rug lies on creaky floorboards, and shelves brim with books, mugs, and old lanterns.")
|
||||
// // lake := CreateRoom(world, "Ethermere Lake", "Ethermire Lake lies shrouded in mist, its dark, still waters reflecting a sky perpetually overcast. Whispers ride the wind, and strange lights flicker beneath the surface, never breaking it.")
|
||||
// // graveyard := CreateRoom(world, "Graveyard", "An overgrown graveyard shrouded in fog, with cracked headstones and leaning statues. The wind sighs through dead trees, and unseen footsteps echo faintly among the mossy graves.")
|
||||
// // chapel := CreateRoom(world, "Chapel of the Hollow Light", "This ruined chapel leans under ivy and age. Faint light filters through shattered stained glass, casting broken rainbows across dust-choked pews and a long-silent altar.")
|
||||
|
||||
if ok {
|
||||
return p
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// // forest.North = cabin
|
||||
// // forest.South = graveyard
|
||||
// // forest.East = lake
|
||||
// // forest.West = chapel
|
||||
|
||||
// // cabin.South = forest
|
||||
// // cabin.West = chapel
|
||||
// // cabin.East = lake
|
||||
|
||||
// // chapel.North = cabin
|
||||
// // chapel.South = graveyard
|
||||
// // chapel.East = forest
|
||||
|
||||
// // lake.West = forest
|
||||
// // lake.North = cabin
|
||||
// // lake.South = graveyard
|
||||
|
||||
// // graveyard.North = forest
|
||||
// // graveyard.West = chapel
|
||||
// // graveyard.East = lake
|
||||
|
||||
// // world.rooms = []*Room{
|
||||
// // forest,
|
||||
// // cabin,
|
||||
// // lake,
|
||||
// // graveyard,
|
||||
// // chapel,
|
||||
// // }
|
||||
|
||||
// // world.defaultRoom = forest
|
||||
|
||||
// return
|
||||
// }
|
||||
|
||||
// func RegisterComponentType[T any](world *World, compType ComponentType) {
|
||||
// world.components[compType] = CreateComponentStorage[any](compType)
|
||||
// }
|
||||
|
||||
// func SetComponent[T](compType ComponentType, ent Entity, component any) {
|
||||
// world.components[compType]
|
||||
// }
|
||||
|
||||
// func (w *World) AddPlayerToDefaultRoom(p *Player) {
|
||||
// w.players[p.Identity()] = p
|
||||
// w.defaultRoom.PlayerJoinRoom(p)
|
||||
// p.SetRoom(w.defaultRoom)
|
||||
// }
|
||||
|
||||
// func (w *World) RemovePlayerById(id uuid.UUID) {
|
||||
// p, ok := w.players[id]
|
||||
|
||||
// if ok {
|
||||
// p.currentRoom.PlayerLeaveRoom(p)
|
||||
// delete(w.players, id)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
// func (w *World) FindPlayerById(id uuid.UUID) *Player {
|
||||
// p, ok := w.players[id]
|
||||
|
||||
// if ok {
|
||||
// return p
|
||||
// } else {
|
||||
// return nil
|
||||
// }
|
||||
// }
|
||||
|
|
Loading…
Add table
Reference in a new issue