This commit is contained in:
Miroslav Vasilev 2024-05-16 00:06:13 +03:00
parent 6396c63d73
commit 6af69a9169
4 changed files with 1024 additions and 17 deletions

282
engine/ecs/ecs.go Normal file
View file

@ -0,0 +1,282 @@
package ecs
import (
"slices"
"time"
"github.com/gdamore/tcell/v2"
)
/* ===================== ECSError ===================== */
type ECSError struct {
err string
}
func ecsError(err string) *ECSError {
return &ECSError{
err: err,
}
}
func (e *ECSError) Error() string {
return e.err
}
/* ==================================================== */
/* ====================== Types ======================= */
type ComponentType uint64
type SystemOrder uint8
type EntityId uint64
type ComponentMask uint64
/* ==================================================== */
/* ================== ComponentMask =================== */
func MaskOf(ids ...ComponentType) ComponentMask {
mask := uint64(0)
for _, id := range ids {
mask |= uint64(id)
}
return ComponentMask(mask)
}
func (c ComponentMask) CombinedWithMask(mask ComponentMask) ComponentMask {
return ComponentMask(uint64(c) | uint64(mask))
}
func (c ComponentMask) CombinedWithType(id ComponentType) ComponentMask {
return ComponentMask(uint64(c) | uint64(id))
}
func (c ComponentMask) Contains(id ComponentType) bool {
return (uint64(id) & uint64(c)) == uint64(id)
}
func (c ComponentMask) ContainsMultiple(ids ComponentMask) bool {
return (uint64(ids) & uint64(c)) == uint64(ids)
}
/* ==================================================== */
/* ===================== System ======================= */
type System interface {
Name() string
Order() int
Input(world *World, e tcell.EventKey)
Tick(world *World, deltaTime int64)
}
/* ==================================================== */
/* =================== Component ====================== */
type Component interface {
Type() ComponentType
}
/* ==================================================== */
/* ================== BasicEntity ===================== */
type BasicEntity struct {
id EntityId
containedComponents ComponentMask
components map[ComponentType]Component
}
func CreateRandomEntityId() EntityId {
return EntityId(time.Now().UnixNano())
}
func createEntity(components ...Component) *BasicEntity {
ent := &BasicEntity{
id: CreateRandomEntityId(),
components: make(map[ComponentType]Component, 0),
}
for _, c := range components {
ent.components[c.Type()] = c
}
return ent
}
func (ent *BasicEntity) Id() EntityId {
return ent.id
}
func (ent *BasicEntity) ContainedComponents() ComponentMask {
return ComponentMask(ent.containedComponents)
}
func (ent *BasicEntity) AddComponent(c Component) {
ent.components[c.Type()] = c
}
func (ent *BasicEntity) AllComponents() []Component {
vals := make([]Component, len(ent.components))
for _, v := range ent.components {
vals = append(vals, v)
}
return vals
}
func (ent *BasicEntity) QueryComponents(componentIds ...ComponentType) (components []Component, err *ECSError) {
comps := make([]Component, len(componentIds))
for _, id := range componentIds {
comp := ent.components[id]
if comp == nil {
return nil, ecsError("Failure: Entity does not contain all of requested types")
}
comps = append(comps, comp)
}
return comps, nil
}
func (ent *BasicEntity) ContainsComponents(mask ComponentMask) bool {
return ent.containedComponents.ContainsMultiple(mask)
}
func (ent *BasicEntity) FetchComponent(id ComponentType) (component Component, err *ECSError) {
comp := ent.components[id]
if comp == nil {
return nil, ecsError("Failure: Entity does not contain requested component")
}
return comp, nil
}
/* ==================================================== */
/* ==================== World ========================= */
type World struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
func CreateWorld() *World {
return &World{
entities: make(map[EntityId]*BasicEntity, 0),
systems: make([]System, 0),
components: make(map[ComponentType][]Component, 0),
}
}
func (w *World) QueryComponents(componentIds ...ComponentType) (components map[ComponentType][]Component, err *ECSError) {
if !w.registeredComponentTypes.ContainsMultiple(MaskOf(componentIds...)) {
return nil, ecsError("Failure: One of the provided queries component types has not been registered")
}
comps := make(map[ComponentType][]Component, 0)
for _, id := range componentIds {
comp := w.components[id]
// No components of the requested type exist, that's ok, add an empty slice for that type
if comp == nil {
comps[id] = make([]Component, 0)
continue
}
comps[id] = comp
}
return comps, nil
}
func (w *World) RegisterComponentType(t ComponentType, name string) (err *ECSError) {
if w.registeredComponentTypes.Contains(t) {
return ecsError("Failure: ComponentType conflict, another component already exists with that type number")
}
w.registeredComponentTypes = w.registeredComponentTypes.CombinedWithType(t)
w.registeredComponentNames[t] = name
return nil
}
func (w *World) AddComponentToEntity(ent *BasicEntity, comp Component) (modifiedEntity *BasicEntity, err *ECSError) {
if !w.registeredComponentTypes.Contains(comp.Type()) {
return nil, ecsError("Failure: Attempting to add unknown component to an entity.")
}
if ent.ContainsComponents(ComponentMask(comp.Type())) {
return nil, ecsError("Failure: Entity already contains component")
}
ent.AddComponent(comp)
if w.components[comp.Type()] == nil {
w.components[comp.Type()] = make([]Component, 0)
}
w.components[comp.Type()] = append(w.components[comp.Type()], comp)
return ent, nil
}
func (w *World) AddSystem(s System) (err *ECSError) {
w.systems = append(w.systems, s)
slices.SortFunc(w.systems, func(a System, b System) int { return a.Order() - b.Order() })
return nil
}
func (w *World) CreateEntity(comps ...Component) *BasicEntity {
ent := createEntity(comps...)
w.entities[ent.Id()] = ent
return ent
}
func (w *World) FindEntityById(id EntityId) (entity *BasicEntity, err *ECSError) {
ent := w.entities[id]
if ent == nil {
return nil, ecsError("Failure: No entity with request id exists")
}
return ent, nil
}
func (w *World) RemoveEntity(id EntityId) {
delete(w.entities, id)
}
func (w *World) Tick(dt int64) {
for _, s := range w.systems {
s.Tick(w, dt)
}
}
func (w *World) Input(e tcell.EventKey) {
for _, s := range w.systems {
s.Input(w, e)
}
}
/* ==================================================== */

