From 6af69a91691ccdca86834a0627b4feba01dedd98 Mon Sep 17 00:00:00 2001 From: mvvasilev Date: Thu, 16 May 2024 00:06:13 +0300 Subject: [PATCH] Add ECS --- engine/ecs/ecs.go | 282 ++++++++++++++ engine/ecs/ecs_test.go | 742 ++++++++++++++++++++++++++++++++++++ game/logic/logic_snippet.go | 12 - game/model/npc.go | 5 - 4 files changed, 1024 insertions(+), 17 deletions(-) create mode 100644 engine/ecs/ecs.go create mode 100644 engine/ecs/ecs_test.go delete mode 100644 game/logic/logic_snippet.go diff --git a/engine/ecs/ecs.go b/engine/ecs/ecs.go new file mode 100644 index 0000000..388632f --- /dev/null +++ b/engine/ecs/ecs.go @@ -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) + } +} + +/* ==================================================== */ diff --git a/engine/ecs/ecs_test.go b/engine/ecs/ecs_test.go new file mode 100644 index 0000000..d2726e3 --- /dev/null +++ b/engine/ecs/ecs_test.go @@ -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) + }) + } +} diff --git a/game/logic/logic_snippet.go b/game/logic/logic_snippet.go deleted file mode 100644 index c17eab0..0000000 --- a/game/logic/logic_snippet.go +++ /dev/null @@ -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) -} diff --git a/game/model/npc.go b/game/model/npc.go index ee3043b..f2aed8f 100644 --- a/game/model/npc.go +++ b/game/model/npc.go @@ -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) {