1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2025-02-09 10:06:35 -05:00

major refactor of akara ecs

* component ID's are dynamically allocated now
* removed `akara.BaseComponent` member from components
* component declarations drastically reduced
This commit is contained in:
gravestench 2020-11-27 21:45:58 -08:00
parent d4efe67052
commit 3f5d2c0938
56 changed files with 1480 additions and 2076 deletions

View File

@ -1,41 +0,0 @@
package d2components
import (
"github.com/gravestench/akara"
)
// Component type ID's
const (
GameConfigCID akara.ComponentID = iota
FilePathCID
FileTypeCID
FileSourceCID
FileHandleCID
AssetStringTableCID
AssetFontTableCID
AssetDataDictionaryCID
AssetPaletteCID
AssetPaletteTransformCID
AssetCofCID
AssetDc6CID
AssetDccCID
AssetDs1CID
AssetDt1CID
AssetWavCID
AssetD2AnimDataCID
PositionCID
VelocityCID
DirtyCID
PriorityCID
RenderableCID
CameraCID
ViewportCID
ViewportFilterCID
MainViewportCID
OriginCID
SizeCID
AnimationCID
ScaleCID
AlphaCID
SegmentedSpriteCID
)

View File

@ -5,60 +5,40 @@ import (
"github.com/gravestench/akara"
)
// static check that AlphaComponent implements Component
var _ akara.Component = &AlphaComponent{}
// static check that Alpha implements Component
var _ akara.Component = &Alpha{}
// static check that AlphaMap implements ComponentMap
var _ akara.ComponentMap = &AlphaMap{}
// AlphaComponent is a component that contains an embedded cof struct
type AlphaComponent struct {
*akara.BaseComponent
// Alpha is a component that contains normalized alpha transparency (0.0 ... 1.0)
type Alpha struct {
Alpha float64
}
// AlphaMap is a map of entity ID's to Alpha
type AlphaMap struct {
*akara.BaseComponentMap
}
// New creates a new alpha component instance. The default alpha is opaque with value 1.0
func (*Alpha) New() akara.Component {
const defaultAlpha = 1.0
// AddAlpha adds a new AlphaComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *AlphaComponent instead of an akara.Component
func (cm *AlphaMap) AddAlpha(id akara.EID) *AlphaComponent {
c := cm.Add(id).(*AlphaComponent)
c.Alpha = 1
return c
}
// GetAlpha returns the AlphaComponent associated with the given entity id
func (cm *AlphaMap) GetAlpha(id akara.EID) (*AlphaComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*AlphaComponent), found
}
// Alpha is a convenient reference to be used as a component identifier
var Alpha = newAlpha() // nolint:gochecknoglobals // global by design
func newAlpha() akara.Component {
return &AlphaComponent{
BaseComponent: akara.NewBaseComponent(AlphaCID, newAlpha, newAlphaMap),
return &Alpha{
Alpha: defaultAlpha,
}
}
func newAlphaMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AlphaCID, newAlpha, newAlphaMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
// AlphaFactory is a wrapper for the generic component factory that returns Alpha component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Alpha.
type AlphaFactory struct {
Alpha *akara.ComponentFactory
}
cm := &AlphaMap{
BaseComponentMap: baseMap,
// AddAlpha adds a Alpha component to the given entity and returns it
func (m *AlphaFactory) AddAlpha(id akara.EID) *Alpha {
return m.Alpha.Add(id).(*Alpha)
}
// GetAlpha returns the Alpha component for the given entity, and a bool for whether or not it exists
func (m *AlphaFactory) GetAlpha(id akara.EID) (*Alpha, bool) {
component, found := m.Alpha.Get(id)
if !found {
return nil, found
}
return cm
return component.(*Alpha), found
}

View File

@ -8,55 +8,35 @@ import (
)
// static check that AnimationComponent implements Component
var _ akara.Component = &AnimationComponent{}
var _ akara.Component = &Animation{}
// static check that AnimationMap implements ComponentMap
var _ akara.ComponentMap = &AnimationMap{}
// AnimationComponent is a component that contains a width and height
type AnimationComponent struct {
*akara.BaseComponent
// Animation is a component that contains a width and height
type Animation struct {
d2interface.Animation
}
// AnimationMap is a map of entity ID's to Animation
type AnimationMap struct {
*akara.BaseComponentMap
// New returns an animation component. By default, it contains a nil instance of an animation.
func (*Animation) New() akara.Component {
return &Animation{}
}
// AddAnimation adds a new AnimationComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *AnimationComponent instead of an akara.Component
func (cm *AnimationMap) AddAnimation(id akara.EID) *AnimationComponent {
return cm.Add(id).(*AnimationComponent)
// AnimationFactory is a wrapper for the generic component factory that returns Animation component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Animation.
type AnimationFactory struct {
Animation *akara.ComponentFactory
}
// GetAnimation returns the AnimationComponent associated with the given entity id
func (cm *AnimationMap) GetAnimation(id akara.EID) (*AnimationComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddAnimation adds a Animation component to the given entity and returns it
func (m *AnimationFactory) AddAnimation(id akara.EID) *Animation {
return m.Animation.Add(id).(*Animation)
}
// GetAnimation returns the Animation component for the given entity, and a bool for whether or not it exists
func (m *AnimationFactory) GetAnimation(id akara.EID) (*Animation, bool) {
component, found := m.Animation.Get(id)
if !found {
return nil, found
}
return entry.(*AnimationComponent), found
}
// Animation is a convenient reference to be used as a component identifier
var Animation = newAnimation() // nolint:gochecknoglobals // global by design
func newAnimation() akara.Component {
return &AnimationComponent{
BaseComponent: akara.NewBaseComponent(AnimationCID, newAnimation, newAnimationMap),
}
}
func newAnimationMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AnimationCID, newAnimation, newAnimationMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &AnimationMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*Animation), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2cof"
)
// static check that CofComponent implements Component
var _ akara.Component = &CofComponent{}
// static check that Cof implements Component
var _ akara.Component = &Cof{}
// static check that CofMap implements ComponentMap
var _ akara.ComponentMap = &CofMap{}
// CofComponent is a component that contains an embedded cof struct
type CofComponent struct {
*akara.BaseComponent
// Cof is a component that contains an embedded cof struct
type Cof struct {
*d2cof.COF
}
// CofMap is a map of entity ID's to Cof
type CofMap struct {
*akara.BaseComponentMap
// New returns a new Cof component. By default, it contains a nil instance.
func (*Cof) New() akara.Component {
return &Cof{}
}
// AddCof adds a new CofComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *CofComponent instead of an akara.Component
func (cm *CofMap) AddCof(id akara.EID) *CofComponent {
return cm.Add(id).(*CofComponent)
// CofFactory is a wrapper for the generic component factory that returns Cof component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Cof.
type CofFactory struct {
Cof *akara.ComponentFactory
}
// GetCof returns the CofComponent associated with the given entity id
func (cm *CofMap) GetCof(id akara.EID) (*CofComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddCof adds a Cof component to the given entity and returns it
func (m *CofFactory) AddCof(id akara.EID) *Cof {
return m.Cof.Add(id).(*Cof)
}
// GetCof returns the Cof component for the given entity, and a bool for whether or not it exists
func (m *CofFactory) GetCof(id akara.EID) (*Cof, bool) {
component, found := m.Cof.Get(id)
if !found {
return nil, found
}
return entry.(*CofComponent), found
}
// Cof is a convenient reference to be used as a component identifier
var Cof = newCof() // nolint:gochecknoglobals // global by design
func newCof() akara.Component {
return &CofComponent{
BaseComponent: akara.NewBaseComponent(AssetCofCID, newCof, newCofMap),
}
}
func newCofMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetCofCID, newCof, newCofMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &CofMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*Cof), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2animdata"
)
// static check that AnimDataComponent implements Component
var _ akara.Component = &AnimDataComponent{}
// static check that AnimationData implements Component
var _ akara.Component = &AnimationData{}
// static check that AnimDataMap implements ComponentMap
var _ akara.ComponentMap = &AnimDataMap{}
// AnimDataComponent is a component that contains an embedded AnimationData struct
type AnimDataComponent struct {
*akara.BaseComponent
// AnimationData is a component that contains an embedded AnimationData struct
type AnimationData struct {
*d2animdata.AnimationData
}
// AnimDataMap is a map of entity ID's to AnimData
type AnimDataMap struct {
*akara.BaseComponentMap
// New returns an AnimationData component. By default, it contains a nil instance.
func (*AnimationData) New() akara.Component {
return &AnimationData{}
}
// AddAnimData adds a new AnimDataComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *AnimDataComponent instead of an akara.Component
func (cm *AnimDataMap) AddAnimData(id akara.EID) *AnimDataComponent {
return cm.Add(id).(*AnimDataComponent)
// AnimationDataFactory is a wrapper for the generic component factory that returns AnimationData component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a AnimationData.
type AnimationDataFactory struct {
AnimationData *akara.ComponentFactory
}
// GetAnimData returns the AnimDataComponent associated with the given entity id
func (cm *AnimDataMap) GetAnimData(id akara.EID) (*AnimDataComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddAnimationData adds a AnimationData component to the given entity and returns it
func (m *AnimationDataFactory) AddAnimationData(id akara.EID) *AnimationData {
return m.AnimationData.Add(id).(*AnimationData)
}
// GetAnimationData returns the AnimationData component for the given entity, and a bool for whether or not it exists
func (m *AnimationDataFactory) GetAnimationData(id akara.EID) (*AnimationData, bool) {
component, found := m.AnimationData.Get(id)
if !found {
return nil, found
}
return entry.(*AnimDataComponent), found
}
// AnimData is a convenient reference to be used as a component identifier
var AnimData = newAnimData() // nolint:gochecknoglobals // global by design
func newAnimData() akara.Component {
return &AnimDataComponent{
BaseComponent: akara.NewBaseComponent(AssetD2AnimDataCID, newAnimData, newAnimDataMap),
}
}
func newAnimDataMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetD2AnimDataCID, newAnimData, newAnimDataMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &AnimDataMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*AnimationData), found
}

View File

@ -8,55 +8,35 @@ import (
)
// static check that DataDictionaryComponent implements Component
var _ akara.Component = &DataDictionaryComponent{}
var _ akara.Component = &DataDictionary{}
// static check that DataDictionaryMap implements ComponentMap
var _ akara.ComponentMap = &DataDictionaryMap{}
// DataDictionaryComponent is a component that contains an embedded txt data dictionary struct
type DataDictionaryComponent struct {
*akara.BaseComponent
// DataDictionary is a component that contains an embedded txt data dictionary struct
type DataDictionary struct {
*d2txt.DataDictionary
}
// DataDictionaryMap is a map of entity ID's to DataDictionary
type DataDictionaryMap struct {
*akara.BaseComponentMap
// New returns a DataDictionary component. By default, it contains a nil instance.
func (*DataDictionary) New() akara.Component {
return &AnimationData{}
}
// AddDataDictionary adds a new DataDictionaryComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *DataDictionaryComponent instead of an akara.Component
func (cm *DataDictionaryMap) AddDataDictionary(id akara.EID) *DataDictionaryComponent {
return cm.Add(id).(*DataDictionaryComponent)
// DataDictionaryFactory is a wrapper for the generic component factory that returns DataDictionary component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a DataDictionary.
type DataDictionaryFactory struct {
DataDictionary *akara.ComponentFactory
}
// GetDataDictionary returns the DataDictionaryComponent associated with the given entity id
func (cm *DataDictionaryMap) GetDataDictionary(id akara.EID) (*DataDictionaryComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddDataDictionary adds a DataDictionary component to the given entity and returns it
func (m *DataDictionaryFactory) AddDataDictionary(id akara.EID) *DataDictionary {
return m.DataDictionary.Add(id).(*DataDictionary)
}
// GetDataDictionary returns the DataDictionary component for the given entity, and a bool for whether or not it exists
func (m *DataDictionaryFactory) GetDataDictionary(id akara.EID) (*DataDictionary, bool) {
component, found := m.DataDictionary.Get(id)
if !found {
return nil, found
}
return entry.(*DataDictionaryComponent), found
}
// DataDictionary is a convenient reference to be used as a component identifier
var DataDictionary = newDataDictionary() // nolint:gochecknoglobals // global by design
func newDataDictionary() akara.Component {
return &DataDictionaryComponent{
BaseComponent: akara.NewBaseComponent(AssetDataDictionaryCID, newDataDictionary, newDataDictionaryMap),
}
}
func newDataDictionaryMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetDataDictionaryCID, newDataDictionary, newDataDictionaryMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &DataDictionaryMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*DataDictionary), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dc6"
)
// static check that Dc6Component implements Component
var _ akara.Component = &Dc6Component{}
// static check that Dc6 implements Component
var _ akara.Component = &Dc6{}
// static check that Dc6Map implements ComponentMap
var _ akara.ComponentMap = &Dc6Map{}
// Dc6Component is a component that contains an embedded DC6 struct
type Dc6Component struct {
*akara.BaseComponent
// Dc6 is a component that contains an embedded DC6 struct
type Dc6 struct {
*d2dc6.DC6
}
// Dc6Map is a map of entity ID's to Dc6
type Dc6Map struct {
*akara.BaseComponentMap
// New returns a Dc6 component. By default, it contains a nil instance.
func (*Dc6) New() akara.Component {
return &Dc6{}
}
// AddDc6 adds a new Dc6Component for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *Dc6Component instead of an akara.Component
func (cm *Dc6Map) AddDc6(id akara.EID) *Dc6Component {
return cm.Add(id).(*Dc6Component)
// Dc6Factory is a wrapper for the generic component factory that returns Dc6 component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Dc6.
type Dc6Factory struct {
Dc6 *akara.ComponentFactory
}
// GetDc6 returns the Dc6Component associated with the given entity id
func (cm *Dc6Map) GetDc6(id akara.EID) (*Dc6Component, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddDc6 adds a Dc6 component to the given entity and returns it
func (m *Dc6Factory) AddDc6(id akara.EID) *Dc6 {
return m.Dc6.Add(id).(*Dc6)
}
// GetDc6 returns the Dc6 component for the given entity, and a bool for whether or not it exists
func (m *Dc6Factory) GetDc6(id akara.EID) (*Dc6, bool) {
component, found := m.Dc6.Get(id)
if !found {
return nil, found
}
return entry.(*Dc6Component), found
}
// Dc6 is a convenient reference to be used as a component identifier
var Dc6 = newDc6() // nolint:gochecknoglobals // global by design
func newDc6() akara.Component {
return &Dc6Component{
BaseComponent: akara.NewBaseComponent(AssetDc6CID, newDc6, newDc6Map),
}
}
func newDc6Map() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetDc6CID, newDc6, newDc6Map)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &Dc6Map{
BaseComponentMap: baseMap,
}
return cm
return component.(*Dc6), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dcc"
)
// static check that DccComponent implements Component
var _ akara.Component = &DccComponent{}
// static check that Dcc implements Component
var _ akara.Component = &Dcc{}
// static check that DccMap implements ComponentMap
var _ akara.ComponentMap = &DccMap{}
// DccComponent is a component that contains an embedded DCC struct
type DccComponent struct {
*akara.BaseComponent
// Dcc is a component that contains an embedded DCC struct
type Dcc struct {
*d2dcc.DCC
}
// DccMap is a map of entity ID's to Dcc
type DccMap struct {
*akara.BaseComponentMap
// New returns a Dcc component. By default, it contains a nil instance.
func (*Dcc) New() akara.Component {
return &Dcc{}
}
// AddDcc adds a new DccComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *DccComponent instead of an akara.Component
func (cm *DccMap) AddDcc(id akara.EID) *DccComponent {
return cm.Add(id).(*DccComponent)
// DccFactory is a wrapper for the generic component factory that returns Dcc component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Dcc.
type DccFactory struct {
Dcc *akara.ComponentFactory
}
// GetDcc returns the DccComponent associated with the given entity id
func (cm *DccMap) GetDcc(id akara.EID) (*DccComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddDcc adds a Dcc component to the given entity and returns it
func (m *DccFactory) AddDcc(id akara.EID) *Dcc {
return m.Dcc.Add(id).(*Dcc)
}
// GetDcc returns the Dcc component for the given entity, and a bool for whether or not it exists
func (m *DccFactory) GetDcc(id akara.EID) (*Dcc, bool) {
component, found := m.Dcc.Get(id)
if !found {
return nil, found
}
return entry.(*DccComponent), found
}
// Dcc is a convenient reference to be used as a component identifier
var Dcc = newDcc() // nolint:gochecknoglobals // global by design
func newDcc() akara.Component {
return &DccComponent{
BaseComponent: akara.NewBaseComponent(AssetDccCID, newDcc, newDccMap),
}
}
func newDccMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetDccCID, newDcc, newDccMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &DccMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*Dcc), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2ds1"
)
// static check that Ds1Component implements Component
var _ akara.Component = &Ds1Component{}
// static check that Ds1 implements Component
var _ akara.Component = &Ds1{}
// static check that Ds1Map implements ComponentMap
var _ akara.ComponentMap = &Ds1Map{}
// Ds1Component is a component that contains an embedded DS1 struct
type Ds1Component struct {
*akara.BaseComponent
// Ds1 is a component that contains an embedded DS1 struct
type Ds1 struct {
*d2ds1.DS1
}
// Ds1Map is a map of entity ID's to Ds1
type Ds1Map struct {
*akara.BaseComponentMap
// New returns a Ds1 component. By default, it contains a nil instance.
func (*Ds1) New() akara.Component {
return &Ds1{}
}
// AddDs1 adds a new Ds1Component for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *Ds1Component instead of an akara.Component
func (cm *Ds1Map) AddDs1(id akara.EID) *Ds1Component {
return cm.Add(id).(*Ds1Component)
// Ds1Factory is a wrapper for the generic component factory that returns Ds1 component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Ds1.
type Ds1Factory struct {
Ds1 *akara.ComponentFactory
}
// GetDs1 returns the Ds1Component associated with the given entity id
func (cm *Ds1Map) GetDs1(id akara.EID) (*Ds1Component, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddDs1 adds a Ds1 component to the given entity and returns it
func (m *Ds1Factory) AddDs1(id akara.EID) *Ds1 {
return m.Ds1.Add(id).(*Ds1)
}
// GetDs1 returns the Ds1 component for the given entity, and a bool for whether or not it exists
func (m *Ds1Factory) GetDs1(id akara.EID) (*Ds1, bool) {
component, found := m.Ds1.Get(id)
if !found {
return nil, found
}
return entry.(*Ds1Component), found
}
// Ds1 is a convenient reference to be used as a component identifier
var Ds1 = newDs1() // nolint:gochecknoglobals // global by design
func newDs1() akara.Component {
return &Ds1Component{
BaseComponent: akara.NewBaseComponent(AssetDs1CID, newDs1, newDs1Map),
}
}
func newDs1Map() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetDs1CID, newDs1, newDs1Map)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &Ds1Map{
BaseComponentMap: baseMap,
}
return cm
return component.(*Ds1), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1"
)
// static check that Dt1Component implements Component
var _ akara.Component = &Dt1Component{}
// static check that Dt1 implements Component
var _ akara.Component = &Dt1{}
// static check that Dt1Map implements ComponentMap
var _ akara.ComponentMap = &Dt1Map{}
// Dt1Component is a component that contains an embedded DT1 struct
type Dt1Component struct {
*akara.BaseComponent
// Dt1 is a component that contains an embedded DT1 struct
type Dt1 struct {
*d2dt1.DT1
}
// Dt1Map is a map of entity ID's to Dt1
type Dt1Map struct {
*akara.BaseComponentMap
// New returns a Dt1 component. By default, it contains a nil instance.
func (*Dt1) New() akara.Component {
return &Dt1{}
}
// AddDt1 adds a new Dt1Component for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *Dt1Component instead of an akara.Component
func (cm *Dt1Map) AddDt1(id akara.EID) *Dt1Component {
return cm.Add(id).(*Dt1Component)
// Dt1Factory is a wrapper for the generic component factory that returns Dt1 component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Dt1.
type Dt1Factory struct {
Dt1 *akara.ComponentFactory
}
// GetDt1 returns the Dt1Component associated with the given entity id
func (cm *Dt1Map) GetDt1(id akara.EID) (*Dt1Component, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddDt1 adds a Dt1 component to the given entity and returns it
func (m *Dt1Factory) AddDt1(id akara.EID) *Dt1 {
return m.Dt1.Add(id).(*Dt1)
}
// GetDt1 returns the Dt1 component for the given entity, and a bool for whether or not it exists
func (m *Dt1Factory) GetDt1(id akara.EID) (*Dt1, bool) {
component, found := m.Dt1.Get(id)
if !found {
return nil, found
}
return entry.(*Dt1Component), found
}
// Dt1 is a convenient reference to be used as a component identifier
var Dt1 = newDt1() // nolint:gochecknoglobals // global by design
func newDt1() akara.Component {
return &Dt1Component{
BaseComponent: akara.NewBaseComponent(AssetDt1CID, newDt1, newDt1Map),
}
}
func newDt1Map() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetDt1CID, newDt1, newDt1Map)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &Dt1Map{
BaseComponentMap: baseMap,
}
return cm
return component.(*Dt1), found
}

View File

@ -5,56 +5,36 @@ import (
"github.com/gravestench/akara"
)
// static check that FontTableComponent implements Component
var _ akara.Component = &FontTableComponent{}
// static check that FontTable implements Component
var _ akara.Component = &FontTable{}
// static check that FontTableMap implements ComponentMap
var _ akara.ComponentMap = &FontTableMap{}
// FontTableComponent is a component that contains font table data as a byte slice
type FontTableComponent struct {
*akara.BaseComponent
// FontTable is a component that contains font table data as a byte slice
type FontTable struct {
Data []byte
}
// FontTableMap is a map of entity ID's to FontTable
type FontTableMap struct {
*akara.BaseComponentMap
// New returns a FontTable component. By default, Data is a nil instance.
func (*FontTable) New() akara.Component {
return &FontTable{}
}
// AddFontTable adds a new FontTableComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *FontTableComponent instead of an akara.Component
func (cm *FontTableMap) AddFontTable(id akara.EID) *FontTableComponent {
return cm.Add(id).(*FontTableComponent)
// FontTableFactory is a wrapper for the generic component factory that returns FontTable component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a FontTable.
type FontTableFactory struct {
FontTable *akara.ComponentFactory
}
// GetFontTable returns the FontTableComponent associated with the given entity id
func (cm *FontTableMap) GetFontTable(id akara.EID) (*FontTableComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddFontTable adds a FontTable component to the given entity and returns it
func (m *FontTableFactory) AddFontTable(id akara.EID) *FontTable {
return m.FontTable.Add(id).(*FontTable)
}
// GetFontTable returns the FontTable component for the given entity, and a bool for whether or not it exists
func (m *FontTableFactory) GetFontTable(id akara.EID) (*FontTable, bool) {
component, found := m.FontTable.Get(id)
if !found {
return nil, found
}
return entry.(*FontTableComponent), found
}
// FontTable is a convenient reference to be used as a component identifier
var FontTable = newFontTable() // nolint:gochecknoglobals // global by design
func newFontTable() akara.Component {
return &FontTableComponent{
BaseComponent: akara.NewBaseComponent(AssetFontTableCID, newFontTable, newFontTableMap),
}
}
func newFontTableMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetFontTableCID, newFontTable, newFontTableMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &FontTableMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*FontTable), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
)
// static check that PaletteComponent implements Component
var _ akara.Component = &PaletteComponent{}
// static check that Palette implements Component
var _ akara.Component = &Palette{}
// static check that PaletteMap implements ComponentMap
var _ akara.ComponentMap = &PaletteMap{}
// PaletteComponent is a component that contains an embedded palette interface
type PaletteComponent struct {
*akara.BaseComponent
// Palette is a component that contains an embedded palette interface
type Palette struct {
d2interface.Palette
}
// PaletteMap is a map of entity ID's to Palette
type PaletteMap struct {
*akara.BaseComponentMap
// New returns a new Palette component. By default, it contains a nil instance.
func (*Palette) New() akara.Component {
return &Palette{}
}
// AddPalette adds a new PaletteComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *PaletteComponent instead of an akara.Component
func (cm *PaletteMap) AddPalette(id akara.EID) *PaletteComponent {
return cm.Add(id).(*PaletteComponent)
// PaletteFactory is a wrapper for the generic component factory that returns Palette component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Palette.
type PaletteFactory struct {
Palette *akara.ComponentFactory
}
// GetPalette returns the PaletteComponent associated with the given entity id
func (cm *PaletteMap) GetPalette(id akara.EID) (*PaletteComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddPalette adds a Palette component to the given entity and returns it
func (m *PaletteFactory) AddPalette(id akara.EID) *Palette {
return m.Palette.Add(id).(*Palette)
}
// GetPalette returns the Palette component for the given entity, and a bool for whether or not it exists
func (m *PaletteFactory) GetPalette(id akara.EID) (*Palette, bool) {
component, found := m.Palette.Get(id)
if !found {
return nil, found
}
return entry.(*PaletteComponent), found
}
// Palette is a convenient reference to be used as a component identifier
var Palette = newPalette() // nolint:gochecknoglobals // global by design
func newPalette() akara.Component {
return &PaletteComponent{
BaseComponent: akara.NewBaseComponent(AssetPaletteCID, newPalette, newPaletteMap),
}
}
func newPaletteMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetPaletteCID, newPalette, newPaletteMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &PaletteMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*Palette), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2pl2"
)
// static check that PaletteTransformComponent implements Component
var _ akara.Component = &PaletteTransformComponent{}
// static check that PaletteTransform implements Component
var _ akara.Component = &PaletteTransform{}
// static check that PaletteTransformMap implements ComponentMap
var _ akara.ComponentMap = &PaletteTransformMap{}
// PaletteTransformComponent is a component that contains an embedded palette transform (pl2) struct
type PaletteTransformComponent struct {
*akara.BaseComponent
Transform *d2pl2.PL2
// PaletteTransform is a component that contains an embedded palette transform (pl2) struct
type PaletteTransform struct {
*d2pl2.PL2
}
// PaletteTransformMap is a map of entity ID's to PaletteTransform
type PaletteTransformMap struct {
*akara.BaseComponentMap
// New returns a new PaletteTransform component. By default, it contains a nil instance.
func (*PaletteTransform) New() akara.Component {
return &PaletteTransform{}
}
// AddPaletteTransform adds a new PaletteTransformComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *PaletteTransformComponent instead of an akara.Component
func (cm *PaletteTransformMap) AddPaletteTransform(id akara.EID) *PaletteTransformComponent {
return cm.Add(id).(*PaletteTransformComponent)
// PaletteTransformFactory is a wrapper for the generic component factory that returns PaletteTransform component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a PaletteTransform.
type PaletteTransformFactory struct {
PaletteTransform *akara.ComponentFactory
}
// GetPaletteTransform returns the PaletteTransformComponent associated with the given entity id
func (cm *PaletteTransformMap) GetPaletteTransform(id akara.EID) (*PaletteTransformComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddPaletteTransform adds a PaletteTransform component to the given entity and returns it
func (m *PaletteTransformFactory) AddPaletteTransform(id akara.EID) *PaletteTransform {
return m.PaletteTransform.Add(id).(*PaletteTransform)
}
// GetPaletteTransform returns the PaletteTransform component for the given entity, and a bool for whether or not it exists
func (m *PaletteTransformFactory) GetPaletteTransform(id akara.EID) (*PaletteTransform, bool) {
component, found := m.PaletteTransform.Get(id)
if !found {
return nil, found
}
return entry.(*PaletteTransformComponent), found
}
// PaletteTransform is a convenient reference to be used as a component identifier
var PaletteTransform = newPaletteTransform() // nolint:gochecknoglobals // global by design
func newPaletteTransform() akara.Component {
return &PaletteTransformComponent{
BaseComponent: akara.NewBaseComponent(AssetPaletteTransformCID, newPaletteTransform, newPaletteTransformMap),
}
}
func newPaletteTransformMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetPaletteTransformCID, newPaletteTransform, newPaletteTransformMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &PaletteTransformMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*PaletteTransform), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2tbl"
)
// static check that StringTableComponent implements Component
var _ akara.Component = &StringTableComponent{}
// static check that StringTable implements Component
var _ akara.Component = &StringTable{}
// static check that StringTableMap implements ComponentMap
var _ akara.ComponentMap = &StringTableMap{}
// StringTableComponent is a component that contains an embedded text table struct
type StringTableComponent struct {
*akara.BaseComponent
// StringTable is a component that contains an embedded text table struct
type StringTable struct {
*d2tbl.TextDictionary
}
// StringTableMap is a map of entity ID's to StringTable
type StringTableMap struct {
*akara.BaseComponentMap
// New returns a new StringTable component. By default, it contains a nil instance.
func (*StringTable) New() akara.Component {
return &StringTable{}
}
// AddStringTable adds a new StringTableComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *StringTableComponent instead of an akara.Component
func (cm *StringTableMap) AddStringTable(id akara.EID) *StringTableComponent {
return cm.Add(id).(*StringTableComponent)
// StringTableFactory is a wrapper for the generic component factory that returns StringTable component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a StringTable.
type StringTableFactory struct {
StringTable *akara.ComponentFactory
}
// GetStringTable returns the StringTableComponent associated with the given entity id
func (cm *StringTableMap) GetStringTable(id akara.EID) (*StringTableComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddStringTable adds a StringTable component to the given entity and returns it
func (m *StringTableFactory) AddStringTable(id akara.EID) *StringTable {
return m.StringTable.Add(id).(*StringTable)
}
// GetStringTable returns the StringTable component for the given entity, and a bool for whether or not it exists
func (m *StringTableFactory) GetStringTable(id akara.EID) (*StringTable, bool) {
component, found := m.StringTable.Get(id)
if !found {
return nil, found
}
return entry.(*StringTableComponent), found
}
// StringTable is a convenient reference to be used as a component identifier
var StringTable = newStringTable() // nolint:gochecknoglobals // global by design
func newStringTable() akara.Component {
return &StringTableComponent{
BaseComponent: akara.NewBaseComponent(AssetStringTableCID, newStringTable, newStringTableMap),
}
}
func newStringTableMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetStringTableCID, newStringTable, newStringTableMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &StringTableMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*StringTable), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/gravestench/akara"
)
// static check that WavComponent implements Component
var _ akara.Component = &WavComponent{}
// static check that Wav implements Component
var _ akara.Component = &Wav{}
// static check that WavMap implements ComponentMap
var _ akara.ComponentMap = &WavMap{}
// WavComponent is a component that contains an embedded io.ReadSeeker for streaming wav audio files
type WavComponent struct {
*akara.BaseComponent
// Wav is a component that contains an embedded io.ReadSeeker for streaming wav audio files
type Wav struct {
Data io.ReadSeeker
}
// WavMap is a map of entity ID's to Wav
type WavMap struct {
*akara.BaseComponentMap
// New returns a new Wav component. By default, it contains a nil instance.
func (*Wav) New() akara.Component {
return &Wav{}
}
// AddWav adds a new WavComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *WavComponent instead of an akara.Component
func (cm *WavMap) AddWav(id akara.EID) *WavComponent {
return cm.Add(id).(*WavComponent)
// WavFactory is a wrapper for the generic component factory that returns Wav component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Wav.
type WavFactory struct {
Wav *akara.ComponentFactory
}
// GetWav returns the WavComponent associated with the given entity id
func (cm *WavMap) GetWav(id akara.EID) (*WavComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddWav adds a Wav component to the given entity and returns it
func (m *WavFactory) AddWav(id akara.EID) *Wav {
return m.Wav.Add(id).(*Wav)
}
// GetWav returns the Wav component for the given entity, and a bool for whether or not it exists
func (m *WavFactory) GetWav(id akara.EID) (*Wav, bool) {
component, found := m.Wav.Get(id)
if !found {
return nil, found
}
return entry.(*WavComponent), found
}
// Wav is a convenient reference to be used as a component identifier
var Wav = newWav() // nolint:gochecknoglobals // global by design
func newWav() akara.Component {
return &WavComponent{
BaseComponent: akara.NewBaseComponent(AssetWavCID, newWav, newWavMap),
}
}
func newWavMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(AssetWavCID, newWav, newWavMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &WavMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*Wav), found
}

View File

@ -7,63 +7,51 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
)
// static check that CameraComponent implements Component
var _ akara.Component = &CameraComponent{}
const (
defaultCameraWidth = 800
defaultCameraHeight = 600
defaultCameraZoom = 1.0
)
// static check that CameraMap implements ComponentMap
var _ akara.ComponentMap = &CameraMap{}
// static check that Camera implements Component
var _ akara.Component = &Camera{}
// CameraComponent represents a camera that can be rendered to
type CameraComponent struct {
*akara.BaseComponent
// Camera represents a camera that can be rendered to
type Camera struct {
*d2vector.Vector
Width int
Height int
Zoom float64
}
// CameraMap is a map of entity ID's to Camera
type CameraMap struct {
*akara.BaseComponentMap
}
// AddCamera adds a new CameraComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *CameraComponent instead of an akara.Component
func (cm *CameraMap) AddCamera(id akara.EID) *CameraComponent {
camera := cm.Add(id).(*CameraComponent)
camera.Vector = d2vector.NewVector(0, 0)
return camera
}
// GetCamera returns the CameraComponent associated with the given entity id
func (cm *CameraMap) GetCamera(id akara.EID) (*CameraComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*CameraComponent), found
}
// Camera is a convenient reference to be used as a component identifier
var Camera = newCamera() // nolint:gochecknoglobals // global by design
func newCamera() akara.Component {
return &CameraComponent{
BaseComponent: akara.NewBaseComponent(CameraCID, newCamera, newCameraMap),
// New returns a new Camera component.
// The camera defaults to position (0,0), 800x600 resolution, and zoom of 1.0
func (*Camera) New() akara.Component {
return &Camera{
Vector: d2vector.NewVector(0, 0),
Width: defaultCameraWidth,
Height: defaultCameraHeight,
Zoom: defaultCameraZoom,
}
}
func newCameraMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(CameraCID, newCamera, newCameraMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
// CameraFactory is a wrapper for the generic component factory that returns Camera component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Camera.
type CameraFactory struct {
Camera *akara.ComponentFactory
}
cm := &CameraMap{
BaseComponentMap: baseMap,
// AddCamera adds a Camera component to the given entity and returns it
func (m *CameraFactory) AddCamera(id akara.EID) *Camera {
return m.Camera.Add(id).(*Camera)
}
// GetCamera returns the Camera component for the given entity, and a bool for whether or not it exists
func (m *CameraFactory) GetCamera(id akara.EID) (*Camera, bool) {
component, found := m.Camera.Get(id)
if !found {
return nil, found
}
return cm
return component.(*Camera), found
}

View File

@ -5,56 +5,36 @@ import (
"github.com/gravestench/akara"
)
// static check that DirtyComponent implements Component
var _ akara.Component = &DirtyComponent{}
// static check that Dirty implements Component
var _ akara.Component = &Dirty{}
// static check that DirtyMap implements ComponentMap
var _ akara.ComponentMap = &DirtyMap{}
// DirtyComponent is a flag component that is used to denote a "dirty" state
type DirtyComponent struct {
*akara.BaseComponent
// Dirty is a flag component that is used to denote a "dirty" state
type Dirty struct {
IsDirty bool
}
// DirtyMap is a map of entity ID's to Dirty
type DirtyMap struct {
*akara.BaseComponentMap
// New creates a new Dirty. By default, IsDirty is false.
func (*Dirty) New() akara.Component {
return &Dirty{}
}
// AddDirty adds a new DirtyComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *DirtyComponent instead of an akara.Component
func (cm *DirtyMap) AddDirty(id akara.EID) *DirtyComponent {
return cm.Add(id).(*DirtyComponent)
// DirtyFactory is a wrapper for the generic component factory that returns Dirty component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Dirty.
type DirtyFactory struct {
Dirty *akara.ComponentFactory
}
// GetDirty returns the DirtyComponent associated with the given entity id
func (cm *DirtyMap) GetDirty(id akara.EID) (*DirtyComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddDirty adds a Dirty component to the given entity and returns it
func (m *DirtyFactory) AddDirty(id akara.EID) *Dirty {
return m.Dirty.Add(id).(*Dirty)
}
// GetDirty returns the Dirty component for the given entity, and a bool for whether or not it exists
func (m *DirtyFactory) GetDirty(id akara.EID) (*Dirty, bool) {
component, found := m.Dirty.Get(id)
if !found {
return nil, found
}
return entry.(*DirtyComponent), found
}
// Dirty is a convenient reference to be used as a component identifier
var Dirty = newDirty() // nolint:gochecknoglobals // global by design
func newDirty() akara.Component {
return &DirtyComponent{
BaseComponent: akara.NewBaseComponent(DirtyCID, newDirty, newDirtyMap),
}
}
func newDirtyMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(DirtyCID, newDirty, newDirtyMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &DirtyMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*Dirty), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
)
// static check that FileHandleComponent implements Component
var _ akara.Component = &FileHandleComponent{}
// static check that FileHandle implements Component
var _ akara.Component = &FileHandle{}
// static check that FileHandleMap implements ComponentMap
var _ akara.ComponentMap = &FileHandleMap{}
// FileHandleComponent is a component that contains a data stream for file data
type FileHandleComponent struct {
*akara.BaseComponent
// FileHandle is a component that contains a data stream for file data
type FileHandle struct {
Data d2interface.DataStream
}
// FileHandleMap is a map of entity ID's to FileHandle
type FileHandleMap struct {
*akara.BaseComponentMap
// New returns a FileHandle component. By default, it contains a nil instance.
func (*FileHandle) New() akara.Component {
return &FileHandle{}
}
// AddFileHandle adds a new FileHandleComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *FileHandleComponent instead of an akara.Component
func (cm *FileHandleMap) AddFileHandle(id akara.EID) *FileHandleComponent {
return cm.Add(id).(*FileHandleComponent)
// FileHandleFactory is a wrapper for the generic component factory that returns FileHandle component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a FileHandle.
type FileHandleFactory struct {
FileHandle *akara.ComponentFactory
}
// GetFileHandle returns the FileHandleComponent associated with the given entity id
func (cm *FileHandleMap) GetFileHandle(id akara.EID) (*FileHandleComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddFileHandle adds a FileHandle component to the given entity and returns it
func (m *FileHandleFactory) AddFileHandle(id akara.EID) *FileHandle {
return m.FileHandle.Add(id).(*FileHandle)
}
// GetFileHandle returns the FileHandle component for the given entity, and a bool for whether or not it exists
func (m *FileHandleFactory) GetFileHandle(id akara.EID) (*FileHandle, bool) {
component, found := m.FileHandle.Get(id)
if !found {
return nil, found
}
return entry.(*FileHandleComponent), found
}
// FileHandle is a convenient reference to be used as a component identifier
var FileHandle = newFileHandle() // nolint:gochecknoglobals // global by design
func newFileHandle() akara.Component {
return &FileHandleComponent{
BaseComponent: akara.NewBaseComponent(FileHandleCID, newFileHandle, newFileHandleMap),
}
}
func newFileHandleMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(FileHandleCID, newFileHandle, newFileHandleMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &FileHandleMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*FileHandle), found
}

View File

@ -5,56 +5,36 @@ import (
"github.com/gravestench/akara"
)
// static check that FilePathComponent implements Component
var _ akara.Component = &FilePathComponent{}
// static check that FilePath implements Component
var _ akara.Component = &FilePath{}
// static check that FilePathMap implements ComponentMap
var _ akara.ComponentMap = &FilePathMap{}
// FilePathComponent represents a file path for a file
type FilePathComponent struct {
*akara.BaseComponent
// FilePath represents a file path for a file
type FilePath struct {
Path string
}
// FilePathMap is a map of entity ID's to FilePath
type FilePathMap struct {
*akara.BaseComponentMap
// New returns a FilePath component. By default, it contains an empty string.
func (*FilePath) New() akara.Component {
return &FilePath{}
}
// AddFilePath adds a new FilePathComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *FilePathComponent instead of an akara.Component
func (cm *FilePathMap) AddFilePath(id akara.EID) *FilePathComponent {
return cm.Add(id).(*FilePathComponent)
// FilePathFactory is a wrapper for the generic component factory that returns FilePath component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a FilePath.
type FilePathFactory struct {
FilePath *akara.ComponentFactory
}
// GetFilePath returns the FilePathComponent associated with the given entity id
func (cm *FilePathMap) GetFilePath(id akara.EID) (*FilePathComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddFilePath adds a FilePath component to the given entity and returns it
func (m *FilePathFactory) AddFilePath(id akara.EID) *FilePath {
return m.FilePath.Add(id).(*FilePath)
}
// GetFilePath returns the FilePath component for the given entity, and a bool for whether or not it exists
func (m *FilePathFactory) GetFilePath(id akara.EID) (*FilePath, bool) {
component, found := m.FilePath.Get(id)
if !found {
return nil, found
}
return entry.(*FilePathComponent), found
}
// FilePath is a convenient reference to be used as a component identifier
var FilePath = newFilePath() // nolint:gochecknoglobals // global by design
func newFilePath() akara.Component {
return &FilePathComponent{
BaseComponent: akara.NewBaseComponent(FilePathCID, newFilePath, newFilePathMap),
}
}
func newFilePathMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(FilePathCID, newFilePath, newFilePathMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &FilePathMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*FilePath), found
}

View File

@ -7,62 +7,42 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
)
// static check that FileSource implements Component
var _ akara.Component = &FileSource{}
// AbstractSource is the abstract representation of what a file source is
type AbstractSource interface {
Path() string // the path of the source itself
Open(path *FilePathComponent) (d2interface.DataStream, error)
Open(path *FilePath) (d2interface.DataStream, error)
}
// static check that FileSourceComponent implements Component
var _ akara.Component = &FileSourceComponent{}
// static check that FileSourceMap implements ComponentMap
var _ akara.ComponentMap = &FileSourceMap{}
// FileSourceComponent contains an embedded file source interface, something that can open files
type FileSourceComponent struct {
*akara.BaseComponent
// FileSource contains an embedded file source interface, something that can open files
type FileSource struct {
AbstractSource
}
// FileSourceMap is a map of entity ID's to FileSource
type FileSourceMap struct {
*akara.BaseComponentMap
// New returns a FileSource component. By default, it contains a nil instance.
func (*FileSource) New() akara.Component {
return &FileSource{}
}
// AddFileSource adds a new FileSourceComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *FileSourceComponent instead of an akara.Component
func (cm *FileSourceMap) AddFileSource(id akara.EID) *FileSourceComponent {
return cm.Add(id).(*FileSourceComponent)
// FileSourceFactory is a wrapper for the generic component factory that returns FileSource component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a FileSource.
type FileSourceFactory struct {
FileSource *akara.ComponentFactory
}
// GetFileSource returns the FileSourceComponent associated with the given entity id
func (cm *FileSourceMap) GetFileSource(id akara.EID) (*FileSourceComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddFileSource adds a FileSource component to the given entity and returns it
func (m *FileSourceFactory) AddFileSource(id akara.EID) *FileSource {
return m.FileSource.Add(id).(*FileSource)
}
// GetFileSource returns the FileSource component for the given entity, and a bool for whether or not it exists
func (m *FileSourceFactory) GetFileSource(id akara.EID) (*FileSource, bool) {
component, found := m.FileSource.Get(id)
if !found {
return nil, found
}
return entry.(*FileSourceComponent), found
}
// FileSource is a convenient reference to be used as a component identifier
var FileSource = newFileSource() // nolint:gochecknoglobals // global by design
func newFileSource() akara.Component {
return &FileSourceComponent{
BaseComponent: akara.NewBaseComponent(FileSourceCID, newFileSource, newFileSourceMap),
}
}
func newFileSourceMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(FileSourceCID, newFileSource, newFileSourceMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &FileSourceMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*FileSource), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
)
// static check that FileTypeComponent implements Component
var _ akara.Component = &FileTypeComponent{}
// static check that FileType implements Component
var _ akara.Component = &FileType{}
// static check that FileTypeMap implements ComponentMap
var _ akara.ComponentMap = &FileTypeMap{}
// FileTypeComponent is used to flag file entities with a file type
type FileTypeComponent struct {
*akara.BaseComponent
// FileType is used to flag file entities with a file type
type FileType struct {
Type d2enum.FileType
}
// FileTypeMap is a map of entity ID's to FileType
type FileTypeMap struct {
*akara.BaseComponentMap
// New returns a FileType component. By default, it contains a nil instance.
func (*FileType) New() akara.Component {
return &FileType{}
}
// AddFileType adds a new FileTypeComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *FileTypeComponent instead of an akara.Component
func (cm *FileTypeMap) AddFileType(id akara.EID) *FileTypeComponent {
return cm.Add(id).(*FileTypeComponent)
// FileTypeFactory is a wrapper for the generic component factory that returns FileType component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a FileType.
type FileTypeFactory struct {
FileType *akara.ComponentFactory
}
// GetFileType returns the FileTypeComponent associated with the given entity id
func (cm *FileTypeMap) GetFileType(id akara.EID) (*FileTypeComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddFileType adds a FileType component to the given entity and returns it
func (m *FileTypeFactory) AddFileType(id akara.EID) *FileType {
return m.FileType.Add(id).(*FileType)
}
// GetFileType returns the FileType component for the given entity, and a bool for whether or not it exists
func (m *FileTypeFactory) GetFileType(id akara.EID) (*FileType, bool) {
component, found := m.FileType.Get(id)
if !found {
return nil, found
}
return entry.(*FileTypeComponent), found
}
// FileType is a convenient reference to be used as a component identifier
var FileType = newFileType() // nolint:gochecknoglobals // global by design
func newFileType() akara.Component {
return &FileTypeComponent{
BaseComponent: akara.NewBaseComponent(FileTypeCID, newFileType, newFileTypeMap),
}
}
func newFileTypeMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(FileTypeCID, newFileType, newFileTypeMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &FileTypeMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*FileType), found
}

View File

@ -6,20 +6,16 @@ import (
"path"
"runtime"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
)
// static check that GameConfigComponent implements Component
var _ akara.Component = &GameConfigComponent{}
// static check that GameConfig implements Component
var _ akara.Component = &GameConfig{}
// static check that GameConfigMap implements ComponentMap
var _ akara.ComponentMap = &GameConfigMap{}
// GameConfigComponent represents an OpenDiablo2 game configuration
type GameConfigComponent struct {
*akara.BaseComponent
// GameConfig represents an OpenDiablo2 game configuration
type GameConfig struct {
MpqLoadOrder []string
MpqPath string
TicksPerSecond int
@ -33,54 +29,15 @@ type GameConfigComponent struct {
LogLevel d2util.LogLevel
}
// GameConfigMap is a map of entity ID's to GameConfig
type GameConfigMap struct {
*akara.BaseComponentMap
}
// AddGameConfig adds a new GameConfigComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *GameConfigComponent instead of an akara.Component
func (cm *GameConfigMap) AddGameConfig(id akara.EID) *GameConfigComponent {
return defaultConfig(cm.Add(id).(*GameConfigComponent))
}
// GetGameConfig returns the GameConfigComponent associated with the given entity id
func (cm *GameConfigMap) GetGameConfig(id akara.EID) (*GameConfigComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*GameConfigComponent), found
}
// GameConfig is a convenient reference to be used as a component identifier
var GameConfig = newGameConfig() // nolint:gochecknoglobals // global by design
func newGameConfig() akara.Component {
return &GameConfigComponent{
BaseComponent: akara.NewBaseComponent(GameConfigCID, newGameConfig, newGameConfigMap),
}
}
func newGameConfigMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(GameConfigCID, newGameConfig, newGameConfigMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &GameConfigMap{
BaseComponentMap: baseMap,
}
return cm
}
func defaultConfig(config *GameConfigComponent) *GameConfigComponent {
// New creates the default GameConfig
func (*GameConfig) New() akara.Component {
const (
defaultSfxVolume = 1.0
defaultBgmVolume = 0.3
)
config := &GameConfig{}
config.FullScreen = false
config.TicksPerSecond = -1
config.RunInBackground = true
@ -132,3 +89,24 @@ func defaultConfig(config *GameConfigComponent) *GameConfigComponent {
return config
}
// GameConfigFactory is a wrapper for the generic component factory that returns GameConfig component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a GameConfig.
type GameConfigFactory struct {
GameConfig *akara.ComponentFactory
}
// AddGameConfig adds a GameConfig component to the given entity and returns it
func (m *GameConfigFactory) AddGameConfig(id akara.EID) *GameConfig {
return m.GameConfig.Add(id).(*GameConfig)
}
// GetGameConfig returns the GameConfig component for the given entity, and a bool for whether or not it exists
func (m *GameConfigFactory) GetGameConfig(id akara.EID) (*GameConfig, bool) {
component, found := m.GameConfig.Get(id)
if !found {
return nil, found
}
return component.(*GameConfig), found
}

View File

@ -3,60 +3,44 @@ package d2components
import (
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
)
// static check that OriginComponent implements Component
var _ akara.Component = &OriginComponent{}
// static check that Origin implements Component
var _ akara.Component = &Origin{}
// static check that OriginMap implements ComponentMap
var _ akara.ComponentMap = &OriginMap{}
// OriginComponent is a component that describes the origin point of an entity.
// The values are normalized to the display width/height.
// For example, origin (0,0) is top-left corner, (0.5, 0.5) is center
type OriginComponent struct {
*akara.BaseComponent
X, Y float64
// Origin is a component that describes the origin point of an entity as a vector.
// The values should be interpreted as normalized to the width/height of the entity (depends on other components...).
// For example, origin (0,0) should be top-left corner, (0.5, 0.5) should be center.
type Origin struct {
*d2vector.Vector
}
// OriginMap is a map of entity ID's to Origin
type OriginMap struct {
*akara.BaseComponentMap
}
// AddOrigin adds a new OriginComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *OriginComponent instead of an akara.Component
func (cm *OriginMap) AddOrigin(id akara.EID) *OriginComponent {
return cm.Add(id).(*OriginComponent)
}
// GetOrigin returns the OriginComponent associated with the given entity id
func (cm *OriginMap) GetOrigin(id akara.EID) (*OriginComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*OriginComponent), found
}
// Origin is a convenient reference to be used as a component identifier
var Origin = newOrigin() // nolint:gochecknoglobals // global by design
func newOrigin() akara.Component {
return &OriginComponent{
BaseComponent: akara.NewBaseComponent(OriginCID, newOrigin, newOriginMap),
// New creates a new Origin. By default, the origin is the top-left corner (0,0)
func (*Origin) New() akara.Component {
return &Origin{
Vector: d2vector.NewVector(0, 0),
}
}
func newOriginMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(OriginCID, newOrigin, newOriginMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
// OriginFactory is a wrapper for the generic component factory that returns Origin component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Origin.
type OriginFactory struct {
Origin *akara.ComponentFactory
}
cm := &OriginMap{
BaseComponentMap: baseMap,
// AddOrigin adds a Origin component to the given entity and returns it
func (m *OriginFactory) AddOrigin(id akara.EID) *Origin {
return m.Origin.Add(id).(*Origin)
}
// GetOrigin returns the Origin component for the given entity, and a bool for whether or not it exists
func (m *OriginFactory) GetOrigin(id akara.EID) (*Origin, bool) {
component, found := m.Origin.Get(id)
if !found {
return nil, found
}
return cm
return component.(*Origin), found
}

View File

@ -7,62 +7,41 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
)
// static check that PositionComponent implements Component
var _ akara.Component = &PositionComponent{}
// static check that Position implements Component
var _ akara.Component = &Position{}
// static check that PositionMap implements ComponentMap
var _ akara.ComponentMap = &PositionMap{}
// PositionComponent contains an embedded d2vector.Position
type PositionComponent struct {
*akara.BaseComponent
// Position contains an embedded d2vector.Position, which is a vector with
// helper methods for translating between screen, isometric, tile, and sub-tile space.
type Position struct {
*d2vector.Position
}
// PositionMap is a map of entity ID's to Position
type PositionMap struct {
*akara.BaseComponentMap
}
// AddPosition adds a new PositionComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *PositionComponent instead of an akara.Component
func (cm *PositionMap) AddPosition(id akara.EID) *PositionComponent {
pos := cm.Add(id).(*PositionComponent)
// New creates a new Position. By default, the position is (0,0)
func (*Position) New() akara.Component {
p := d2vector.NewPosition(0, 0)
pos.Position = &p
return pos
}
// GetPosition returns the PositionComponent associated with the given entity id
func (cm *PositionMap) GetPosition(id akara.EID) (*PositionComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*PositionComponent), found
}
// Position is a convenient reference to be used as a component identifier
var Position = newPosition() // nolint:gochecknoglobals // global by design
func newPosition() akara.Component {
return &PositionComponent{
BaseComponent: akara.NewBaseComponent(PositionCID, newPosition, newPositionMap),
return &Position{
Position: &p,
}
}
func newPositionMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(PositionCID, newPosition, newPositionMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
// PositionFactory is a wrapper for the generic component factory that returns Position component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Position.
type PositionFactory struct {
Position *akara.ComponentFactory
}
cm := &PositionMap{
BaseComponentMap: baseMap,
// AddPosition adds a Position component to the given entity and returns it
func (m *PositionFactory) AddPosition(id akara.EID) *Position {
return m.Position.Add(id).(*Position)
}
// GetPosition returns the Position component for the given entity, and a bool for whether or not it exists
func (m *PositionFactory) GetPosition(id akara.EID) (*Position, bool) {
component, found := m.Position.Get(id)
if !found {
return nil, found
}
return cm
return component.(*Position), found
}

View File

@ -5,57 +5,37 @@ import (
"github.com/gravestench/akara"
)
// static check that PriorityComponent implements Component
var _ akara.Component = &PriorityComponent{}
// static check that Priority implements Component
var _ akara.Component = new(Priority)
// static check that PriorityMap implements ComponentMap
var _ akara.ComponentMap = &PriorityMap{}
// PriorityComponent is a component that is used to add a priority value.
// Priority is a component that is used to add a priority value.
// This can generally be used for sorting entities when order matters.
type PriorityComponent struct {
*akara.BaseComponent
type Priority struct {
Priority int
}
// PriorityMap is a map of entity ID's to Priority
type PriorityMap struct {
*akara.BaseComponentMap
// New returns a new Priority instance. The default is 0.
func (Priority) New() akara.Component {
return &Priority{}
}
// AddPriority adds a new PriorityComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *PriorityComponent instead of an akara.Component
func (cm *PriorityMap) AddPriority(id akara.EID) *PriorityComponent {
return cm.Add(id).(*PriorityComponent)
// PriorityFactory is a wrapper for the generic component factory that returns Priority component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Priority.
type PriorityFactory struct {
Priority *akara.ComponentFactory
}
// GetPriority returns the PriorityComponent associated with the given entity id
func (cm *PriorityMap) GetPriority(id akara.EID) (*PriorityComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddPriority adds a Priority component to the given entity and returns it
func (m *PriorityFactory) AddPriority(id akara.EID) *Priority {
return m.Priority.Add(id).(*Priority)
}
// GetPriority returns the Priority component for the given entity, and a bool for whether or not it exists
func (m *PriorityFactory) GetPriority(id akara.EID) (*Priority, bool) {
component, found := m.Priority.Get(id)
if !found {
return nil, found
}
return entry.(*PriorityComponent), found
}
// Priority is a convenient reference to be used as a component identifier
var Priority = newPriority() // nolint:gochecknoglobals // global by design
func newPriority() akara.Component {
return &PriorityComponent{
BaseComponent: akara.NewBaseComponent(PriorityCID, newPriority, newPriorityMap),
}
}
func newPriorityMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(PriorityCID, newPriority, newPriorityMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &PriorityMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*Priority), found
}

View File

@ -7,56 +7,36 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"
)
// static check that RenderableComponent implements Component
var _ akara.Component = &RenderableComponent{}
// static check that Renderable implements Component
var _ akara.Component = &Renderable{}
// static check that RenderableMap implements ComponentMap
var _ akara.ComponentMap = &RenderableMap{}
// RenderableComponent is a component that contains an embedded surface interface, which is used for rendering
type RenderableComponent struct {
*akara.BaseComponent
// Renderable is a component that contains an embedded surface interface, which is used for rendering
type Renderable struct {
d2interface.Surface
}
// RenderableMap is a map of entity ID's to Renderable
type RenderableMap struct {
*akara.BaseComponentMap
// New returns a Renderable component. By default, it contains a nil instance.
func (*Renderable) New() akara.Component {
return &Renderable{}
}
// AddRenderable adds a new RenderableComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *RenderableComponent instead of an akara.Component
func (cm *RenderableMap) AddRenderable(id akara.EID) *RenderableComponent {
return cm.Add(id).(*RenderableComponent)
// RenderableFactory is a wrapper for the generic component factory that returns Renderable component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Renderable.
type RenderableFactory struct {
Renderable *akara.ComponentFactory
}
// GetRenderable returns the RenderableComponent associated with the given entity id
func (cm *RenderableMap) GetRenderable(id akara.EID) (*RenderableComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// AddRenderable adds a Renderable component to the given entity and returns it
func (m *RenderableFactory) AddRenderable(id akara.EID) *Renderable {
return m.Renderable.Add(id).(*Renderable)
}
// GetRenderable returns the Renderable component for the given entity, and a bool for whether or not it exists
func (m *RenderableFactory) GetRenderable(id akara.EID) (*Renderable, bool) {
component, found := m.Renderable.Get(id)
if !found {
return nil, found
}
return entry.(*RenderableComponent), found
}
// Renderable is a convenient reference to be used as a component identifier
var Renderable = newRenderable() // nolint:gochecknoglobals // global by design
func newRenderable() akara.Component {
return &RenderableComponent{
BaseComponent: akara.NewBaseComponent(RenderableCID, newRenderable, newRenderableMap),
}
}
func newRenderableMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(RenderableCID, newRenderable, newRenderableMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &RenderableMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*Renderable), found
}

View File

@ -7,60 +7,38 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
)
// static check that ScaleComponent implements Component
var _ akara.Component = &ScaleComponent{}
// static check that Scale implements Component
var _ akara.Component = &Scale{}
// static check that ScaleMap implements ComponentMap
var _ akara.ComponentMap = &ScaleMap{}
// ScaleComponent represents an entities x,y axis scale as a vector
type ScaleComponent struct {
*akara.BaseComponent
// Scale represents an entities x,y axis scale as a vector
type Scale struct {
*d2vector.Vector
}
// ScaleMap is a map of entity ID's to Scale
type ScaleMap struct {
*akara.BaseComponentMap
}
// AddScale adds a new ScaleComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *ScaleComponent instead of an akara.Component
func (cm *ScaleMap) AddScale(id akara.EID) *ScaleComponent {
c := cm.Add(id).(*ScaleComponent)
c.Vector = d2vector.NewVector(1, 1)
return c
}
// GetScale returns the ScaleComponent associated with the given entity id
func (cm *ScaleMap) GetScale(id akara.EID) (*ScaleComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*ScaleComponent), found
}
// Scale is a convenient reference to be used as a component identifier
var Scale = newScale() // nolint:gochecknoglobals // global by design
func newScale() akara.Component {
return &ScaleComponent{
BaseComponent: akara.NewBaseComponent(ScaleCID, newScale, newScaleMap),
// New creates a new Scale instance. By default, the scale is (1,1)
func (*Scale) New() akara.Component {
return &Scale{
Vector: d2vector.NewVector(1, 1),
}
}
func newScaleMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(ScaleCID, newScale, newScaleMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
// ScaleFactory is a wrapper for the generic component factory that returns Scale component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Scale.
type ScaleFactory struct {
Scale *akara.ComponentFactory
}
cm := &ScaleMap{
BaseComponentMap: baseMap,
// AddScale adds a Scale component to the given entity and returns it
func (m *ScaleFactory) AddScale(id akara.EID) *Scale {
return m.Scale.Add(id).(*Scale)
}
// GetScale returns the Scale component for the given entity, and a bool for whether or not it exists
func (m *ScaleFactory) GetScale(id akara.EID) (*Scale, bool) {
component, found := m.Scale.Get(id)
if !found {
return nil, found
}
return cm
return component.(*Scale), found
}

View File

@ -5,63 +5,43 @@ import (
"github.com/gravestench/akara"
)
// static check that SegmentedSpriteComponent implements Component
var _ akara.Component = &SegmentedSpriteComponent{}
// static check that SegmentedSprite implements Component
var _ akara.Component = &SegmentedSprite{}
// static check that SegmentedSpriteMap implements ComponentMap
var _ akara.ComponentMap = &SegmentedSpriteMap{}
// SegmentedSpriteComponent represents an entities x,y axis scale as a vector
type SegmentedSpriteComponent struct {
*akara.BaseComponent
// SegmentedSprite represents the segmentation of a dcc or dc6 sprite.
// For example, the title screen background is a single image with 12 frames.
// The segmentation can be described as 4 x segments, 3 y segments, with a frame offset of 0.
type SegmentedSprite struct {
Xsegments int
Ysegments int
FrameOffset int
}
// SegmentedSpriteMap is a map of entity ID's to SegmentedSprite
type SegmentedSpriteMap struct {
*akara.BaseComponentMap
}
// AddSegmentedSprite adds a new SegmentedSpriteComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *SegmentedSpriteComponent instead of an akara.Component
func (cm *SegmentedSpriteMap) AddSegmentedSprite(id akara.EID) *SegmentedSpriteComponent {
c := cm.Add(id).(*SegmentedSpriteComponent)
c.Xsegments = 1
c.Ysegments = 1
return c
}
// GetSegmentedSprite returns the SegmentedSpriteComponent associated with the given entity id
func (cm *SegmentedSpriteMap) GetSegmentedSprite(id akara.EID) (*SegmentedSpriteComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*SegmentedSpriteComponent), found
}
// SegmentedSprite is a convenient reference to be used as a component identifier
var SegmentedSprite = newSegmentedSprite() // nolint:gochecknoglobals // global by design
func newSegmentedSprite() akara.Component {
return &SegmentedSpriteComponent{
BaseComponent: akara.NewBaseComponent(SegmentedSpriteCID, newSegmentedSprite, newSegmentedSpriteMap),
// New creates a new SegmentedSprite component. By default, x and y segments are both set to 1.
func (*SegmentedSprite) New() akara.Component {
return &SegmentedSprite{
Xsegments: 1,
Ysegments: 1,
}
}
func newSegmentedSpriteMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(SegmentedSpriteCID, newSegmentedSprite, newSegmentedSpriteMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
// SegmentedSpriteFactory is a wrapper for the generic component factory that returns SegmentedSprite component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a SegmentedSprite.
type SegmentedSpriteFactory struct {
SegmentedSprite *akara.ComponentFactory
}
cm := &SegmentedSpriteMap{
BaseComponentMap: baseMap,
// AddSegmentedSprite adds a SegmentedSprite component to the given entity and returns it
func (m *SegmentedSpriteFactory) AddSegmentedSprite(id akara.EID) *SegmentedSprite {
return m.SegmentedSprite.Add(id).(*SegmentedSprite)
}
// GetSegmentedSprite returns the SegmentedSprite component for the given entity, and a bool for whether or not it exists
func (m *SegmentedSpriteFactory) GetSegmentedSprite(id akara.EID) (*SegmentedSprite, bool) {
component, found := m.SegmentedSprite.Get(id)
if !found {
return nil, found
}
return cm
return component.(*SegmentedSprite), found
}

View File

@ -7,60 +7,38 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
)
// static check that SizeComponent implements Component
var _ akara.Component = &SizeComponent{}
// static check that Size implements Component
var _ akara.Component = &Size{}
// static check that SizeMap implements ComponentMap
var _ akara.ComponentMap = &SizeMap{}
// SizeComponent represents an entities width and height as a vector
type SizeComponent struct {
*akara.BaseComponent
// Size represents an entities width and height as a vector
type Size struct {
*d2vector.Vector
}
// SizeMap is a map of entity ID's to Size
type SizeMap struct {
*akara.BaseComponentMap
}
// AddSize adds a new SizeComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *SizeComponent instead of an akara.Component
func (cm *SizeMap) AddSize(id akara.EID) *SizeComponent {
c := cm.Add(id).(*SizeComponent)
c.Vector = d2vector.NewVector(1, 1)
return c
}
// GetSize returns the SizeComponent associated with the given entity id
func (cm *SizeMap) GetSize(id akara.EID) (*SizeComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*SizeComponent), found
}
// Size is a convenient reference to be used as a component identifier
var Size = newSize() // nolint:gochecknoglobals // global by design
func newSize() akara.Component {
return &SizeComponent{
BaseComponent: akara.NewBaseComponent(SizeCID, newSize, newSizeMap),
// New creates a new Size. By default, size is (0,0).
func (*Size) New() akara.Component {
return &Size{
Vector: d2vector.NewVector(0, 0),
}
}
func newSizeMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(SizeCID, newSize, newSizeMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
// SizeFactory is a wrapper for the generic component factory that returns Size component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Size.
type SizeFactory struct {
Size *akara.ComponentFactory
}
cm := &SizeMap{
BaseComponentMap: baseMap,
// AddSize adds a Size component to the given entity and returns it
func (m *SizeFactory) AddSize(id akara.EID) *Size {
return m.Size.Add(id).(*Size)
}
// GetSize returns the Size component for the given entity, and a bool for whether or not it exists
func (m *SizeFactory) GetSize(id akara.EID) (*Size, bool) {
component, found := m.Size.Get(id)
if !found {
return nil, found
}
return cm
return component.(*Size), found
}

View File

@ -7,60 +7,38 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2math/d2vector"
)
// static check that VelocityComponent implements Component
var _ akara.Component = &VelocityComponent{}
// static check that Velocity implements Component
var _ akara.Component = &Velocity{}
// static check that VelocityMap implements ComponentMap
var _ akara.ComponentMap = &VelocityMap{}
// VelocityComponent contains an embedded velocity as a vector
type VelocityComponent struct {
*akara.BaseComponent
// Velocity contains an embedded velocity as a vector
type Velocity struct {
*d2vector.Vector
}
// VelocityMap is a map of entity ID's to Velocity
type VelocityMap struct {
*akara.BaseComponentMap
}
// AddVelocity adds a new VelocityComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *VelocityComponent instead of an akara.Component
func (cm *VelocityMap) AddVelocity(id akara.EID) *VelocityComponent {
c := cm.Add(id).(*VelocityComponent)
c.Vector = d2vector.NewVector(0, 0)
return c
}
// GetVelocity returns the VelocityComponent associated with the given entity id
func (cm *VelocityMap) GetVelocity(id akara.EID) (*VelocityComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*VelocityComponent), found
}
// Velocity is a convenient reference to be used as a component identifier
var Velocity = newVelocity() // nolint:gochecknoglobals // global by design
func newVelocity() akara.Component {
return &VelocityComponent{
BaseComponent: akara.NewBaseComponent(VelocityCID, newVelocity, newVelocityMap),
// New creates a new Velocity. By default, the velocity is (0,0).
func (*Velocity) New() akara.Component {
return &Velocity{
Vector: d2vector.NewVector(0, 0),
}
}
func newVelocityMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(VelocityCID, newVelocity, newVelocityMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
// VelocityFactory is a wrapper for the generic component factory that returns Velocity component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Velocity.
type VelocityFactory struct {
Velocity *akara.ComponentFactory
}
cm := &VelocityMap{
BaseComponentMap: baseMap,
// AddVelocity adds a Velocity component to the given entity and returns it
func (m *VelocityFactory) AddVelocity(id akara.EID) *Velocity {
return m.Velocity.Add(id).(*Velocity)
}
// GetVelocity returns the Velocity component for the given entity, and a bool for whether or not it exists
func (m *VelocityFactory) GetVelocity(id akara.EID) (*Velocity, bool) {
component, found := m.Velocity.Get(id)
if !found {
return nil, found
}
return cm
return component.(*Velocity), found
}

View File

@ -7,29 +7,19 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2geom"
)
// static check that ViewportComponent implements Component
var _ akara.Component = &ViewportComponent{}
// static check that Viewport implements Component
var _ akara.Component = &Viewport{}
// static check that ViewportMap implements ComponentMap
var _ akara.ComponentMap = &ViewportMap{}
// ViewportComponent represents the size and position of a scene viewport. This is used
// Viewport represents the size and position of a scene viewport. This is used
// to control where on screen a viewport is rendered.
type ViewportComponent struct {
*akara.BaseComponent
type Viewport struct {
*d2geom.Rectangle
}
// ViewportMap is a map of entity ID's to Viewport
type ViewportMap struct {
*akara.BaseComponentMap
}
// AddViewport adds a new ViewportComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *ViewportComponent instead of an akara.Component
func (cm *ViewportMap) AddViewport(id akara.EID) *ViewportComponent {
c := cm.Add(id).(*ViewportComponent)
// New creates a new Viewport. By default, the viewport size is 800x600,
// and is positioned at the top-left of the screen.
func (*Viewport) New() akara.Component {
c := &Viewport{}
const defaultWidth, defaultHeight = 800, 600
@ -43,32 +33,23 @@ func (cm *ViewportMap) AddViewport(id akara.EID) *ViewportComponent {
return c
}
// GetViewport returns the ViewportComponent associated with the given entity id
func (cm *ViewportMap) GetViewport(id akara.EID) (*ViewportComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*ViewportComponent), found
// ViewportFactory is a wrapper for the generic component factory that returns Viewport component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Viewport.
type ViewportFactory struct {
Viewport *akara.ComponentFactory
}
// Viewport is a convenient reference to be used as a component identifier
var Viewport = newViewport() // nolint:gochecknoglobals // global by design
func newViewport() akara.Component {
return &ViewportComponent{
BaseComponent: akara.NewBaseComponent(ViewportCID, newViewport, newViewportMap),
}
// AddViewport adds a Viewport component to the given entity and returns it
func (m *ViewportFactory) AddViewport(id akara.EID) *Viewport {
return m.Viewport.Add(id).(*Viewport)
}
func newViewportMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(ViewportCID, newViewport, newViewportMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &ViewportMap{
BaseComponentMap: baseMap,
// GetViewport returns the Viewport component for the given entity, and a bool for whether or not it exists
func (m *ViewportFactory) GetViewport(id akara.EID) (*Viewport, bool) {
component, found := m.Viewport.Get(id)
if !found {
return nil, found
}
return cm
return component.(*Viewport), found
}

View File

@ -5,61 +5,42 @@ import (
"github.com/gravestench/akara"
)
// static check that ViewportFilterComponent implements Component
var _ akara.Component = &ViewportFilterComponent{}
// static check that ViewportFilter implements Component
var _ akara.Component = &ViewportFilter{}
// static check that ViewportFilterMap implements ComponentMap
var _ akara.ComponentMap = &ViewportFilterMap{}
// ViewportFilterComponent is a component that contains a bitset that denotes which viewport
// ViewportFilter is a component that contains a bitset that denotes which viewport
// the entity will be rendered.
type ViewportFilterComponent struct {
*akara.BaseComponent
type ViewportFilter struct {
*akara.BitSet
}
// ViewportFilterMap is a map of entity ID's to ViewportFilter
type ViewportFilterMap struct {
*akara.BaseComponentMap
}
// New creates a new ViewportFilter.
// By default, the filter is set to only allow the main scene viewport.
func (*ViewportFilter) New() akara.Component {
const mainViewport = 0
// AddViewportFilter adds a new ViewportFilterComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *ViewportFilterComponent instead of an akara.Component
func (cm *ViewportFilterMap) AddViewportFilter(id akara.EID) *ViewportFilterComponent {
c := cm.Add(id).(*ViewportFilterComponent)
c.BitSet = akara.NewBitSet(0)
return c
}
// GetViewportFilter returns the ViewportFilterComponent associated with the given entity id
func (cm *ViewportFilterMap) GetViewportFilter(id akara.EID) (*ViewportFilterComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
}
return entry.(*ViewportFilterComponent), found
}
// ViewportFilter is a convenient reference to be used as a component identifier
var ViewportFilter = newViewportFilter() // nolint:gochecknoglobals // global by design
func newViewportFilter() akara.Component {
return &ViewportFilterComponent{
BaseComponent: akara.NewBaseComponent(ViewportFilterCID, newViewportFilter, newViewportFilterMap),
return &ViewportFilter{
BitSet: akara.NewBitSet(mainViewport),
}
}
func newViewportFilterMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(ViewportFilterCID, newViewportFilter, newViewportFilterMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
// ViewportFilterFactory is a wrapper for the generic component factory that returns ViewportFilter component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a ViewportFilter.
type ViewportFilterFactory struct {
ViewportFilter *akara.ComponentFactory
}
cm := &ViewportFilterMap{
BaseComponentMap: baseMap,
// AddViewportFilter adds a ViewportFilter component to the given entity and returns it
func (m *ViewportFilterFactory) AddViewportFilter(id akara.EID) *ViewportFilter {
return m.ViewportFilter.Add(id).(*ViewportFilter)
}
// GetViewportFilter returns the ViewportFilter component for the given entity, and a bool for whether or not it exists
func (m *ViewportFilterFactory) GetViewportFilter(id akara.EID) (*ViewportFilter, bool) {
component, found := m.ViewportFilter.Get(id)
if !found {
return nil, found
}
return cm
return component.(*ViewportFilter), found
}

View File

@ -4,55 +4,34 @@ import (
"github.com/gravestench/akara"
)
// static check that MainViewportComponent implements Component
var _ akara.Component = &MainViewportComponent{}
// static check that MainViewport implements Component
var _ akara.Component = &MainViewport{}
// static check that MainViewportMap implements ComponentMap
var _ akara.ComponentMap = &MainViewportMap{}
// MainViewport is used to flag viewports as the main viewport of a scene
type MainViewport struct{}
// MainViewportComponent is used to flag viewports as the main viewport of a scene
type MainViewportComponent struct {
*akara.BaseComponent
// New returns a new MainViewport instance. It is always a nil instance.
func (*MainViewport) New() akara.Component {
return (*MainViewport)(nil)
}
// MainViewportMap is a map of entity ID's to MainViewport
type MainViewportMap struct {
*akara.BaseComponentMap
// MainViewportFactory is a wrapper for the generic component factory that returns MainViewport component instances.
// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a MainViewport.
type MainViewportFactory struct {
MainViewport *akara.ComponentFactory
}
// AddMainViewport adds a new MainViewportComponent for the given entity id and returns it.
// this is a convenience method for the generic Add method, as it returns a
// *MainViewportComponent instead of an akara.Component
func (cm *MainViewportMap) AddMainViewport(id akara.EID) *MainViewportComponent {
return cm.Add(id).(*MainViewportComponent)
// AddMainViewport adds a MainViewport component to the given entity and returns it
func (m *MainViewportFactory) AddMainViewport(id akara.EID) *MainViewport {
return m.MainViewport.Add(id).(*MainViewport)
}
// GetMainViewport returns the MainViewportComponent associated with the given entity id
func (cm *MainViewportMap) GetMainViewport(id akara.EID) (*MainViewportComponent, bool) {
entry, found := cm.Get(id)
if entry == nil {
return nil, false
// GetMainViewport returns the MainViewport component for the given entity, and a bool for whether or not it exists
func (m *MainViewportFactory) GetMainViewport(id akara.EID) (*MainViewport, bool) {
component, found := m.MainViewport.Get(id)
if !found {
return nil, found
}
return entry.(*MainViewportComponent), found
}
// MainViewport is a convenient reference to be used as a component identifier
var MainViewport = newMainViewport() // nolint:gochecknoglobals // global by design
func newMainViewport() akara.Component {
return &MainViewportComponent{
BaseComponent: akara.NewBaseComponent(MainViewportCID, newMainViewport, newMainViewportMap),
}
}
func newMainViewportMap() akara.ComponentMap {
baseComponent := akara.NewBaseComponent(MainViewportCID, newMainViewport, newMainViewportMap)
baseMap := akara.NewBaseComponentMap(baseComponent)
cm := &MainViewportMap{
BaseComponentMap: baseMap,
}
return cm
return component.(*MainViewport), found
}

View File

@ -22,87 +22,109 @@ const (
// static check that the game config system implements the system interface
var _ akara.System = &AppBootstrapSystem{}
// NewAppBootstrapSystem creates a system that initializes the common application facilities, namely
// config file source directories and loading/creating the OpenDiablo2 config file.
func NewAppBootstrapSystem() *AppBootstrapSystem {
// we are going to check entities that dont yet have loaded asset types
filesToCheck := akara.NewFilter().
Require( // files that need to be loaded
d2components.FileType,
d2components.FileHandle,
d2components.FilePath,
).
Forbid( // files which have been loaded
d2components.GameConfig,
d2components.StringTable,
d2components.DataDictionary,
d2components.Palette,
d2components.PaletteTransform,
d2components.Cof,
d2components.Dc6,
d2components.Dcc,
d2components.Ds1,
d2components.Dt1,
d2components.Wav,
d2components.AnimData,
).
Build()
// we are interested in actual game config instances, too
gameConfigs := akara.NewFilter().Require(d2components.GameConfig).Build()
sys := &AppBootstrapSystem{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(filesToCheck, gameConfigs),
Logger: d2util.NewLogger(),
}
sys.SetPrefix(logPrefixAppBootstrap)
sys.Debug("Created")
return sys
}
// AppBootstrapSystem is responsible for the common initialization process between
// the app modes (eg common to the game client as well as the headless server)
type AppBootstrapSystem struct {
*akara.BaseSubscriberSystem
akara.BaseSubscriberSystem
*d2util.Logger
subscribedFiles *akara.Subscription
subscribedConfigs *akara.Subscription
*d2components.GameConfigMap
*d2components.FilePathMap
*d2components.FileTypeMap
*d2components.FileHandleMap
*d2components.FileSourceMap
d2components.GameConfigFactory
d2components.FilePathFactory
d2components.FileTypeFactory
d2components.FileHandleFactory
d2components.FileSourceFactory
}
// Init will inject (or use existing) components related to setting up the config sources
func (m *AppBootstrapSystem) Init(world *akara.World) {
m.World = world
m.setupLogger()
m.Info("initializing ...")
m.subscribedFiles = m.Subscriptions[0]
m.subscribedConfigs = m.Subscriptions[1]
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.GameConfigMap = world.InjectMap(d2components.GameConfig).(*d2components.GameConfigMap)
m.FilePathMap = world.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.FileTypeMap = world.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
m.FileHandleMap = world.InjectMap(d2components.FileHandle).(*d2components.FileHandleMap)
m.FileSourceMap = world.InjectMap(d2components.FileSource).(*d2components.FileSourceMap)
m.setupSubscriptions()
m.setupFactories()
m.injectSystems()
m.setupConfigSources()
m.setupConfigFile()
m.Info("... initialization complete!")
}
func (m *AppBootstrapSystem) setupLogger() {
m.Logger = d2util.NewLogger()
m.SetPrefix(logPrefixAppBootstrap)
}
func (m *AppBootstrapSystem) setupSubscriptions() {
m.Info("setting up component subscriptions")
// we are going to check entities that dont yet have loaded asset types
filesToCheck := m.NewComponentFilter().
Require( // files that need to be loaded
&d2components.FileType{},
&d2components.FileHandle{},
&d2components.FilePath{},
).
Forbid( // files which have been loaded
&d2components.GameConfig{},
&d2components.StringTable{},
&d2components.DataDictionary{},
&d2components.Palette{},
&d2components.PaletteTransform{},
&d2components.Cof{},
&d2components.Dc6{},
&d2components.Dcc{},
&d2components.Ds1{},
&d2components.Dt1{},
&d2components.Wav{},
&d2components.AnimationData{},
).
Build()
// we are interested in actual game config instances, too
gameConfigs := m.NewComponentFilter().Require(&d2components.GameConfig{}).Build()
m.subscribedFiles = m.World.AddSubscription(filesToCheck)
m.subscribedConfigs = m.World.AddSubscription(gameConfigs)
}
func (m *AppBootstrapSystem) setupFactories() {
m.Info("setting up component factories")
gameConfigID := m.RegisterComponent(&d2components.GameConfig{})
filePathID := m.RegisterComponent(&d2components.FilePath{})
fileTypeID := m.RegisterComponent(&d2components.FileType{})
fileHandleID := m.RegisterComponent(&d2components.FileHandle{})
fileSourceID := m.RegisterComponent(&d2components.FileSource{})
m.GameConfig = m.GetComponentFactory(gameConfigID)
m.FilePath = m.GetComponentFactory(filePathID)
m.FileType = m.GetComponentFactory(fileTypeID)
m.FileHandle = m.GetComponentFactory(fileHandleID)
m.FileSource = m.GetComponentFactory(fileSourceID)
}
func (m *AppBootstrapSystem) injectSystems() {
m.World.AddSystem(NewFileTypeResolver())
m.World.AddSystem(NewFileSourceResolver())
m.World.AddSystem(NewFileHandleResolver())
m.World.AddSystem(NewGameConfigSystem())
m.World.AddSystem(NewAssetLoader())
m.World.AddSystem(NewGameObjectFactory())
m.Info("injecting file type resolution system")
m.AddSystem(&FileTypeResolver{})
m.Info("injecting file source resolution system")
m.AddSystem(&FileSourceResolver{})
m.Info("injecting file handle resolution system")
m.AddSystem(&FileHandleResolver{})
m.Info("injecting game configuration system")
m.AddSystem(&GameConfigSystem{})
m.Info("injecting asset loader system")
m.AddSystem(&AssetLoaderSystem{})
m.Info("injecting game object factory system")
m.AddSystem(&GameObjectFactory{})
}
// we make two entities and assign file paths for the two directories that
@ -159,13 +181,14 @@ func (m *AppBootstrapSystem) Update() {
return
}
m.Info("adding mpq's from config file")
m.initMpqSources(cfg)
m.Info("app bootstrap complete, deactivating system")
m.SetActive(false) // bootstrap is complete!
m.SetActive(false)
}
func (m *AppBootstrapSystem) initMpqSources(cfg *d2components.GameConfigComponent) {
func (m *AppBootstrapSystem) initMpqSources(cfg *d2components.GameConfig) {
for _, mpqFileName := range cfg.MpqLoadOrder {
fullMpqFilePath := path.Join(cfg.MpqPath, mpqFileName)

View File

@ -31,95 +31,123 @@ const (
logPrefixAssetLoader = "Asset Loader System"
)
// NewAssetLoader creates a new asset loader instance
func NewAssetLoader() *AssetLoaderSystem {
// we are going to check entities that dont yet have loaded asset types
filesToLoad := akara.NewFilter().
Require(d2components.FilePath). // we want to process entities with these file components
Require(d2components.FileType).
Require(d2components.FileHandle).
Forbid(d2components.FileSource). // but we forbid files that are already loaded
Forbid(d2components.GameConfig).
Forbid(d2components.StringTable).
Forbid(d2components.DataDictionary).
Forbid(d2components.Palette).
Forbid(d2components.PaletteTransform).
Forbid(d2components.Cof).
Forbid(d2components.Dc6).
Forbid(d2components.Dcc).
Forbid(d2components.Ds1).
Forbid(d2components.Dt1).
Forbid(d2components.Wav).
Forbid(d2components.AnimData).
Build()
fileSources := akara.NewFilter().
Require(d2components.FileSource).
Build()
assetLoader := &AssetLoaderSystem{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(filesToLoad, fileSources),
cache: d2cache.CreateCache(assetCacheBudget).(*d2cache.Cache),
Logger: d2util.NewLogger(),
}
assetLoader.SetPrefix(logPrefixAssetLoader)
return assetLoader
}
var _ akara.System = &AssetLoaderSystem{}
// AssetLoaderSystem is responsible for parsing data from file handles into various structs, like COF or DC6
type AssetLoaderSystem struct {
*akara.BaseSubscriberSystem
akara.BaseSubscriberSystem
*d2util.Logger
fileSub *akara.Subscription
sourceSub *akara.Subscription
cache *d2cache.Cache
*d2components.FilePathMap
*d2components.FileTypeMap
*d2components.FileHandleMap
*d2components.FileSourceMap
*d2components.StringTableMap
*d2components.FontTableMap
*d2components.DataDictionaryMap
*d2components.PaletteMap
*d2components.PaletteTransformMap
*d2components.CofMap
*d2components.Dc6Map
*d2components.DccMap
*d2components.Ds1Map
*d2components.Dt1Map
*d2components.WavMap
*d2components.AnimDataMap
d2components.FilePathFactory
d2components.FileTypeFactory
d2components.FileHandleFactory
d2components.FileSourceFactory
d2components.StringTableFactory
d2components.FontTableFactory
d2components.DataDictionaryFactory
d2components.PaletteFactory
d2components.PaletteTransformFactory
d2components.CofFactory
d2components.Dc6Factory
d2components.DccFactory
d2components.Ds1Factory
d2components.Dt1Factory
d2components.WavFactory
d2components.AnimationDataFactory
}
// Init injects component maps related to various asset types
func (m *AssetLoaderSystem) Init(_ *akara.World) {
func (m *AssetLoaderSystem) Init(world *akara.World) {
m.World = world
m.setupLogger()
m.Info("initializing ...")
m.fileSub = m.Subscriptions[0]
m.sourceSub = m.Subscriptions[1]
m.setupSubscriptions()
m.setupFactories()
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.FilePathMap = m.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.FileTypeMap = m.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
m.FileHandleMap = m.InjectMap(d2components.FileHandle).(*d2components.FileHandleMap)
m.FileSourceMap = m.InjectMap(d2components.FileSource).(*d2components.FileSourceMap)
m.StringTableMap = m.InjectMap(d2components.StringTable).(*d2components.StringTableMap)
m.DataDictionaryMap = m.InjectMap(d2components.DataDictionary).(*d2components.DataDictionaryMap)
m.PaletteMap = m.InjectMap(d2components.Palette).(*d2components.PaletteMap)
m.PaletteTransformMap = m.InjectMap(d2components.PaletteTransform).(*d2components.PaletteTransformMap)
m.FontTableMap = m.InjectMap(d2components.FontTable).(*d2components.FontTableMap)
m.CofMap = m.InjectMap(d2components.Cof).(*d2components.CofMap)
m.Dc6Map = m.InjectMap(d2components.Dc6).(*d2components.Dc6Map)
m.DccMap = m.InjectMap(d2components.Dcc).(*d2components.DccMap)
m.Ds1Map = m.InjectMap(d2components.Ds1).(*d2components.Ds1Map)
m.Dt1Map = m.InjectMap(d2components.Dt1).(*d2components.Dt1Map)
m.WavMap = m.InjectMap(d2components.Wav).(*d2components.WavMap)
m.AnimDataMap = m.InjectMap(d2components.AnimData).(*d2components.AnimDataMap)
m.cache = d2cache.CreateCache(assetCacheBudget).(*d2cache.Cache)
}
func (m *AssetLoaderSystem) setupLogger() {
m.Logger = d2util.NewLogger()
m.SetPrefix(logPrefixAssetLoader)
}
func (m *AssetLoaderSystem) setupSubscriptions() {
m.Info("setting up component subscriptions")
// we are going to check entities that dont yet have loaded asset types
filesToLoad := m.NewComponentFilter().
Require(
&d2components.FilePath{}, // we want to process entities with these file components
&d2components.FileType{},
&d2components.FileHandle{},
).
Forbid(
&d2components.FileSource{}, // but we forbid files that are already loaded
&d2components.GameConfig{},
&d2components.StringTable{},
&d2components.DataDictionary{},
&d2components.Palette{},
&d2components.PaletteTransform{},
&d2components.Cof{},
&d2components.Dc6{},
&d2components.Dcc{},
&d2components.Ds1{},
&d2components.Dt1{},
&d2components.Wav{},
&d2components.AnimationData{},
).
Build()
fileSources := m.NewComponentFilter().
Require(&d2components.FileSource{}).
Build()
m.fileSub = m.AddSubscription(filesToLoad)
m.sourceSub = m.AddSubscription(fileSources)
}
func (m *AssetLoaderSystem) setupFactories() {
m.Info("setting up component factories")
filePathID := m.RegisterComponent(&d2components.FilePath{})
fileTypeID := m.RegisterComponent(&d2components.FileType{})
fileHandleID := m.RegisterComponent(&d2components.FileHandle{})
fileSourceID := m.RegisterComponent(&d2components.FileSource{})
stringTableID := m.RegisterComponent(&d2components.StringTable{})
fontTableID := m.RegisterComponent(&d2components.FontTable{})
dataDictionaryID := m.RegisterComponent(&d2components.DataDictionary{})
paletteID := m.RegisterComponent(&d2components.Palette{})
paletteTransformID := m.RegisterComponent(&d2components.PaletteTransform{})
cofID := m.RegisterComponent(&d2components.Cof{})
dc6ID := m.RegisterComponent(&d2components.Dc6{})
dccID := m.RegisterComponent(&d2components.Dcc{})
ds1ID := m.RegisterComponent(&d2components.Ds1{})
dt1ID := m.RegisterComponent(&d2components.Dt1{})
wavID := m.RegisterComponent(&d2components.Wav{})
animationDataID := m.RegisterComponent(&d2components.AnimationData{})
m.FilePath = m.GetComponentFactory(filePathID)
m.FileType = m.GetComponentFactory(fileTypeID)
m.FileHandle = m.GetComponentFactory(fileHandleID)
m.FileSource = m.GetComponentFactory(fileSourceID)
m.StringTable = m.GetComponentFactory(stringTableID)
m.DataDictionary = m.GetComponentFactory(dataDictionaryID)
m.Palette = m.GetComponentFactory(paletteID)
m.PaletteTransform = m.GetComponentFactory(paletteTransformID)
m.FontTable = m.GetComponentFactory(fontTableID)
m.Cof = m.GetComponentFactory(cofID)
m.Dc6 = m.GetComponentFactory(dc6ID)
m.Dcc = m.GetComponentFactory(dccID)
m.Ds1 = m.GetComponentFactory(ds1ID)
m.Dt1 = m.GetComponentFactory(dt1ID)
m.Wav = m.GetComponentFactory(wavID)
m.AnimationData = m.GetComponentFactory(animationDataID)
}
// Update processes all of the Entities in the subscription of file entities that need to be processed
@ -190,7 +218,7 @@ func (m *AssetLoaderSystem) assignFromCache(id akara.EID, path string, t d2enum.
case d2enum.FileTypePalette:
m.AddPalette(id).Palette = entry.(d2interface.Palette)
case d2enum.FileTypePaletteTransform:
m.AddPaletteTransform(id).Transform = entry.(*d2pl2.PL2)
m.AddPaletteTransform(id).PL2 = entry.(*d2pl2.PL2)
case d2enum.FileTypeCOF:
m.AddCof(id).COF = entry.(*d2cof.COF)
case d2enum.FileTypeDC6:
@ -204,7 +232,7 @@ func (m *AssetLoaderSystem) assignFromCache(id akara.EID, path string, t d2enum.
case d2enum.FileTypeWAV:
m.AddWav(id).Data = entry.(d2interface.DataStream)
case d2enum.FileTypeD2:
m.AddAnimData(id).AnimationData = entry.(*d2animdata.AnimationData)
m.AddAnimationData(id).AnimationData = entry.(*d2animdata.AnimationData)
}
return found
@ -277,7 +305,7 @@ func (m *AssetLoaderSystem) parseAndCache(id akara.EID, path string, t d2enum.Fi
case d2enum.FileTypeD2:
m.Infof("Loading animation data: %s", path)
if err := m.loadAnimData(id, path, data); err != nil {
if err := m.loadAnimationData(id, path, data); err != nil {
m.Error(err.Error())
}
}
@ -327,7 +355,7 @@ func (m *AssetLoaderSystem) loadPalette(id akara.EID, path string, data []byte)
func (m *AssetLoaderSystem) loadPaletteTransform(id akara.EID, path string, data []byte) error {
loaded, err := d2pl2.Load(data)
if err == nil {
m.AddPaletteTransform(id).Transform = loaded
m.AddPaletteTransform(id).PL2 = loaded
if cacheErr := m.cache.Insert(path, loaded, assetCacheEntryWeight); cacheErr != nil {
m.Error(cacheErr.Error())
@ -411,10 +439,10 @@ func (m *AssetLoaderSystem) loadWAV(id akara.EID, path string, seeker io.ReadSee
}
}
func (m *AssetLoaderSystem) loadAnimData(id akara.EID, path string, data []byte) error {
func (m *AssetLoaderSystem) loadAnimationData(id akara.EID, path string, data []byte) error {
loaded, err := d2animdata.Load(data)
if err == nil {
m.AddAnimData(id).AnimationData = loaded
m.AddAnimationData(id).AnimationData = loaded
if cacheErr := m.cache.Insert(path, loaded, assetCacheEntryWeight); cacheErr != nil {
m.Error(cacheErr.Error())

View File

@ -30,69 +30,86 @@ const (
logPrefixFileHandleResolver = "File Handle Resolver"
)
// NewFileHandleResolver creates a new file handle resolver system
func NewFileHandleResolver() *FileHandleResolutionSystem {
// this filter is for entities that have a file path and file type but no file handle.
filesToSource := akara.NewFilter().
Require(d2components.FilePath).
Require(d2components.FileType).
Forbid(d2components.FileHandle).
Forbid(d2components.FileSource).
Build()
sourcesToUse := akara.NewFilter().
RequireOne(d2components.FileSource).
Build()
fhr := &FileHandleResolutionSystem{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(filesToSource, sourcesToUse),
cache: d2cache.CreateCache(fileHandleCacheBudget).(*d2cache.Cache),
Logger: d2util.NewLogger(),
}
fhr.SetPrefix(logPrefixFileHandleResolver)
return fhr
}
// FileHandleResolutionSystem is responsible for using file sources to resolve files.
// FileHandleResolver is responsible for using file sources to resolve files.
// File sources are checked in the order that the sources were added.
//
// A file source can be something like an MPQ archive or a file system directory on the host machine.
//
// A file handle is a primitive representation of a loaded file; something that has data
// in the form of a byte slice, but has not been parsed into a more meaningful struct, like a DC6 animation.
type FileHandleResolutionSystem struct {
*akara.BaseSubscriberSystem
type FileHandleResolver struct {
akara.BaseSubscriberSystem
*d2util.Logger
cache *d2cache.Cache
filesToLoad *akara.Subscription
sourcesToUse *akara.Subscription
filePaths *d2components.FilePathMap
fileTypes *d2components.FileTypeMap
fileSources *d2components.FileSourceMap
fileHandles *d2components.FileHandleMap
d2components.FilePathFactory
d2components.FileTypeFactory
d2components.FileSourceFactory
d2components.FileHandleFactory
}
// Init initializes the system with the given world
func (m *FileHandleResolutionSystem) Init(_ *akara.World) {
func (m *FileHandleResolver) Init(world *akara.World) {
m.World = world
m.cache = d2cache.CreateCache(fileHandleCacheBudget).(*d2cache.Cache)
m.setupLogger()
m.Info("initializing ...")
m.filesToLoad = m.Subscriptions[0]
m.sourcesToUse = m.Subscriptions[1]
m.setupSubscriptions()
m.setupFactories()
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.filePaths = m.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.fileTypes = m.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
m.fileHandles = m.InjectMap(d2components.FileHandle).(*d2components.FileHandleMap)
m.fileSources = m.InjectMap(d2components.FileSource).(*d2components.FileSourceMap)
m.Info("... initialization complete!")
}
func (m *FileHandleResolver) setupLogger() {
m.Logger = d2util.NewLogger()
m.SetPrefix(logPrefixFileHandleResolver)
}
func (m *FileHandleResolver) setupSubscriptions() {
m.Info("setting up component subscriptions")
// this filter is for entities that have a file path and file type but no file handle.
filesToLoad := m.NewComponentFilter().
Require(
&d2components.FilePath{},
&d2components.FileType{},
).
Forbid(
&d2components.FileHandle{},
&d2components.FileSource{},
).
Build()
sourcesToUse := m.NewComponentFilter().
Require(&d2components.FileSource{}).
Build()
m.filesToLoad = m.AddSubscription(filesToLoad)
m.sourcesToUse = m.AddSubscription(sourcesToUse)
}
func (m *FileHandleResolver) setupFactories() {
m.Info("setting up component factories")
filePathID := m.RegisterComponent(&d2components.FilePath{})
fileTypeID := m.RegisterComponent(&d2components.FileType{})
fileHandleID := m.RegisterComponent(&d2components.FileHandle{})
fileSourceID := m.RegisterComponent(&d2components.FileSource{})
m.FilePath = m.GetComponentFactory(filePathID)
m.FileType = m.GetComponentFactory(fileTypeID)
m.FileHandle = m.GetComponentFactory(fileHandleID)
m.FileSource = m.GetComponentFactory(fileSourceID)
}
// Update iterates over entities which have not had a file handle resolved.
// For each source, it attempts to load this file with the given source.
// If the file can be opened by the source, we create the file handle using that source.
func (m *FileHandleResolutionSystem) Update() {
func (m *FileHandleResolver) Update() {
filesToLoad := m.filesToLoad.GetEntities()
sourcesToUse := m.sourcesToUse.GetEntities()
@ -106,23 +123,23 @@ func (m *FileHandleResolutionSystem) Update() {
}
// try to load a file with a source, returns true if loaded
func (m *FileHandleResolutionSystem) loadFileWithSource(fileID, sourceID akara.EID) bool {
fp, found := m.filePaths.GetFilePath(fileID)
func (m *FileHandleResolver) loadFileWithSource(fileID, sourceID akara.EID) bool {
fp, found := m.GetFilePath(fileID)
if !found {
return false
}
ft, found := m.fileTypes.GetFileType(fileID)
ft, found := m.GetFileType(fileID)
if !found {
return false
}
source, found := m.fileSources.GetFileSource(sourceID)
source, found := m.GetFileSource(sourceID)
if !found {
return false
}
sourceFp, found := m.filePaths.GetFilePath(sourceID)
sourceFp, found := m.GetFilePath(sourceID)
if !found {
return false
}
@ -136,7 +153,7 @@ func (m *FileHandleResolutionSystem) loadFileWithSource(fileID, sourceID akara.E
cacheKey := m.makeCacheKey(fp.Path, sourceFp.Path)
if entry, found := m.cache.Retrieve(cacheKey); found {
component := m.fileHandles.AddFileHandle(fileID)
component := m.AddFileHandle(fileID)
component.Data = entry.(d2interface.DataStream)
return true
@ -155,11 +172,11 @@ func (m *FileHandleResolutionSystem) loadFileWithSource(fileID, sourceID akara.E
}
tryPath := strings.ReplaceAll(fp.Path, "sfx", "music")
tmpComponent := &d2components.FilePathComponent{Path: tryPath}
tmpComponent := &d2components.FilePath{Path: tryPath}
cacheKey = m.makeCacheKey(tryPath, sourceFp.Path)
if entry, found := m.cache.Retrieve(cacheKey); found {
component := m.fileHandles.AddFileHandle(fileID)
component := m.AddFileHandle(fileID)
component.Data = entry.(d2interface.DataStream)
fp.Path = tryPath
@ -176,7 +193,7 @@ func (m *FileHandleResolutionSystem) loadFileWithSource(fileID, sourceID akara.E
m.Infof("resolved `%s` with source `%s`", fp.Path, sourceFp.Path)
component := m.fileHandles.AddFileHandle(fileID)
component := m.AddFileHandle(fileID)
component.Data = data
if err := m.cache.Insert(cacheKey, data, fileHandleCacheEntryWeight); err != nil {
@ -186,7 +203,7 @@ func (m *FileHandleResolutionSystem) loadFileWithSource(fileID, sourceID akara.E
return true
}
func (m *FileHandleResolutionSystem) makeCacheKey(path, source string) string {
func (m *FileHandleResolver) makeCacheKey(path, source string) string {
const sep = "->"
return strings.Join([]string{source, path}, sep)
}

View File

@ -7,7 +7,6 @@ import (
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
)
func Test_FileHandleResolver_Process(t *testing.T) {
@ -15,34 +14,30 @@ func Test_FileHandleResolver_Process(t *testing.T) {
cfg := akara.NewWorldConfig()
fileTypeResolver := NewFileTypeResolver()
fileHandleResolver := NewFileHandleResolver()
fileSourceResolver := NewFileSourceResolver()
typeSys := &FileTypeResolver{}
handleSys := &FileHandleResolver{}
sourceSys := &FileSourceResolver{}
cfg.With(fileTypeResolver).
With(fileSourceResolver).
With(fileHandleResolver)
cfg.With(typeSys).
With(sourceSys).
With(handleSys)
world := akara.NewWorld(cfg)
filepathMap, err := world.GetMap(d2components.FilePath)
if err != nil {
t.Error("file path component map not found")
}
filePaths := filepathMap.(*d2components.FilePathMap)
filePaths := typeSys.FilePathFactory
fileHandles := handleSys.FileHandleFactory
sourceEntity := world.NewEntity()
sourceFp := filePaths.AddFilePath(sourceEntity)
sourceFp.Path = testDataPath
source := filePaths.AddFilePath(sourceEntity)
source.Path = testDataPath
fileEntity := world.NewEntity()
fileFp := filePaths.AddFilePath(fileEntity)
fileFp.Path = "testfile_a.txt"
file := filePaths.AddFilePath(fileEntity)
file.Path = "testfile_a.txt"
_ = world.Update(0)
ft, found := fileTypeResolver.GetFileType(sourceEntity)
ft, found := typeSys.GetFileType(sourceEntity)
if !found {
t.Error("file source type not created for entity")
return
@ -53,14 +48,6 @@ func Test_FileHandleResolver_Process(t *testing.T) {
return
}
handleMap, err := world.GetMap(d2components.FileHandle)
if err != nil {
t.Error("file handle component map is nil")
return
}
fileHandles := handleMap.(*d2components.FileHandleMap)
handle, found := fileHandles.GetFileHandle(fileEntity)
if !found {
t.Error("file handle for entity was not found")

View File

@ -19,48 +19,64 @@ const (
logPrefixFileSourceResolver = "File Source Resolver"
)
// NewFileSourceResolver creates a new file source resolver system
func NewFileSourceResolver() *FileSourceResolver {
// subscribe to entities with a file type and file path, but no file source type
filesToCheck := akara.NewFilter().
Require(d2components.FilePath).
Require(d2components.FileType).
Forbid(d2components.FileSource).
Build()
fsr := &FileSourceResolver{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(filesToCheck),
Logger: d2util.NewLogger(),
}
fsr.SetPrefix(logPrefixFileSourceResolver)
return fsr
}
// FileSourceResolver is responsible for determining if files can be used as a file source.
// If it can, it sets the file up as a source, and the file handle resolver system can
// then use the source to open files.
type FileSourceResolver struct {
*akara.BaseSubscriberSystem
akara.BaseSubscriberSystem
*d2util.Logger
fileSub *akara.Subscription
filePaths *d2components.FilePathMap
fileTypes *d2components.FileTypeMap
fileSources *d2components.FileSourceMap
filesToCheck *akara.Subscription
d2components.FilePathFactory
d2components.FileTypeFactory
d2components.FileSourceFactory
}
// Init initializes the file source resolver, injecting the necessary components into the world
func (m *FileSourceResolver) Init(_ *akara.World) {
func (m *FileSourceResolver) Init(world *akara.World) {
m.World = world
m.setupLogger()
m.Info("initializing ...")
m.fileSub = m.Subscriptions[0]
m.setupSubscriptions()
m.setupFactories()
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.filePaths = m.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.fileTypes = m.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
m.fileSources = m.InjectMap(d2components.FileSource).(*d2components.FileSourceMap)
m.Info("... initialization complete!")
}
func (m *FileSourceResolver) setupLogger() {
m.Logger = d2util.NewLogger()
m.SetPrefix(logPrefixFileSourceResolver)
}
func (m *FileSourceResolver) setupSubscriptions() {
m.Info("setting up component subscriptions")
// subscribe to entities with a file type and file path, but no file source type
filesToCheck := m.NewComponentFilter().
Require(
&d2components.FilePath{},
&d2components.FileType{},
).
Forbid(
&d2components.FileSource{},
).
Build()
m.filesToCheck = m.AddSubscription(filesToCheck)
}
func (m *FileSourceResolver) setupFactories() {
m.Info("setting up component factories")
filePathID := m.RegisterComponent(&d2components.FilePath{})
fileTypeID := m.RegisterComponent(&d2components.FileType{})
fileSourceID := m.RegisterComponent(&d2components.FileSource{})
m.FilePath = m.GetComponentFactory(filePathID)
m.FileType = m.GetComponentFactory(fileTypeID)
m.FileSource = m.GetComponentFactory(fileSourceID)
}
// Update iterates over entities from its subscription, and checks if it can be used as a file source
@ -73,12 +89,12 @@ func (m *FileSourceResolver) Update() {
}
func (m *FileSourceResolver) processSourceEntity(id akara.EID) {
fp, found := m.filePaths.GetFilePath(id)
fp, found := m.GetFilePath(id)
if !found {
return
}
ft, found := m.fileTypes.GetFileType(id)
ft, found := m.GetFileType(id)
if !found {
return
}
@ -95,16 +111,11 @@ func (m *FileSourceResolver) processSourceEntity(id akara.EID) {
break
}
source := m.fileSources.AddFileSource(id)
source.AbstractSource = instance
m.AddFileSource(id).AbstractSource = instance
m.Infof("using MPQ source for `%s`", fp.Path)
case d2enum.FileTypeDirectory:
instance := m.makeFileSystemSource(fp.Path)
source := m.fileSources.AddFileSource(id)
source.AbstractSource = instance
m.AddFileSource(id).AbstractSource = m.makeFileSystemSource(fp.Path)
m.Infof("using FILESYSTEM source for `%s`", fp.Path)
}
}
@ -118,7 +129,7 @@ type fsSource struct {
rootDir string
}
func (s *fsSource) Open(path *d2components.FilePathComponent) (d2interface.DataStream, error) {
func (s *fsSource) Open(path *d2components.FilePath) (d2interface.DataStream, error) {
fileData, err := os.Open(s.fullPath(path.Path))
if err != nil {
return nil, err
@ -149,7 +160,7 @@ type mpqSource struct {
mpq d2interface.Archive
}
func (s *mpqSource) Open(path *d2components.FilePathComponent) (d2interface.DataStream, error) {
func (s *mpqSource) Open(path *d2components.FilePath) (d2interface.DataStream, error) {
fileData, err := s.mpq.ReadFileStream(s.cleanMpqPath(path.Path))
if err != nil {
return nil, err

View File

@ -6,7 +6,6 @@ import (
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
)
func Test_FileSourceResolution(t *testing.T) {
@ -14,20 +13,16 @@ func Test_FileSourceResolution(t *testing.T) {
cfg := akara.NewWorldConfig()
srcResolver := NewFileSourceResolver()
fileTypeResolver := NewFileTypeResolver()
sourceSys := &FileSourceResolver{}
typeSys := &FileTypeResolver{}
cfg.With(fileTypeResolver).
With(srcResolver)
cfg.With(typeSys).
With(sourceSys)
world := akara.NewWorld(cfg)
filepathMap, err := world.GetMap(d2components.FilePath)
if err != nil {
t.Error("file path component map not found")
}
filePaths := filepathMap.(*d2components.FilePathMap)
filePaths := typeSys.FilePathFactory
fileSources := sourceSys.FileSourceFactory
sourceEntity := world.NewEntity()
sourceFp := filePaths.AddFilePath(sourceEntity)
@ -35,7 +30,7 @@ func Test_FileSourceResolution(t *testing.T) {
_ = world.Update(0)
ft, found := fileTypeResolver.GetFileType(sourceEntity)
ft, found := typeSys.GetFileType(sourceEntity)
if !found {
t.Error("file source type not created for entity")
}
@ -44,7 +39,7 @@ func Test_FileSourceResolution(t *testing.T) {
t.Error("expected file system source type for entity")
}
fs, found := srcResolver.fileSources.GetFileSource(sourceEntity)
fs, found := fileSources.GetFileSource(sourceEntity)
if !found {
t.Error("file source not created for entity")
}

View File

@ -18,25 +18,6 @@ const (
logPrefixFileTypeResolver = "File Type Resolver"
)
// NewFileTypeResolver creates a new file type resolution system.
func NewFileTypeResolver() *FileTypeResolver {
// we subscribe only to entities that have a filepath
// and have not yet been given a file type
filesToCheck := akara.NewFilter().
Require(d2components.FilePath).
Forbid(d2components.FileType).
Build()
ftr := &FileTypeResolver{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(filesToCheck),
Logger: d2util.NewLogger(),
}
ftr.SetPrefix(logPrefixFileTypeResolver)
return ftr
}
// static check that FileTypeResolver implements the System interface
var _ akara.System = &FileTypeResolver{}
@ -46,23 +27,47 @@ var _ akara.System = &FileTypeResolver{}
// and it will then create the file type component for the entity, thus removing the entity
// from its subscription.
type FileTypeResolver struct {
*akara.BaseSubscriberSystem
akara.BaseSubscriberSystem
*d2util.Logger
filesToCheck *akara.Subscription
*d2components.FilePathMap
*d2components.FileTypeMap
d2components.FilePathFactory
d2components.FileTypeFactory
}
// Init initializes the system with the given world
func (m *FileTypeResolver) Init(_ *akara.World) {
func (m *FileTypeResolver) Init(world *akara.World) {
m.World = world
m.setupLogger()
m.Info("initializing ...")
m.filesToCheck = m.Subscriptions[0]
m.setupFactories()
m.setupSubscriptions()
}
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.FilePathMap = m.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.FileTypeMap = m.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
func (m *FileTypeResolver) setupLogger() {
m.Logger = d2util.NewLogger()
m.SetPrefix(logPrefixFileTypeResolver)
}
func (m *FileTypeResolver) setupFactories() {
filePathID := m.RegisterComponent(&d2components.FilePath{})
fileTypeID := m.RegisterComponent(&d2components.FileType{})
m.FilePath = m.GetComponentFactory(filePathID)
m.FileType = m.GetComponentFactory(fileTypeID)
}
func (m *FileTypeResolver) setupSubscriptions() {
// we subscribe only to entities that have a filepath
// and have not yet been given a file type
filesToCheck := m.NewComponentFilter().
Require(&d2components.FilePath{}).
Forbid(&d2components.FileType{}).
Build()
m.filesToCheck = m.AddSubscription(filesToCheck)
}
// Update processes all of the Entities

View File

@ -9,30 +9,23 @@ import (
)
func TestNewFileTypeResolver_KnownType(t *testing.T) {
cfg := akara.NewWorldConfig()
resolver := NewFileTypeResolver()
cfg.With(resolver)
world := akara.NewWorld(cfg)
typeSys := &FileTypeResolver{}
world := akara.NewWorld(akara.NewWorldConfig().With(typeSys))
e := world.NewEntity()
typeSys.AddFilePath(e).Path = "/some/path/to/a/file.dcc"
fp := resolver.AddFilePath(e)
fp.Path = "/some/path/to/a/file.dcc"
if len(resolver.Subscriptions[0].GetEntities()) != 1 {
t.Error("entity with file path not added to file type resolver subscription")
if len(typeSys.filesToCheck.GetEntities()) != 1 {
t.Error("entity with file path not added to file type typeSys subscription")
}
_ = world.Update(0)
if len(resolver.Subscriptions[0].GetEntities()) != 0 {
t.Error("entity with existing file type not removed from file type resolver subscription")
if len(typeSys.filesToCheck.GetEntities()) != 0 {
t.Error("entity with existing file type not removed from file type typeSys subscription")
}
ft, found := resolver.GetFileType(e)
ft, found := typeSys.GetFileType(e)
if !found {
t.Error("file type component not added to entity with file path component")
}
@ -43,22 +36,17 @@ func TestNewFileTypeResolver_KnownType(t *testing.T) {
}
func TestNewFileTypeResolver_UnknownType(t *testing.T) {
cfg := akara.NewWorldConfig()
resolver := NewFileTypeResolver()
cfg.With(resolver)
world := akara.NewWorld(cfg)
typeSys := &FileTypeResolver{}
world := akara.NewWorld(akara.NewWorldConfig().With(typeSys))
e := world.NewEntity()
fp := resolver.AddFilePath(e)
fp := typeSys.AddFilePath(e)
fp.Path = "/some/path/to/a/file.XYZ"
_ = world.Update(0)
ft, _ := resolver.GetFileType(e)
ft, _ := typeSys.GetFileType(e)
if ft.Type != d2enum.FileTypeUnknown {
t.Error("unexpected file type")

View File

@ -4,7 +4,6 @@ import (
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2util"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
)
const (
@ -14,53 +13,51 @@ const (
// static check that the game config system implements the system interface
var _ akara.System = &GameClientBootstrapSystem{}
// NewGameClientBootstrapSystem makes a new client bootstrap system
func NewGameClientBootstrapSystem() *GameClientBootstrapSystem {
// we are interested in actual game config instances, too
gameConfigs := akara.NewFilter().Require(d2components.GameConfig).Build()
sys := &GameClientBootstrapSystem{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(gameConfigs),
Logger: d2util.NewLogger(),
}
sys.SetPrefix(logPrefixGameClientBootstrap)
return sys
}
// GameClientBootstrapSystem is responsible for setting up other
// systems that are common to both the game client and the headless game server
type GameClientBootstrapSystem struct {
*akara.BaseSubscriberSystem
akara.BaseSubscriberSystem
*d2util.Logger
*RenderSystem
}
// Init injects the common systems required by both the game client and headless server
func (m *GameClientBootstrapSystem) Init(_ *akara.World) {
func (m *GameClientBootstrapSystem) Init(world *akara.World) {
m.World = world
m.setupLogger()
m.Info("initializing ...")
m.injectSystems()
m.Info("game client bootstrap complete, deactivating")
m.SetActive(false)
m.Info("initialization complete")
if err := m.World.Update(0); err != nil {
m.Error(err.Error())
}
}
func (m *GameClientBootstrapSystem) injectSystems() {
m.RenderSystem = NewRenderSystem()
func (m *GameClientBootstrapSystem) setupLogger() {
m.Logger = d2util.NewLogger()
m.SetPrefix(logPrefixGameClientBootstrap)
}
m.World.AddSystem(m.RenderSystem)
m.World.AddSystem(NewUpdateCounterSystem())
m.World.AddSystem(NewLoadingScene())
m.World.AddSystem(NewMainMenuScene())
func (m *GameClientBootstrapSystem) injectSystems() {
m.Info("injecting render system")
m.AddSystem(&RenderSystem{})
m.Info("injecting update counter system")
m.AddSystem(&UpdateCounter{})
m.Info("injecting loading scene")
m.AddSystem(NewLoadingScene())
m.Info("injecting main menu scene")
m.AddSystem(NewMainMenuScene())
}
// Update does nothing, but exists to satisfy the `akara.System` interface
func (m *GameClientBootstrapSystem) Update() {
// nothing to do after init ...
m.Info("game client bootstrap complete, deactivating")
m.RemoveSystem(m)
}

View File

@ -18,42 +18,6 @@ const (
loggerPrefixGameConfig = "Game Config"
)
// NewGameConfigSystem creates a new game config system
func NewGameConfigSystem() *GameConfigSystem {
// we are going to check entities that dont yet have loaded asset types
filesToCheck := akara.NewFilter().
Require(d2components.FilePath).
Require(d2components.FileType).
Require(d2components.FileHandle).
Forbid(d2components.GameConfig).
Forbid(d2components.StringTable).
Forbid(d2components.DataDictionary).
Forbid(d2components.Palette).
Forbid(d2components.PaletteTransform).
Forbid(d2components.Cof).
Forbid(d2components.Dc6).
Forbid(d2components.Dcc).
Forbid(d2components.Ds1).
Forbid(d2components.Dt1).
Forbid(d2components.Wav).
Forbid(d2components.AnimData).
Build()
// we are interested in actual game config instances, too
gameConfigs := akara.NewFilter().
Require(d2components.GameConfig).
Build()
gcs := &GameConfigSystem{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(filesToCheck, gameConfigs),
Logger: d2util.NewLogger(),
}
gcs.SetPrefix(loggerPrefixGameConfig)
return gcs
}
// GameConfigSystem is responsible for game config configFileBootstrap procedure, as well as
// clearing the `Dirty` component of game configs. In the `configFileBootstrap` method of this system
// you can see that this system will add entities for the directories it expects config files
@ -65,34 +29,87 @@ func NewGameConfigSystem() *GameConfigSystem {
// other systems are not present in the world, but no config files will be loaded by
// this system either...
type GameConfigSystem struct {
*akara.BaseSubscriberSystem
akara.BaseSubscriberSystem
*d2util.Logger
filesToCheck *akara.Subscription
gameConfigs *akara.Subscription
*d2components.GameConfigMap
*d2components.FilePathMap
*d2components.FileTypeMap
*d2components.FileHandleMap
*d2components.FileSourceMap
*d2components.DirtyMap
ActiveConfig *d2components.GameConfigComponent
d2components.GameConfigFactory
d2components.FilePathFactory
d2components.FileTypeFactory
d2components.FileHandleFactory
d2components.FileSourceFactory
d2components.DirtyFactory
activeConfig *d2components.GameConfig
}
// Init the world with the necessary components related to game config files
func (m *GameConfigSystem) Init(world *akara.World) {
m.World = world
m.setupLogger()
m.Info("initializing ...")
m.filesToCheck = m.Subscriptions[0]
m.gameConfigs = m.Subscriptions[1]
m.setupFactories()
m.setupSubscriptions()
}
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.FilePathMap = world.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
m.FileTypeMap = world.InjectMap(d2components.FileType).(*d2components.FileTypeMap)
m.FileHandleMap = world.InjectMap(d2components.FileHandle).(*d2components.FileHandleMap)
m.FileSourceMap = world.InjectMap(d2components.FileSource).(*d2components.FileSourceMap)
m.GameConfigMap = world.InjectMap(d2components.GameConfig).(*d2components.GameConfigMap)
m.DirtyMap = world.InjectMap(d2components.Dirty).(*d2components.DirtyMap)
func (m *GameConfigSystem) setupLogger() {
m.Logger = d2util.NewLogger()
m.SetPrefix(loggerPrefixGameConfig)
}
func (m *GameConfigSystem) setupFactories() {
m.Info("setting up component factories")
filePathID := m.RegisterComponent(&d2components.FilePath{})
fileTypeID := m.RegisterComponent(&d2components.FileType{})
fileHandleID := m.RegisterComponent(&d2components.FileHandle{})
fileSourceID := m.RegisterComponent(&d2components.FileSource{})
gameConfigID := m.RegisterComponent(&d2components.GameConfig{})
dirtyID := m.RegisterComponent(&d2components.Dirty{})
m.FilePath = m.GetComponentFactory(filePathID)
m.FileType = m.GetComponentFactory(fileTypeID)
m.FileHandle = m.GetComponentFactory(fileHandleID)
m.FileSource = m.GetComponentFactory(fileSourceID)
m.GameConfig = m.GetComponentFactory(gameConfigID)
m.Dirty = m.GetComponentFactory(dirtyID)
}
func (m *GameConfigSystem) setupSubscriptions() {
m.Info("setting up component subscriptions")
// we are going to check entities that dont yet have loaded asset types
filesToCheck := m.NewComponentFilter().
Require(
&d2components.FilePath{},
&d2components.FileType{},
&d2components.FileHandle{},
).
Forbid(
&d2components.GameConfig{},
&d2components.StringTable{},
&d2components.DataDictionary{},
&d2components.Palette{},
&d2components.PaletteTransform{},
&d2components.Cof{},
&d2components.Dc6{},
&d2components.Dcc{},
&d2components.Ds1{},
&d2components.Dt1{},
&d2components.Wav{},
&d2components.AnimationData{},
).
Build()
// we are interested in actual game config instances, too
gameConfigs := m.NewComponentFilter().
Require(&d2components.GameConfig{}).
Build()
m.filesToCheck = m.AddSubscription(filesToCheck)
m.gameConfigs = m.AddSubscription(gameConfigs)
}
// Update checks for new config files
@ -130,8 +147,8 @@ func (m *GameConfigSystem) loadConfig(eid akara.EID) {
gameConfig := m.AddGameConfig(eid)
if err := json.NewDecoder(fh.Data).Decode(gameConfig); err != nil {
m.GameConfigMap.Remove(eid)
m.RemoveEntity(eid)
}
m.ActiveConfig = gameConfig
m.activeConfig = gameConfig
}

View File

@ -3,8 +3,6 @@ package d2systems
import (
"testing"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
"github.com/gravestench/akara"
)
@ -13,10 +11,10 @@ func Test_GameConfigSystem_Bootstrap(t *testing.T) {
cfg := akara.NewWorldConfig()
typeSys := NewFileTypeResolver()
handleSys := NewFileHandleResolver()
srcSys := NewFileSourceResolver()
cfgSys := NewGameConfigSystem()
typeSys := &FileTypeResolver{}
handleSys := &FileHandleResolver{}
srcSys := &FileSourceResolver{}
cfgSys := &GameConfigSystem{}
cfg.With(typeSys).
With(srcSys).
@ -25,21 +23,8 @@ func Test_GameConfigSystem_Bootstrap(t *testing.T) {
world := akara.NewWorld(cfg)
// for the purpose of this test, we want to add the testdata directory, so that
// when the game looks for the config file it gets pulled from there
filePathsAbstract, err := world.GetMap(d2components.FilePath)
if err != nil {
t.Error("file path component map not found")
return
}
filePaths := filePathsAbstract.(*d2components.FilePathMap)
cfgDir := filePaths.AddFilePath(world.NewEntity())
cfgDir.Path = testDataPath
cfgFile := filePaths.AddFilePath(world.NewEntity())
cfgFile.Path = "config.json"
cfgSys.AddFilePath(world.NewEntity()).Path = testDataPath
cfgSys.AddFilePath(world.NewEntity()).Path = "config.json"
// at this point the world has initialized the systems. when the world
// updates it should process the config dir to a source and then

View File

@ -10,36 +10,35 @@ const (
logPrefixGameObjectFactory = "Object Factory"
)
// NewGameObjectFactory creates a game object factory
func NewGameObjectFactory() *GameObjectFactory {
m := &GameObjectFactory{
BaseSystem: &akara.BaseSystem{},
Logger: d2util.NewLogger(),
}
m.SetPrefix(logPrefixGameObjectFactory)
return m
}
// static check that GameObjectFactory implements the System interface
var _ akara.System = &GameObjectFactory{}
// GameObjectFactory is a wrapper system for subordinate systems that
// do the actual object creation work.
type GameObjectFactory struct {
*akara.BaseSystem
akara.BaseSystem
*d2util.Logger
SpriteFactory *SpriteFactory
*SpriteFactory
}
// Init will initialize the Game Object Factory by injecting all of the factory subsystems into the world
func (t *GameObjectFactory) Init(world *akara.World) {
t.World = world
t.setupLogger()
t.Info("initializing ...")
t.injectSubSystems()
}
func (t *GameObjectFactory) setupLogger() {
t.Logger = d2util.NewLogger()
t.SetPrefix(logPrefixGameObjectFactory)
}
func (t *GameObjectFactory) injectSubSystems() {
t.Info("creating sprite factory")
t.SpriteFactory = NewSpriteFactorySubsystem(t.BaseSystem, t.Logger)
}

View File

@ -14,58 +14,51 @@ const (
logPrefixMovementSystem = "Movement System"
)
// NewMovementSystem creates a movement system
func NewMovementSystem() *MovementSystem {
cfg := akara.NewFilter().Require(d2components.Position, d2components.Velocity)
filter := cfg.Build()
sys := &MovementSystem{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(filter),
Logger: d2util.NewLogger(),
}
sys.SetPrefix(logPrefixMovementSystem)
return sys
}
// static check that MovementSystem implements the System interface
var _ akara.System = &MovementSystem{}
// MovementSystem handles entity movement based on velocity and position components
type MovementSystem struct {
*akara.BaseSubscriberSystem
akara.BaseSubscriberSystem
*d2util.Logger
*d2components.PositionMap
*d2components.VelocityMap
d2components.PositionFactory
d2components.VelocityFactory
movableEntities *akara.Subscription
}
// Init initializes the system with the given world
func (m *MovementSystem) Init(_ *akara.World) {
func (m *MovementSystem) Init(world *akara.World) {
m.World = world
m.Logger = d2util.NewLogger()
m.SetPrefix(logPrefixMovementSystem)
m.Info("initializing ...")
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.PositionMap = m.InjectMap(d2components.Position).(*d2components.PositionMap)
m.VelocityMap = m.InjectMap(d2components.Velocity).(*d2components.VelocityMap)
positionID := m.RegisterComponent(&d2components.Position{})
velocityID := m.RegisterComponent(&d2components.Velocity{})
m.Position = m.GetComponentFactory(positionID)
m.Velocity = m.GetComponentFactory(velocityID)
movable := m.NewComponentFilter().Require(
&d2components.Position{},
&d2components.Velocity{},
).Build()
m.movableEntities = m.AddSubscription(movable)
}
// Update positions of all entities with their velocities
func (m *MovementSystem) Update() {
for subIdx := range m.Subscriptions {
entities := m.Subscriptions[subIdx].GetEntities()
entities := m.movableEntities.GetEntities()
m.Infof("Processing movement for %d entities ...", len(entities))
for entIdx := range entities {
m.ProcessEntity(entities[entIdx])
}
for entIdx := range entities {
m.move(entities[entIdx])
}
}
// ProcessEntity updates an individual entity in the movement system
func (m *MovementSystem) ProcessEntity(id akara.EID) {
func (m *MovementSystem) move(id akara.EID) {
position, found := m.GetPosition(id)
if !found {
return
@ -77,5 +70,5 @@ func (m *MovementSystem) ProcessEntity(id akara.EID) {
}
s := float64(m.World.TimeDelta) / float64(time.Second)
position.Vector = *position.Vector.Add(velocity.Vector.Clone().Scale(s))
position.Vector.Add(velocity.Vector.Clone().Scale(s))
}

View File

@ -7,14 +7,12 @@ import (
"time"
"github.com/gravestench/akara"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2components"
)
func TestMovementSystem_Init(t *testing.T) {
cfg := akara.NewWorldConfig()
cfg.With(NewMovementSystem())
cfg.With(&MovementSystem{})
world := akara.NewWorld(cfg)
@ -24,7 +22,7 @@ func TestMovementSystem_Init(t *testing.T) {
}
func TestMovementSystem_Active(t *testing.T) {
sys := NewMovementSystem()
sys := &MovementSystem{}
if sys.Active() {
t.Error("system should not be active at creation")
@ -32,7 +30,7 @@ func TestMovementSystem_Active(t *testing.T) {
}
func TestMovementSystem_SetActive(t *testing.T) {
sys := NewMovementSystem()
sys := &MovementSystem{}
sys.SetActive(false)
@ -42,20 +40,13 @@ func TestMovementSystem_SetActive(t *testing.T) {
}
func TestMovementSystem_EntityAdded(t *testing.T) {
cfg := akara.NewWorldConfig()
sys := NewMovementSystem()
cfg.With(sys).
With(d2components.Position).
With(d2components.Velocity)
moveSys := &MovementSystem{}
cfg := akara.NewWorldConfig().With(moveSys)
world := akara.NewWorld(cfg)
e := world.NewEntity()
position := sys.AddPosition(e)
velocity := sys.AddVelocity(e)
position := moveSys.AddPosition(e)
velocity := moveSys.AddVelocity(e)
px, py := 10., 10.
vx, vy := 1., 0.
@ -63,18 +54,18 @@ func TestMovementSystem_EntityAdded(t *testing.T) {
position.Set(px, py)
velocity.Set(vx, vy)
if len(sys.Subscriptions[0].GetEntities()) != 1 {
if len(moveSys.movableEntities.GetEntities()) != 1 {
t.Error("entity not added to the system")
}
if p, found := sys.GetPosition(e); !found {
if p, found := moveSys.GetPosition(e); !found {
t.Error("position component not found")
} else if p.X() != px || p.Y() != py {
fmtError := "position component values incorrect:\n\t expected %v, %v but got %v, %v"
t.Errorf(fmtError, px, py, p.X(), p.Y())
}
if v, found := sys.GetVelocity(e); !found {
if v, found := moveSys.GetVelocity(e); !found {
t.Error("position component not found")
} else if v.X() != vx || v.Y() != vy {
fmtError := "velocity component values incorrect:\n\t expected %v, %v but got %v, %v"
@ -86,12 +77,9 @@ func TestMovementSystem_Update(t *testing.T) {
// world configFileBootstrap
cfg := akara.NewWorldConfig()
movementSystem := NewMovementSystem()
positions := d2components.Position.NewMap()
velocities := d2components.Velocity.NewMap()
cfg.With(movementSystem).With(positions).With(velocities)
movementSystem := &MovementSystem{}
cfg.With(movementSystem)
world := akara.NewWorld(cfg)
// lets make an entity and add some components to it
@ -118,7 +106,7 @@ func TestMovementSystem_Update(t *testing.T) {
func benchN(n int, b *testing.B) {
cfg := akara.NewWorldConfig()
movementSystem := NewMovementSystem()
movementSystem := &MovementSystem{}
cfg.With(movementSystem)

View File

@ -21,60 +21,71 @@ const (
logPrefixRenderSystem = "Render System"
)
// NewRenderSystem creates a new render system
func NewRenderSystem() *RenderSystem {
viewports := akara.NewFilter().
Require(d2components.Viewport).
Require(d2components.MainViewport).
Require(d2components.Renderable).
Build()
gameConfigs := akara.NewFilter().Require(d2components.GameConfig).Build()
r := &RenderSystem{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(viewports, gameConfigs),
Logger: d2util.NewLogger(),
}
r.SetPrefix(logPrefixRenderSystem)
return r
}
// static check that RenderSystem implements the System interface
var _ akara.System = &RenderSystem{}
// RenderSystem is responsible for rendering the main viewports of scenes
// to the game screen.
type RenderSystem struct {
*akara.BaseSubscriberSystem
akara.BaseSubscriberSystem
*d2util.Logger
renderer d2interface.Renderer
viewports *akara.Subscription
configs *akara.Subscription
*d2components.GameConfigMap
*d2components.ViewportMap
*d2components.MainViewportMap
*d2components.RenderableMap
d2components.GameConfigFactory
d2components.ViewportFactory
d2components.MainViewportFactory
d2components.RenderableFactory
lastUpdate time.Time
gameLoopInitDelay time.Duration // there is a race condition, this is a hack
}
// Init initializes the system with the given world, injecting the necessary components
func (m *RenderSystem) Init(_ *akara.World) {
func (m *RenderSystem) Init(world *akara.World) {
m.World = world
m.setupLogger()
m.Info("initializing ...")
m.setupFactories()
m.setupSubscriptions()
m.gameLoopInitDelay = time.Millisecond
}
m.viewports = m.Subscriptions[0]
m.configs = m.Subscriptions[1]
func (m *RenderSystem) setupLogger() {
m.Logger = d2util.NewLogger()
m.SetPrefix(logPrefixRenderSystem)
}
// try to inject the components we require, then cast the returned
// abstract ComponentMap back to the concrete implementation
m.GameConfigMap = m.InjectMap(d2components.GameConfig).(*d2components.GameConfigMap)
m.ViewportMap = m.InjectMap(d2components.Viewport).(*d2components.ViewportMap)
m.MainViewportMap = m.InjectMap(d2components.MainViewport).(*d2components.MainViewportMap)
m.RenderableMap = m.InjectMap(d2components.Renderable).(*d2components.RenderableMap)
func (m *RenderSystem) setupFactories() {
gameConfigID := m.RegisterComponent(&d2components.GameConfig{})
viewportID := m.RegisterComponent(&d2components.Viewport{})
mainViewportID := m.RegisterComponent(&d2components.MainViewport{})
renderableID := m.RegisterComponent(&d2components.Renderable{})
m.GameConfig = m.GetComponentFactory(gameConfigID)
m.Viewport = m.GetComponentFactory(viewportID)
m.MainViewport = m.GetComponentFactory(mainViewportID)
m.Renderable = m.GetComponentFactory(renderableID)
}
func (m *RenderSystem) setupSubscriptions() {
viewports := m.NewComponentFilter().
Require(
&d2components.Viewport{},
&d2components.MainViewport{},
&d2components.Renderable{},
).
Build()
gameConfigs := m.NewComponentFilter().
Require(&d2components.GameConfig{}).
Build()
m.viewports = m.AddSubscription(viewports)
m.configs = m.AddSubscription(gameConfigs)
}
// Update will initialize the renderer, start the game loop, and

View File

@ -55,16 +55,16 @@ type BaseScene struct {
Add *sceneObjectFactory
Viewports []akara.EID
GameObjects []akara.EID
*d2components.ViewportMap
*d2components.MainViewportMap
*d2components.ViewportFilterMap
*d2components.CameraMap
*d2components.RenderableMap
*d2components.PositionMap
*d2components.ScaleMap
*d2components.AnimationMap
*d2components.OriginMap
*d2components.AlphaMap
d2components.ViewportFactory
d2components.MainViewportFactory
d2components.ViewportFilterFactory
d2components.CameraFactory
d2components.RenderableFactory
d2components.PositionFactory
d2components.ScaleFactory
d2components.AnimationFactory
d2components.OriginFactory
d2components.AlphaFactory
}
// Booted returns whether or not the scene has booted
@ -87,9 +87,9 @@ func (s *BaseScene) Init(world *akara.World) {
}
func (s *BaseScene) boot() {
s.Info("booting ...")
s.Info("base scene booting ...")
s.injectComponentMaps()
s.setupFactories()
s.Add = &sceneObjectFactory{
BaseScene: s,
@ -129,21 +129,34 @@ func (s *BaseScene) boot() {
s.createDefaultViewport()
s.Info("booted!")
s.Info("base scene booted!")
s.booted = true
}
func (s *BaseScene) injectComponentMaps() {
s.MainViewportMap = s.World.InjectMap(d2components.MainViewport).(*d2components.MainViewportMap)
s.ViewportMap = s.World.InjectMap(d2components.Viewport).(*d2components.ViewportMap)
s.ViewportFilterMap = s.World.InjectMap(d2components.ViewportFilter).(*d2components.ViewportFilterMap)
s.CameraMap = s.World.InjectMap(d2components.Camera).(*d2components.CameraMap)
s.RenderableMap = s.World.InjectMap(d2components.Renderable).(*d2components.RenderableMap)
s.PositionMap = s.World.InjectMap(d2components.Position).(*d2components.PositionMap)
s.ScaleMap = s.World.InjectMap(d2components.Scale).(*d2components.ScaleMap)
s.AnimationMap = s.World.InjectMap(d2components.Animation).(*d2components.AnimationMap)
s.OriginMap = s.World.InjectMap(d2components.Origin).(*d2components.OriginMap)
s.AlphaMap = s.World.InjectMap(d2components.Alpha).(*d2components.AlphaMap)
func (s *BaseScene) setupFactories() {
s.Info("setting up component factories")
mainViewportID := s.RegisterComponent(&d2components.MainViewport{})
viewportID := s.RegisterComponent(&d2components.Viewport{})
viewportFilterID := s.RegisterComponent(&d2components.ViewportFilter{})
cameraID := s.RegisterComponent(&d2components.Camera{})
renderableID := s.RegisterComponent(&d2components.Renderable{})
positionID := s.RegisterComponent(&d2components.Position{})
scaleID := s.RegisterComponent(&d2components.Scale{})
animationID := s.RegisterComponent(&d2components.Animation{})
originID := s.RegisterComponent(&d2components.Origin{})
alphaID := s.RegisterComponent(&d2components.Alpha{})
s.MainViewport = s.GetComponentFactory(mainViewportID)
s.Viewport = s.GetComponentFactory(viewportID)
s.ViewportFilter = s.GetComponentFactory(viewportFilterID)
s.Camera = s.GetComponentFactory(cameraID)
s.Renderable = s.GetComponentFactory(renderableID)
s.Position = s.GetComponentFactory(positionID)
s.Scale = s.GetComponentFactory(scaleID)
s.Animation = s.GetComponentFactory(animationID)
s.Origin = s.GetComponentFactory(originID)
s.Alpha = s.GetComponentFactory(alphaID)
}
func (s *BaseScene) createDefaultViewport() {
@ -239,7 +252,7 @@ func (s *BaseScene) renderViewport(idx int, objects []akara.EID) {
if idx == mainViewport {
s.AddMainViewport(id)
} else {
s.MainViewportMap.Remove(id)
s.MainViewport.Remove(id)
}
camera, found := s.GetCamera(id)

View File

@ -12,6 +12,9 @@ const (
sceneKeyLoading = "Loading"
)
// static check that LoadingScene implements the scene interface
var _ d2interface.Scene = &LoadingScene{}
// NewLoadingScene creates a new main menu scene. This is the first screen that the user
// will see when launching the game.
func NewLoadingScene() *LoadingScene {
@ -22,9 +25,6 @@ func NewLoadingScene() *LoadingScene {
return scene
}
// static check that LoadingScene implements the scene interface
var _ d2interface.Scene = &LoadingScene{}
// LoadingScene represents the game's main menu, where users can select single or multi player,
// or start the map engine test.
type LoadingScene struct {
@ -34,34 +34,42 @@ type LoadingScene struct {
booted bool
}
// Init the main menu scene
func (s *LoadingScene) Init(_ *akara.World) {
s.Info("initializing ...")
func (s *LoadingScene) setupSubscriptions() {
s.Info("setting up component subscriptions")
s.setupSubscription()
}
func (s *LoadingScene) setupSubscription() {
filesToLoad := akara.NewFilter().
Require(d2components.FileHandle).
Forbid(d2components.FileSource). // but we forbid files that are already loaded
Forbid(d2components.GameConfig).
Forbid(d2components.StringTable).
Forbid(d2components.DataDictionary).
Forbid(d2components.Palette).
Forbid(d2components.PaletteTransform).
Forbid(d2components.Cof).
Forbid(d2components.Dc6).
Forbid(d2components.Dcc).
Forbid(d2components.Ds1).
Forbid(d2components.Dt1).
Forbid(d2components.Wav).
Forbid(d2components.AnimData).
filesToLoad := s.NewComponentFilter().
Require(
&d2components.FileHandle{},
).
Forbid( // but we forbid files that are already loaded
&d2components.FileSource{},
&d2components.GameConfig{},
&d2components.StringTable{},
&d2components.DataDictionary{},
&d2components.Palette{},
&d2components.PaletteTransform{},
&d2components.Cof{},
&d2components.Dc6{},
&d2components.Dcc{},
&d2components.Ds1{},
&d2components.Dt1{},
&d2components.Wav{},
&d2components.AnimationData{},
).
Build()
s.filesToLoad = s.World.AddSubscription(filesToLoad)
}
// Init the main menu scene
func (s *LoadingScene) Init(world *akara.World) {
s.World = world
s.Info("initializing ...")
s.setupSubscriptions()
}
func (s *LoadingScene) boot() {
if !s.BaseScene.booted {
return

View File

@ -32,7 +32,9 @@ type MainMenuScene struct {
}
// Init the main menu scene
func (s *MainMenuScene) Init(_ *akara.World) {
func (s *MainMenuScene) Init(world *akara.World) {
s.World = world
s.Info("initializing ...")
}

View File

@ -15,20 +15,9 @@ const (
// NewSpriteFactorySubsystem creates a new sprite factory which is intended
// to be embedded in the game object factory system.
func NewSpriteFactorySubsystem(b *akara.BaseSystem, l *d2util.Logger) *SpriteFactory {
spritesToRender := akara.NewFilter().
Require(d2components.Animation). // we want to process entities that have an animation ...
Forbid(d2components.Renderable). // ... but are missing a surface
Build()
spritesToUpdate := akara.NewFilter().
Require(d2components.Animation). // we want to process entities that have an animation ...
Require(d2components.Renderable). // ... but are missing a surface
Build()
func NewSpriteFactorySubsystem(b akara.BaseSystem, l *d2util.Logger) *SpriteFactory {
sys := &SpriteFactory{
BaseSubscriberSystem: akara.NewBaseSubscriberSystem(spritesToRender, spritesToUpdate),
Logger: l,
Logger: l,
}
sys.BaseSystem = b
@ -47,39 +36,67 @@ type spriteLoadQueue = map[akara.EID]spriteLoadQueueEntry
// SpriteFactory is responsible for queueing sprites to be loaded (as animations),
// as well as binding the animation to a renderer if one is present (which generates the sprite surfaces).
type SpriteFactory struct {
*akara.BaseSubscriberSystem
akara.BaseSubscriberSystem
*d2util.Logger
*RenderSystem
*d2components.FilePathMap
*d2components.PositionMap
*d2components.Dc6Map
*d2components.DccMap
*d2components.PaletteMap
*d2components.AnimationMap
*d2components.RenderableMap
*d2components.SegmentedSpriteMap
RenderSystem *RenderSystem
d2components.FilePathFactory
d2components.PositionFactory
d2components.Dc6Factory
d2components.DccFactory
d2components.PaletteFactory
d2components.AnimationFactory
d2components.RenderableFactory
d2components.SegmentedSpriteFactory
loadQueue spriteLoadQueue
spritesToRender *akara.Subscription
spritesToUpdate *akara.Subscription
}
// Init the sprite factory, injecting the necessary components
func (t *SpriteFactory) Init(_ *akara.World) {
func (t *SpriteFactory) Init(world *akara.World) {
t.World = world
t.Info("initializing sprite factory ...")
t.setupFactories()
t.setupSubscriptions()
t.loadQueue = make(spriteLoadQueue)
}
t.spritesToRender = t.Subscriptions[0]
t.spritesToUpdate = t.Subscriptions[1]
func (t *SpriteFactory) setupFactories() {
filePathID := t.RegisterComponent(&d2components.FilePath{})
positionID := t.RegisterComponent(&d2components.Position{})
dc6ID := t.RegisterComponent(&d2components.Dc6{})
dccID := t.RegisterComponent(&d2components.Dcc{})
paletteID := t.RegisterComponent(&d2components.Palette{})
animationID := t.RegisterComponent(&d2components.Animation{})
renderableID := t.RegisterComponent(&d2components.Renderable{})
segmentedSpriteID := t.RegisterComponent(&d2components.SegmentedSprite{})
t.FilePathMap = t.InjectMap(d2components.FilePath).(*d2components.FilePathMap)
t.PositionMap = t.InjectMap(d2components.Position).(*d2components.PositionMap)
t.Dc6Map = t.InjectMap(d2components.Dc6).(*d2components.Dc6Map)
t.DccMap = t.InjectMap(d2components.Dcc).(*d2components.DccMap)
t.PaletteMap = t.InjectMap(d2components.Palette).(*d2components.PaletteMap)
t.AnimationMap = t.InjectMap(d2components.Animation).(*d2components.AnimationMap)
t.RenderableMap = t.InjectMap(d2components.Renderable).(*d2components.RenderableMap)
t.SegmentedSpriteMap = t.InjectMap(d2components.SegmentedSprite).(*d2components.SegmentedSpriteMap)
t.FilePath = t.GetComponentFactory(filePathID)
t.Position = t.GetComponentFactory(positionID)
t.Dc6 = t.GetComponentFactory(dc6ID)
t.Dcc = t.GetComponentFactory(dccID)
t.Palette = t.GetComponentFactory(paletteID)
t.Animation = t.GetComponentFactory(animationID)
t.Renderable = t.GetComponentFactory(renderableID)
t.SegmentedSpriteFactory.SegmentedSprite = t.GetComponentFactory(segmentedSpriteID)
}
func (t *SpriteFactory) setupSubscriptions() {
spritesToRender := t.NewComponentFilter().
Require(&d2components.Animation{}). // we want to process entities that have an animation ...
Forbid(&d2components.Renderable{}). // ... but are missing a surface
Build()
spritesToUpdate := t.NewComponentFilter().
Require(&d2components.Animation{}). // we want to process entities that have an animation ...
Require(&d2components.Renderable{}). // ... but are missing a surface
Build()
t.spritesToRender = t.AddSubscription(spritesToRender)
t.spritesToUpdate = t.AddSubscription(spritesToUpdate)
}
// Update processes the load queue which attempting to create animations, as well as
@ -191,7 +208,7 @@ func (t *SpriteFactory) tryRenderingSprite(eid akara.EID) {
return
}
anim.BindRenderer(t.renderer)
anim.BindRenderer(t.RenderSystem.renderer)
sfc := anim.GetCurrentFrameSurface()
@ -225,15 +242,15 @@ func (t *SpriteFactory) updateSprite(eid akara.EID) {
}
func (t *SpriteFactory) createDc6Animation(
dc6 *d2components.Dc6Component,
pal *d2components.PaletteComponent,
dc6 *d2components.Dc6,
pal *d2components.Palette,
) (d2interface.Animation, error) {
return d2animation.NewDC6Animation(dc6.DC6, pal.Palette, 0)
}
func (t *SpriteFactory) createDccAnimation(
dcc *d2components.DccComponent,
pal *d2components.PaletteComponent,
dcc *d2components.Dcc,
pal *d2components.Palette,
) (d2interface.Animation, error) {
return d2animation.NewDCCAnimation(dcc.DCC, pal.Palette, 0)
}

View File

@ -10,23 +10,11 @@ const (
logPrefixUpdateCounter = "Update Counter"
)
// NewUpdateCounterSystem creates a new update counter system
func NewUpdateCounterSystem() *UpdateCounter {
uc := &UpdateCounter{
BaseSystem: &akara.BaseSystem{},
Logger: d2util.NewLogger(),
}
uc.SetPrefix(logPrefixUpdateCounter)
return uc
}
var _ akara.System = &UpdateCounter{}
// UpdateCounter is a utility system that logs the number of updates per second
type UpdateCounter struct {
*akara.BaseSystem
akara.BaseSystem
*d2util.Logger
secondsElapsed float64
count int
@ -36,6 +24,8 @@ type UpdateCounter struct {
func (u *UpdateCounter) Init(world *akara.World) {
u.World = world
u.setupLogger()
if u.World == nil {
u.SetActive(false)
}
@ -43,6 +33,11 @@ func (u *UpdateCounter) Init(world *akara.World) {
u.Info("initializing")
}
func (u *UpdateCounter) setupLogger() {
u.Logger = d2util.NewLogger()
u.SetPrefix(logPrefixUpdateCounter)
}
// Update the world update count in 1 second intervals
func (u *UpdateCounter) Update() {
u.count++

2
go.mod
View File

@ -9,7 +9,7 @@ require (
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20201108214237-06ea97f0c265 // indirect
github.com/go-restruct/restruct v1.2.0-alpha
github.com/google/uuid v1.1.2
github.com/gravestench/akara v0.0.0-20201122210148-a1ee8ea83994
github.com/gravestench/akara v0.0.0-20201128054238-892de9d70d6b
github.com/hajimehoshi/ebiten/v2 v2.0.0
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.5.0

2
go.sum
View File

@ -23,6 +23,8 @@ github.com/gravestench/akara v0.0.0-20201119221449-924b47999403 h1:hoCEhoSD+4Hvg
github.com/gravestench/akara v0.0.0-20201119221449-924b47999403/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
github.com/gravestench/akara v0.0.0-20201122210148-a1ee8ea83994 h1:Wp+4kZ0Pkap2ueAkTrE22rk++3VZE8TsU1bewpnzmsM=
github.com/gravestench/akara v0.0.0-20201122210148-a1ee8ea83994/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
github.com/gravestench/akara v0.0.0-20201128054238-892de9d70d6b h1:Ngfdn7O3wXQBzbOLsL6vQ9G4F7utUiKjQqKnwHbY5uI=
github.com/gravestench/akara v0.0.0-20201128054238-892de9d70d6b/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ=
github.com/hajimehoshi/bitmapfont/v2 v2.1.0/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs=
github.com/hajimehoshi/ebiten/v2 v2.0.0 h1:G8mhkKFtnDPPZ/ChaGWx4Bm0NusYEcafGCJ8QLxEaYs=
github.com/hajimehoshi/ebiten/v2 v2.0.0/go.mod h1:hpZZQ/kk8DZqft7QsQ5hZLRQXHSZPdKnaa0tcJ3CZFE=

View File

@ -26,8 +26,8 @@ func main() {
cfg := akara.NewWorldConfig()
cfg.
With(d2systems.NewAppBootstrapSystem()).
With(d2systems.NewGameClientBootstrapSystem())
With(&d2systems.AppBootstrapSystem{}).
With(&d2systems.GameClientBootstrapSystem{})
akara.NewWorld(cfg)