742
engine/ecs/ecs_test.go Normal file
View file

@ -0,0 +1,742 @@
package ecs
import (
"reflect"
"testing"
"github.com/gdamore/tcell/v2"
)
func Test_ecsError(t *testing.T) {
type args struct {
err string
}
tests := []struct {
name string
args args
want *ECSError
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ecsError(tt.args.err); !reflect.DeepEqual(got, tt.want) {
t.Errorf("ecsError() = %v, want %v", got, tt.want)
}
})
}
}
func TestECSError_Error(t *testing.T) {
type fields struct {
err string
}
tests := []struct {
name string
fields fields
want string
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
e := &ECSError{
err: tt.fields.err,
}
if got := e.Error(); got != tt.want {
t.Errorf("ECSError.Error() = %v, want %v", got, tt.want)
}
})
}
}
func TestMaskOf(t *testing.T) {
type args struct {
ids []ComponentType
}
tests := []struct {
name string
args args
want ComponentMask
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := MaskOf(tt.args.ids...); got != tt.want {
t.Errorf("MaskOf() = %v, want %v", got, tt.want)
}
})
}
}
func TestComponentMask_CombinedWithMask(t *testing.T) {
type args struct {
mask ComponentMask
}
tests := []struct {
name string
c ComponentMask
args args
want ComponentMask
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.c.CombinedWithMask(tt.args.mask); got != tt.want {
t.Errorf("ComponentMask.CombinedWithMask() = %v, want %v", got, tt.want)
}
})
}
}
func TestComponentMask_CombinedWithType(t *testing.T) {
type args struct {
id ComponentType
}
tests := []struct {
name string
c ComponentMask
args args
want ComponentMask
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.c.CombinedWithType(tt.args.id); got != tt.want {
t.Errorf("ComponentMask.CombinedWithType() = %v, want %v", got, tt.want)
}
})
}
}
func TestComponentMask_Contains(t *testing.T) {
type args struct {
id ComponentType
}
tests := []struct {
name string
c ComponentMask
args args
want bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.c.Contains(tt.args.id); got != tt.want {
t.Errorf("ComponentMask.Contains() = %v, want %v", got, tt.want)
}
})
}
}
func TestComponentMask_ContainsMultiple(t *testing.T) {
type args struct {
ids ComponentMask
}
tests := []struct {
name string
c ComponentMask
args args
want bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.c.ContainsMultiple(tt.args.ids); got != tt.want {
t.Errorf("ComponentMask.ContainsMultiple() = %v, want %v", got, tt.want)
}
})
}
}
func TestCreateRandomEntityId(t *testing.T) {
tests := []struct {
name string
want EntityId
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := CreateRandomEntityId(); got != tt.want {
t.Errorf("CreateRandomEntityId() = %v, want %v", got, tt.want)
}
})
}
}
func Test_createEntity(t *testing.T) {
type args struct {
components []Component
}
tests := []struct {
name string
args args
want *BasicEntity
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := createEntity(tt.args.components...); !reflect.DeepEqual(got, tt.want) {
t.Errorf("createEntity() = %v, want %v", got, tt.want)
}
})
}
}
func TestBasicEntity_Id(t *testing.T) {
type fields struct {
id EntityId
containedComponents ComponentMask
components map[ComponentType]Component
}
tests := []struct {
name string
fields fields
want EntityId
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ent := &BasicEntity{
id: tt.fields.id,
containedComponents: tt.fields.containedComponents,
components: tt.fields.components,
}
if got := ent.Id(); got != tt.want {
t.Errorf("BasicEntity.Id() = %v, want %v", got, tt.want)
}
})
}
}
func TestBasicEntity_ContainedComponents(t *testing.T) {
type fields struct {
id EntityId
containedComponents ComponentMask
components map[ComponentType]Component
}
tests := []struct {
name string
fields fields
want ComponentMask
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ent := &BasicEntity{
id: tt.fields.id,
containedComponents: tt.fields.containedComponents,
components: tt.fields.components,
}
if got := ent.ContainedComponents(); got != tt.want {
t.Errorf("BasicEntity.ContainedComponents() = %v, want %v", got, tt.want)
}
})
}
}
func TestBasicEntity_AddComponent(t *testing.T) {
type fields struct {
id EntityId
containedComponents ComponentMask
components map[ComponentType]Component
}
type args struct {
c Component
}
tests := []struct {
name string
fields fields
args args
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ent := &BasicEntity{
id: tt.fields.id,
containedComponents: tt.fields.containedComponents,
components: tt.fields.components,
}
ent.AddComponent(tt.args.c)
})
}
}
func TestBasicEntity_AllComponents(t *testing.T) {
type fields struct {
id EntityId
containedComponents ComponentMask
components map[ComponentType]Component
}
tests := []struct {
name string
fields fields
want []Component
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ent := &BasicEntity{
id: tt.fields.id,
containedComponents: tt.fields.containedComponents,
components: tt.fields.components,
}
if got := ent.AllComponents(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("BasicEntity.AllComponents() = %v, want %v", got, tt.want)
}
})
}
}
func TestBasicEntity_QueryComponents(t *testing.T) {
type fields struct {
id EntityId
containedComponents ComponentMask
components map[ComponentType]Component
}
type args struct {
componentIds []ComponentType
}
tests := []struct {
name string
fields fields
args args
wantComponents []Component
wantErr *ECSError
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ent := &BasicEntity{
id: tt.fields.id,
containedComponents: tt.fields.containedComponents,
components: tt.fields.components,
}
gotComponents, gotErr := ent.QueryComponents(tt.args.componentIds...)
if !reflect.DeepEqual(gotComponents, tt.wantComponents) {
t.Errorf("BasicEntity.QueryComponents() gotComponents = %v, want %v", gotComponents, tt.wantComponents)
}
if !reflect.DeepEqual(gotErr, tt.wantErr) {
t.Errorf("BasicEntity.QueryComponents() gotErr = %v, want %v", gotErr, tt.wantErr)
}
})
}
}
func TestBasicEntity_ContainsComponents(t *testing.T) {
type fields struct {
id EntityId
containedComponents ComponentMask
components map[ComponentType]Component
}
type args struct {
mask ComponentMask
}
tests := []struct {
name string
fields fields
args args
want bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ent := &BasicEntity{
id: tt.fields.id,
containedComponents: tt.fields.containedComponents,
components: tt.fields.components,
}
if got := ent.ContainsComponents(tt.args.mask); got != tt.want {
t.Errorf("BasicEntity.ContainsComponents() = %v, want %v", got, tt.want)
}
})
}
}
func TestBasicEntity_FetchComponent(t *testing.T) {
type fields struct {
id EntityId
containedComponents ComponentMask
components map[ComponentType]Component
}
type args struct {
id ComponentType
}
tests := []struct {
name string
fields fields
args args
wantComponent Component
wantErr *ECSError
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ent := &BasicEntity{
id: tt.fields.id,
containedComponents: tt.fields.containedComponents,
components: tt.fields.components,
}
gotComponent, gotErr := ent.FetchComponent(tt.args.id)
if !reflect.DeepEqual(gotComponent, tt.wantComponent) {
t.Errorf("BasicEntity.FetchComponent() gotComponent = %v, want %v", gotComponent, tt.wantComponent)
}
if !reflect.DeepEqual(gotErr, tt.wantErr) {
t.Errorf("BasicEntity.FetchComponent() gotErr = %v, want %v", gotErr, tt.wantErr)
}
})
}
}
func TestCreateWorld(t *testing.T) {
tests := []struct {
name string
want *World
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := CreateWorld(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("CreateWorld() = %v, want %v", got, tt.want)
}
})
}
}
func TestWorld_QueryComponents(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
componentIds []ComponentType
}
tests := []struct {
name string
fields fields
args args
wantComponents map[ComponentType][]Component
wantErr *ECSError
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
gotComponents, gotErr := w.QueryComponents(tt.args.componentIds...)
if !reflect.DeepEqual(gotComponents, tt.wantComponents) {
t.Errorf("World.QueryComponents() gotComponents = %v, want %v", gotComponents, tt.wantComponents)
}
if !reflect.DeepEqual(gotErr, tt.wantErr) {
t.Errorf("World.QueryComponents() gotErr = %v, want %v", gotErr, tt.wantErr)
}
})
}
}
func TestWorld_RegisterComponentType(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
t ComponentType
name string
}
tests := []struct {
name string
fields fields
args args
wantErr *ECSError
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
if gotErr := w.RegisterComponentType(tt.args.t, tt.args.name); !reflect.DeepEqual(gotErr, tt.wantErr) {
t.Errorf("World.RegisterComponentType() = %v, want %v", gotErr, tt.wantErr)
}
})
}
}
func TestWorld_AddComponentToEntity(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
ent *BasicEntity
comp Component
}
tests := []struct {
name string
fields fields
args args
wantModifiedEntity *BasicEntity
wantErr *ECSError
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
gotModifiedEntity, gotErr := w.AddComponentToEntity(tt.args.ent, tt.args.comp)
if !reflect.DeepEqual(gotModifiedEntity, tt.wantModifiedEntity) {
t.Errorf("World.AddComponentToEntity() gotModifiedEntity = %v, want %v", gotModifiedEntity, tt.wantModifiedEntity)
}
if !reflect.DeepEqual(gotErr, tt.wantErr) {
t.Errorf("World.AddComponentToEntity() gotErr = %v, want %v", gotErr, tt.wantErr)
}
})
}
}
func TestWorld_AddSystem(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
s System
}
tests := []struct {
name string
fields fields
args args
wantErr *ECSError
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
if gotErr := w.AddSystem(tt.args.s); !reflect.DeepEqual(gotErr, tt.wantErr) {
t.Errorf("World.AddSystem() = %v, want %v", gotErr, tt.wantErr)
}
})
}
}
func TestWorld_CreateEntity(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
comps []Component
}
tests := []struct {
name string
fields fields
args args
want *BasicEntity
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
if got := w.CreateEntity(tt.args.comps...); !reflect.DeepEqual(got, tt.want) {
t.Errorf("World.CreateEntity() = %v, want %v", got, tt.want)
}
})
}
}
func TestWorld_FindEntityById(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
id EntityId
}
tests := []struct {
name string
fields fields
args args
wantEntity *BasicEntity
wantErr *ECSError
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
gotEntity, gotErr := w.FindEntityById(tt.args.id)
if !reflect.DeepEqual(gotEntity, tt.wantEntity) {
t.Errorf("World.FindEntityById() gotEntity = %v, want %v", gotEntity, tt.wantEntity)
}
if !reflect.DeepEqual(gotErr, tt.wantErr) {
t.Errorf("World.FindEntityById() gotErr = %v, want %v", gotErr, tt.wantErr)
}
})
}
}
func TestWorld_RemoveEntity(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
id EntityId
}
tests := []struct {
name string
fields fields
args args
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
w.RemoveEntity(tt.args.id)
})
}
}
func TestWorld_Tick(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
dt int64
}
tests := []struct {
name string
fields fields
args args
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
w.Tick(tt.args.dt)
})
}
}
func TestWorld_Input(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
e tcell.EventKey
}
tests := []struct {
name string
fields fields
args args
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
w.Input(tt.args.e)
})
}
}

View file

@ -1,12 +0,0 @@
package logic
import (
"mvvasilev/last_light/game/model"
"github.com/gdamore/tcell/v2"
)
type LogicSnippet[T model.Entity] interface {
Input(e *tcell.EventKey)
Tick(dt int64, entity T)
}

View file

@ -19,10 +19,6 @@ func CreateNPC(pos engine.Position) *NPC {
}
}
func (c *NPC) Position() engine.Position {
return c.Positioned.Position()
}
func (c *NPC) MoveTo(newPosition engine.Position) {
c.Positioned.SetPosition(newPosition)
}
@ -32,7 +28,6 @@ func (c *NPC) UniqueId() uuid.UUID {
}
func (c *NPC) Input(e *tcell.EventKey) {
}
func (c *NPC) Tick(dt int64) {