From 2e814f29b0650d42eb2657acbf457984d160ef90 Mon Sep 17 00:00:00 2001 From: gravestench Date: Sat, 5 Dec 2020 23:16:16 -0800 Subject: [PATCH] transform component, scene testing * removed position,scale,rotation components * added Transform component that contains position, rotation, and scale * scene graph update now regenerates the local mat4 using the transform component * akara bugfix: adding new subscriptions will process existing entities * added `--testscene` arg for testing individual scenes in isolation * added rotation support to d2interface.Surface --- d2app/app_ecs.go | 11 + d2common/d2geom/rectangle/namespace.go | 10 +- d2common/d2interface/sprite.go | 2 +- d2common/d2interface/surface.go | 1 + d2common/d2math/matrix4.go | 350 +++++++++++----------- d2common/d2math/quaternion.go | 20 +- d2common/d2sprite/sprite.go | 2 +- d2core/d2components/camera.go | 2 - d2core/d2components/position.go | 45 --- d2core/d2components/scale.go | 43 --- d2core/d2components/transform.go | 57 ++++ d2core/d2render/ebiten/ebiten_renderer.go | 2 + d2core/d2render/ebiten/ebiten_surface.go | 17 +- d2core/d2render/ebiten/surface_state.go | 1 + d2core/d2systems/app_bootstrap.go | 87 +++++- d2core/d2systems/doc.go | 2 +- d2core/d2systems/game_client_bootstrap.go | 48 +-- d2core/d2systems/game_config.go | 6 +- d2core/d2systems/game_config_test.go | 2 +- d2core/d2systems/game_object_factory.go | 4 +- d2core/d2systems/input_system.go | 8 +- d2core/d2systems/movement.go | 10 +- d2core/d2systems/render.go | 16 +- d2core/d2systems/scene_base.go | 229 +++++++------- d2core/d2systems/scene_ebiten_splash.go | 24 +- d2core/d2systems/scene_graph_testing.go | 1 + d2core/d2systems/scene_loading_screen.go | 8 +- d2core/d2systems/scene_mouse_cursor.go | 16 +- d2core/d2systems/scene_object_factory.go | 20 +- d2core/d2systems/scene_render_priority.go | 2 +- d2core/d2systems/scene_shape_system.go | 10 +- d2core/d2systems/scene_sprite_system.go | 8 +- d2core/d2systems/timescale.go | 48 +-- go.mod | 2 +- go.sum | 2 + main.go | 42 +-- 36 files changed, 615 insertions(+), 543 deletions(-) create mode 100644 d2app/app_ecs.go delete mode 100644 d2core/d2components/position.go delete mode 100644 d2core/d2components/scale.go create mode 100644 d2core/d2components/transform.go create mode 100644 d2core/d2systems/scene_graph_testing.go diff --git a/d2app/app_ecs.go b/d2app/app_ecs.go new file mode 100644 index 00000000..01d4b991 --- /dev/null +++ b/d2app/app_ecs.go @@ -0,0 +1,11 @@ +package d2app + +import ( + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2systems" + "github.com/gravestench/akara" +) + +func Run() { + cfg := akara.NewWorldConfig().With(&d2systems.AppBootstrap{}) + akara.NewWorld(cfg) +} diff --git a/d2common/d2geom/rectangle/namespace.go b/d2common/d2geom/rectangle/namespace.go index 130ef355..7c7b1aec 100644 --- a/d2common/d2geom/rectangle/namespace.go +++ b/d2common/d2geom/rectangle/namespace.go @@ -1,10 +1,9 @@ package rectangle -import ( - "github.com/gravestench/pho/geom/point" -) +import "github.com/gravestench/pho/geom/point" type RectangleNamespace interface { + New(x, y, w, h float64) *Rectangle Contains(r *Rectangle, x, y float64) bool GetPoint(r *Rectangle, position float64, p *point.Point) *point.Point GetPoints(r *Rectangle, quantity int, stepRate float64, points []*point.Point) []*point.Point @@ -31,6 +30,11 @@ type RectangleNamespace interface { type Namespace struct{} +// New creates a new Rectangle instance. +func (*Namespace) New(x, y, w, h float64) *Rectangle { + return New(x, y, w, h) +} + // Contains checks if the given x, y is inside the Rectangle's bounds. func (*Namespace) Contains(r *Rectangle, x, y float64) bool { return Contains(r, x, y) diff --git a/d2common/d2interface/sprite.go b/d2common/d2interface/sprite.go index 391f0708..023e57e8 100644 --- a/d2common/d2interface/sprite.go +++ b/d2common/d2interface/sprite.go @@ -8,7 +8,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" ) -// Animation is an animation +// Sprite is an Sprite type Sprite interface { BindRenderer(Renderer) Clone() Sprite diff --git a/d2common/d2interface/surface.go b/d2common/d2interface/surface.go index 62881ec2..db89347e 100644 --- a/d2common/d2interface/surface.go +++ b/d2common/d2interface/surface.go @@ -24,6 +24,7 @@ type Surface interface { PushTranslation(x, y int) PushSkew(x, y float64) PushScale(x, y float64) + PushRotate(theta float64) PushBrightness(brightness float64) PushSaturation(saturation float64) Render(surface Surface) diff --git a/d2common/d2math/matrix4.go b/d2common/d2math/matrix4.go index fd450368..f13e1c77 100644 --- a/d2common/d2math/matrix4.go +++ b/d2common/d2math/matrix4.go @@ -20,6 +20,38 @@ type Matrix4 struct { Values [numMatrix4Values]float64 } +// Decompose this matrix into vec3 for translation, rotation, and scale +func (m *Matrix4) Decompose() (t, r, s *Vector3) { + t = NewVector3(m.Values[12], m.Values[13], m.Values[14]) + + s = NewVector3( + NewVector3(m.Values[0], m.Values[1], m.Values[2]).Length(), + NewVector3(m.Values[4], m.Values[5], m.Values[6]).Length(), + NewVector3(m.Values[8], m.Values[9], m.Values[10]).Length(), + ) + + e := NewEuler(0, 0, 0, EulerOrderDefault) + + rotmat := m.Clone() + rotmat.Values[0] /= s.X + rotmat.Values[1] /= s.Y + rotmat.Values[2] /= s.Z + + rotmat.Values[4] /= s.X + rotmat.Values[5] /= s.Y + rotmat.Values[6] /= s.Z + + rotmat.Values[8] /= s.X + rotmat.Values[9] /= s.Y + rotmat.Values[10] /= s.Z + + e.SetFromRotationMatrix(rotmat, EulerOrderZYX) + + r = NewVector3(e.XYZ()) + + return t, r, s +} + // Clone makes a clone of this Matrix4. func (m *Matrix4) Clone() *Matrix4 { return NewMatrix4(m) @@ -47,12 +79,10 @@ func (m *Matrix4) Set(other *Matrix4) *Matrix4 { // SetValues sets the values of this Matrix4. func (m *Matrix4) SetValues(a, b, c, d, e, f, g, h, i, j, k, l, mm, n, o, p float64) *Matrix4 { - v := m.Values[:] - - v[0], v[1], v[2], v[3] = a, b, c, d - v[4], v[5], v[6], v[7] = e, f, g, h - v[8], v[9], v[10], v[11] = i, j, k, l - v[12], v[13], v[14], v[15] = mm, n, o, p + m.Values[0], m.Values[1], m.Values[2], m.Values[3] = a, b, c, d + m.Values[4], m.Values[5], m.Values[6], m.Values[7] = e, f, g, h + m.Values[8], m.Values[9], m.Values[10], m.Values[11] = i, j, k, l + m.Values[12], m.Values[13], m.Values[14], m.Values[15] = mm, n, o, p return m } @@ -130,27 +160,25 @@ func (m *Matrix4) SetScaling(x, y, z float64) *Matrix4 { // Transpose this Matrix. func (m *Matrix4) Transpose() *Matrix4 { - a := m.Values[:] + a01 := m.Values[1] + a02 := m.Values[2] + a03 := m.Values[3] + a12 := m.Values[6] + a13 := m.Values[7] + a23 := m.Values[11] - a01 := a[1] - a02 := a[2] - a03 := a[3] - a12 := a[6] - a13 := a[7] - a23 := a[11] - - a[1] = a[4] - a[2] = a[8] - a[3] = a[12] - a[4] = a01 - a[6] = a[9] - a[7] = a[13] - a[8] = a02 - a[9] = a12 - a[11] = a[14] - a[12] = a03 - a[13] = a13 - a[14] = a23 + m.Values[1] = m.Values[4] + m.Values[2] = m.Values[8] + m.Values[3] = m.Values[12] + m.Values[4] = a01 + m.Values[6] = m.Values[9] + m.Values[7] = m.Values[13] + m.Values[8] = a02 + m.Values[9] = a12 + m.Values[11] = m.Values[14] + m.Values[12] = a03 + m.Values[13] = a13 + m.Values[14] = a23 return m } @@ -162,12 +190,10 @@ func (m *Matrix4) GetInverse(other *Matrix4) *Matrix4 { // Invert this Matrix. func (m *Matrix4) Invert() *Matrix4 { - a := m.Values[:] - - a00, a01, a02, a03 := a[0], a[1], a[2], a[3] - a10, a11, a12, a13 := a[4], a[5], a[6], a[7] - a20, a21, a22, a23 := a[8], a[9], a[10], a[11] - a30, a31, a32, a33 := a[12], a[13], a[14], a[15] + a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3] + a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7] + a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11] + a30, a31, a32, a33 := m.Values[12], m.Values[13], m.Values[14], m.Values[15] b00 := a00*a11 - a01*a10 b01 := a00*a12 - a02*a10 @@ -213,12 +239,10 @@ func (m *Matrix4) Invert() *Matrix4 { // Adjoint calculates the adjoint, or adjugate, of this Matrix. func (m *Matrix4) Adjoint() *Matrix4 { - a := m.Values[:] - - a00, a01, a02, a03 := a[0], a[1], a[2], a[3] - a10, a11, a12, a13 := a[4], a[5], a[6], a[7] - a20, a21, a22, a23 := a[8], a[9], a[10], a[11] - a30, a31, a32, a33 := a[12], a[13], a[14], a[15] + a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3] + a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7] + a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11] + a30, a31, a32, a33 := m.Values[12], m.Values[13], m.Values[14], m.Values[15] return m.SetValues( a11*(a22*a33-a23*a32)-a21*(a12*a33-a13*a32)+a31*(a12*a23-a13*a22), @@ -242,12 +266,10 @@ func (m *Matrix4) Adjoint() *Matrix4 { // Determinant calculates the determinant of this Matrix. func (m *Matrix4) Determinant() float64 { - a := m.Values[:] - - a00, a01, a02, a03 := a[0], a[1], a[2], a[3] - a10, a11, a12, a13 := a[4], a[5], a[6], a[7] - a20, a21, a22, a23 := a[8], a[9], a[10], a[11] - a30, a31, a32, a33 := a[12], a[13], a[14], a[15] + a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3] + a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7] + a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11] + a30, a31, a32, a33 := m.Values[12], m.Values[13], m.Values[14], m.Values[15] b00 := a00*a11 - a01*a10 b01 := a00*a12 - a02*a10 @@ -267,62 +289,58 @@ func (m *Matrix4) Determinant() float64 { // Multiply this Matrix4 by the given Matrix4. func (m *Matrix4) Multiply(other *Matrix4) *Matrix4 { - a, b := m.Values, other.Values - - a00, a01, a02, a03 := a[0], a[1], a[2], a[3] - a10, a11, a12, a13 := a[4], a[5], a[6], a[7] - a20, a21, a22, a23 := a[8], a[9], a[10], a[11] - a30, a31, a32, a33 := a[12], a[13], a[14], a[15] + a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3] + a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7] + a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11] + a30, a31, a32, a33 := m.Values[12], m.Values[13], m.Values[14], m.Values[15] // Cache only the current line of the second matrix - b0, b1, b2, b3 := b[0], b[1], b[2], b[3] - a[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30 - a[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31 - a[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32 - a[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33 + b0, b1, b2, b3 := other.Values[0], other.Values[1], other.Values[2], other.Values[3] + m.Values[0] = b0*a00 + b1*a10 + b2*a20 + b3*a30 + m.Values[1] = b0*a01 + b1*a11 + b2*a21 + b3*a31 + m.Values[2] = b0*a02 + b1*a12 + b2*a22 + b3*a32 + m.Values[3] = b0*a03 + b1*a13 + b2*a23 + b3*a33 - b0, b1, b2, b3 = b[4], b[5], b[6], b[7] - a[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30 - a[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31 - a[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32 - a[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33 + b0, b1, b2, b3 = other.Values[4], other.Values[5], other.Values[6], other.Values[7] + m.Values[4] = b0*a00 + b1*a10 + b2*a20 + b3*a30 + m.Values[5] = b0*a01 + b1*a11 + b2*a21 + b3*a31 + m.Values[6] = b0*a02 + b1*a12 + b2*a22 + b3*a32 + m.Values[7] = b0*a03 + b1*a13 + b2*a23 + b3*a33 - b0, b1, b2, b3 = b[8], b[9], b[10], b[11] - a[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30 - a[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31 - a[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32 - a[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33 + b0, b1, b2, b3 = other.Values[8], other.Values[9], other.Values[10], other.Values[11] + m.Values[8] = b0*a00 + b1*a10 + b2*a20 + b3*a30 + m.Values[9] = b0*a01 + b1*a11 + b2*a21 + b3*a31 + m.Values[10] = b0*a02 + b1*a12 + b2*a22 + b3*a32 + m.Values[11] = b0*a03 + b1*a13 + b2*a23 + b3*a33 - b0, b1, b2, b3 = b[12], b[13], b[14], b[15] - a[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30 - a[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31 - a[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32 - a[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33 + b0, b1, b2, b3 = other.Values[12], other.Values[13], other.Values[14], other.Values[15] + m.Values[12] = b0*a00 + b1*a10 + b2*a20 + b3*a30 + m.Values[13] = b0*a01 + b1*a11 + b2*a21 + b3*a31 + m.Values[14] = b0*a02 + b1*a12 + b2*a22 + b3*a32 + m.Values[15] = b0*a03 + b1*a13 + b2*a23 + b3*a33 return m } // MultiplyLocal multiplies the values of this Matrix4 by those given in the `other` argument. func (m *Matrix4) MultiplyLocal(other *Matrix4) *Matrix4 { - a, b := m.Values, other.Values - return m.SetValues( - a[0]*b[0]+a[1]*b[4]+a[2]*b[8]+a[3]*b[12], - a[0]*b[1]+a[1]*b[5]+a[2]*b[9]+a[3]*b[13], - a[0]*b[2]+a[1]*b[6]+a[2]*b[10]+a[3]*b[14], - a[0]*b[3]+a[1]*b[7]+a[2]*b[11]+a[3]*b[15], - a[4]*b[0]+a[5]*b[4]+a[6]*b[8]+a[7]*b[12], - a[4]*b[1]+a[5]*b[5]+a[6]*b[9]+a[7]*b[13], - a[4]*b[2]+a[5]*b[6]+a[6]*b[10]+a[7]*b[14], - a[4]*b[3]+a[5]*b[7]+a[6]*b[11]+a[7]*b[15], - a[8]*b[0]+a[9]*b[4]+a[10]*b[8]+a[11]*b[12], - a[8]*b[1]+a[9]*b[5]+a[10]*b[9]+a[11]*b[13], - a[8]*b[2]+a[9]*b[6]+a[10]*b[10]+a[11]*b[14], - a[8]*b[3]+a[9]*b[7]+a[10]*b[11]+a[11]*b[15], - a[12]*b[0]+a[13]*b[4]+a[14]*b[8]+a[15]*b[12], - a[12]*b[1]+a[13]*b[5]+a[14]*b[9]+a[15]*b[13], - a[12]*b[2]+a[13]*b[6]+a[14]*b[10]+a[15]*b[14], - a[12]*b[3]+a[13]*b[7]+a[14]*b[11]+a[15]*b[15], + m.Values[0]*other.Values[0]+m.Values[1]*other.Values[4]+m.Values[2]*other.Values[8]+m.Values[3]*other.Values[12], + m.Values[0]*other.Values[1]+m.Values[1]*other.Values[5]+m.Values[2]*other.Values[9]+m.Values[3]*other.Values[13], + m.Values[0]*other.Values[2]+m.Values[1]*other.Values[6]+m.Values[2]*other.Values[10]+m.Values[3]*other.Values[14], + m.Values[0]*other.Values[3]+m.Values[1]*other.Values[7]+m.Values[2]*other.Values[11]+m.Values[3]*other.Values[15], + m.Values[4]*other.Values[0]+m.Values[5]*other.Values[4]+m.Values[6]*other.Values[8]+m.Values[7]*other.Values[12], + m.Values[4]*other.Values[1]+m.Values[5]*other.Values[5]+m.Values[6]*other.Values[9]+m.Values[7]*other.Values[13], + m.Values[4]*other.Values[2]+m.Values[5]*other.Values[6]+m.Values[6]*other.Values[10]+m.Values[7]*other.Values[14], + m.Values[4]*other.Values[3]+m.Values[5]*other.Values[7]+m.Values[6]*other.Values[11]+m.Values[7]*other.Values[15], + m.Values[8]*other.Values[0]+m.Values[9]*other.Values[4]+m.Values[10]*other.Values[8]+m.Values[11]*other.Values[12], + m.Values[8]*other.Values[1]+m.Values[9]*other.Values[5]+m.Values[10]*other.Values[9]+m.Values[11]*other.Values[13], + m.Values[8]*other.Values[2]+m.Values[9]*other.Values[6]+m.Values[10]*other.Values[10]+m.Values[11]*other.Values[14], + m.Values[8]*other.Values[3]+m.Values[9]*other.Values[7]+m.Values[10]*other.Values[11]+m.Values[11]*other.Values[15], + m.Values[12]*other.Values[0]+m.Values[13]*other.Values[4]+m.Values[14]*other.Values[8]+m.Values[15]*other.Values[12], + m.Values[12]*other.Values[1]+m.Values[13]*other.Values[5]+m.Values[14]*other.Values[9]+m.Values[15]*other.Values[13], + m.Values[12]*other.Values[2]+m.Values[13]*other.Values[6]+m.Values[14]*other.Values[10]+m.Values[15]*other.Values[14], + m.Values[12]*other.Values[3]+m.Values[13]*other.Values[7]+m.Values[14]*other.Values[11]+m.Values[15]*other.Values[15], ) } @@ -333,41 +351,39 @@ func (m *Matrix4) PreMultiply(other *Matrix4) *Matrix4 { // MultiplyMatrices multiplies the two given Matrix4 objects and stores the results in this Matrix. func (m *Matrix4) MultiplyMatrices(a, b *Matrix4) *Matrix4 { - am, bm := a.Values, b.Values + a11 := a.Values[0] + a12 := a.Values[4] + a13 := a.Values[8] + a14 := a.Values[12] + a21 := a.Values[1] + a22 := a.Values[5] + a23 := a.Values[9] + a24 := a.Values[13] + a31 := a.Values[2] + a32 := a.Values[6] + a33 := a.Values[10] + a34 := a.Values[14] + a41 := a.Values[3] + a42 := a.Values[7] + a43 := a.Values[11] + a44 := a.Values[15] - a11 := am[0] - a12 := am[4] - a13 := am[8] - a14 := am[12] - a21 := am[1] - a22 := am[5] - a23 := am[9] - a24 := am[13] - a31 := am[2] - a32 := am[6] - a33 := am[10] - a34 := am[14] - a41 := am[3] - a42 := am[7] - a43 := am[11] - a44 := am[15] - - b11 := bm[0] - b12 := bm[4] - b13 := bm[8] - b14 := bm[12] - b21 := bm[1] - b22 := bm[5] - b23 := bm[9] - b24 := bm[13] - b31 := bm[2] - b32 := bm[6] - b33 := bm[10] - b34 := bm[14] - b41 := bm[3] - b42 := bm[7] - b43 := bm[11] - b44 := bm[15] + b11 := b.Values[0] + b12 := b.Values[4] + b13 := b.Values[8] + b14 := b.Values[12] + b21 := b.Values[1] + b22 := b.Values[5] + b23 := b.Values[9] + b24 := b.Values[13] + b31 := b.Values[2] + b32 := b.Values[6] + b33 := b.Values[10] + b34 := b.Values[14] + b41 := b.Values[3] + b42 := b.Values[7] + b43 := b.Values[11] + b44 := b.Values[15] return m.SetValues( a11*b11+a12*b21+a13*b31+a14*b41, @@ -396,12 +412,10 @@ func (m *Matrix4) Translate(v Vector3Like) *Matrix4 { // TranslateXYZ translates this Matrix using the given values. func (m *Matrix4) TranslateXYZ(x, y, z float64) *Matrix4 { - a := m.Values[:] - - a[12] = a[0]*x + a[4]*y + a[8]*z + a[12] - a[13] = a[1]*x + a[5]*y + a[9]*z + a[13] - a[14] = a[2]*x + a[6]*y + a[10]*z + a[14] - a[15] = a[3]*x + a[7]*y + a[11]*z + a[15] + m.Values[12] = m.Values[0]*x + m.Values[4]*y + m.Values[8]*z + m.Values[12] + m.Values[13] = m.Values[1]*x + m.Values[5]*y + m.Values[9]*z + m.Values[13] + m.Values[14] = m.Values[2]*x + m.Values[6]*y + m.Values[10]*z + m.Values[14] + m.Values[15] = m.Values[3]*x + m.Values[7]*y + m.Values[11]*z + m.Values[15] return m } @@ -413,11 +427,9 @@ func (m *Matrix4) Scale(v Vector3Like) *Matrix4 { // ScaleXYZ applies a scale transformation to this Matrix. func (m *Matrix4) ScaleXYZ(x, y, z float64) *Matrix4 { - a := m.Values[:] - - a[0], a[1], a[2], a[3] = a[0]*x, a[1]*x, a[2]*x, a[3]*x - a[4], a[5], a[6], a[7] = a[4]*y, a[5]*y, a[6]*y, a[7]*y - a[8], a[9], a[10], a[11] = a[8]*z, a[9]*z, a[10]*z, a[11]*z + m.Values[0], m.Values[1], m.Values[2], m.Values[3] = m.Values[0]*x, m.Values[1]*x, m.Values[2]*x, m.Values[3]*x + m.Values[4], m.Values[5], m.Values[6], m.Values[7] = m.Values[4]*y, m.Values[5]*y, m.Values[6]*y, m.Values[7]*y + m.Values[8], m.Values[9], m.Values[10], m.Values[11] = m.Values[8]*z, m.Values[9]*z, m.Values[10]*z, m.Values[11]*z return m } @@ -439,7 +451,6 @@ func (m *Matrix4) MakeRotationAxis(axis Vector3Like, radians float64) *Matrix4 { // Rotate applies a rotation transformation to this Matrix. func (m *Matrix4) Rotate(radians float64, axis Vector3Like) *Matrix4 { - a := m.Values[:] x, y, z := axis.XYZ() length := math.Sqrt(x*x + y*y + z*z) @@ -455,10 +466,10 @@ func (m *Matrix4) Rotate(radians float64, axis Vector3Like) *Matrix4 { c, s := math.Cos(radians), math.Sin(radians) t := 1 - c - a00, a01, a02, a03 := a[0], a[1], a[2], a[3] - a10, a11, a12, a13 := a[4], a[5], a[6], a[7] - a20, a21, a22, a23 := a[8], a[9], a[10], a[11] - a30, a31, a32, a33 := a[12], a[13], a[14], a[15] + a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3] + a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7] + a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11] + a30, a31, a32, a33 := m.Values[12], m.Values[13], m.Values[14], m.Values[15] b00, b01, b02 := x*x*t+c, y*x*t+z*s, z*x*t-y*s b10, b11, b12 := x*y*t-z*s, y*y*t+c, z*y*t+x*s @@ -483,63 +494,60 @@ func (m *Matrix4) Rotate(radians float64, axis Vector3Like) *Matrix4 { // RotateX rotates this matrix on its X axis. func (m *Matrix4) RotateX(radians float64) *Matrix4 { - a := m.Values[:] c, s := math.Cos(radians), math.Sin(radians) - a10, a11, a12, a13 := a[4], a[5], a[6], a[7] - a20, a21, a22, a23 := a[8], a[9], a[10], a[11] + a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7] + a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11] // Perform axis-specific matrix multiplication - a[4] = a10*c + a20*s - a[5] = a11*c + a21*s - a[6] = a12*c + a22*s - a[7] = a13*c + a23*s - a[8] = a20*c - a10*s - a[9] = a21*c - a11*s - a[10] = a22*c - a12*s - a[11] = a23*c - a13*s + m.Values[4] = a10*c + a20*s + m.Values[5] = a11*c + a21*s + m.Values[6] = a12*c + a22*s + m.Values[7] = a13*c + a23*s + m.Values[8] = a20*c - a10*s + m.Values[9] = a21*c - a11*s + m.Values[10] = a22*c - a12*s + m.Values[11] = a23*c - a13*s return m } // RotateY rotates this matrix on its X axis. func (m *Matrix4) RotateY(radians float64) *Matrix4 { - a := m.Values[:] c, s := math.Cos(radians), math.Sin(radians) - a00, a01, a02, a03 := a[0], a[1], a[2], a[3] - a20, a21, a22, a23 := a[8], a[9], a[10], a[11] + a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3] + a20, a21, a22, a23 := m.Values[8], m.Values[9], m.Values[10], m.Values[11] // Perform axis-specific matrix multiplication - a[0] = a00*c + a20*s - a[1] = a01*c + a21*s - a[2] = a02*c + a22*s - a[3] = a03*c + a23*s - a[4] = a20*c - a00*s - a[5] = a21*c - a01*s - a[6] = a22*c - a02*s - a[7] = a23*c - a03*s + m.Values[0] = a00*c + a20*s + m.Values[1] = a01*c + a21*s + m.Values[2] = a02*c + a22*s + m.Values[3] = a03*c + a23*s + m.Values[4] = a20*c - a00*s + m.Values[5] = a21*c - a01*s + m.Values[6] = a22*c - a02*s + m.Values[7] = a23*c - a03*s return m } // RotateZ rotates this matrix on its X axis. func (m *Matrix4) RotateZ(radians float64) *Matrix4 { - a := m.Values[:] c, s := math.Cos(radians), math.Sin(radians) - a00, a01, a02, a03 := a[0], a[1], a[2], a[3] - a10, a11, a12, a13 := a[4], a[5], a[6], a[7] + a00, a01, a02, a03 := m.Values[0], m.Values[1], m.Values[2], m.Values[3] + a10, a11, a12, a13 := m.Values[4], m.Values[5], m.Values[6], m.Values[7] // Perform axis-specific matrix multiplication - a[0] = a00*c + a10*s - a[1] = a01*c + a11*s - a[2] = a02*c + a12*s - a[3] = a03*c + a13*s - a[4] = a10*c - a00*s - a[5] = a11*c - a01*s - a[6] = a12*c - a02*s - a[7] = a13*c - a03*s + m.Values[0] = a00*c + a10*s + m.Values[1] = a01*c + a11*s + m.Values[2] = a02*c + a12*s + m.Values[3] = a03*c + a13*s + m.Values[4] = a10*c - a00*s + m.Values[5] = a11*c - a01*s + m.Values[6] = a12*c - a02*s + m.Values[7] = a13*c - a03*s return m } diff --git a/d2common/d2math/quaternion.go b/d2common/d2math/quaternion.go index e389934a..00414198 100644 --- a/d2common/d2math/quaternion.go +++ b/d2common/d2math/quaternion.go @@ -380,17 +380,15 @@ func (q *Quaternion) SetFromEuler(e *Euler) *Quaternion { // SetFromRotationMatrix sets the rotation of this Quaternion from the given Matrix4. func (q *Quaternion) SetFromRotationMatrix(m4 *Matrix4) *Quaternion { - m := m4.Values - - m11 := m[0] - m12 := m[4] - m13 := m[8] - m21 := m[1] - m22 := m[5] - m23 := m[9] - m31 := m[2] - m32 := m[6] - m33 := m[10] + m11 := m4.Values[0] + m12 := m4.Values[4] + m13 := m4.Values[8] + m21 := m4.Values[1] + m22 := m4.Values[5] + m23 := m4.Values[9] + m31 := m4.Values[2] + m32 := m4.Values[6] + m33 := m4.Values[10] trace := m11 + m22 + m33 var s float64 diff --git a/d2common/d2sprite/sprite.go b/d2common/d2sprite/sprite.go index 90a7440b..f3f364e6 100644 --- a/d2common/d2sprite/sprite.go +++ b/d2common/d2sprite/sprite.go @@ -83,7 +83,7 @@ func (a *Sprite) Advance(elapsed time.Duration) error { a.lastFrameTime += elapsed framesAdvanced := int(float64(a.lastFrameTime) / float64(frameLength)) - a.lastFrameTime -= time.Duration(float64(framesAdvanced) * float64(frameLength)) + a.lastFrameTime -= time.Duration(float64(framesAdvanced)) * frameLength for i := 0; i < framesAdvanced; i++ { startIndex := 0 diff --git a/d2core/d2components/camera.go b/d2core/d2components/camera.go index 89450fbc..97432d13 100644 --- a/d2core/d2components/camera.go +++ b/d2core/d2components/camera.go @@ -20,7 +20,6 @@ var _ akara.Component = &Camera{} // Camera represents a camera that can be rendered to type Camera struct { - Position *d2math.Vector3 PerspectiveMatrix *d2math.Matrix4 OrthogonalMatrix *d2math.Matrix4 Size *d2math.Vector2 @@ -31,7 +30,6 @@ type Camera struct { // The camera defaults to position (0,0), 800x600 resolution, and zoom of 1.0 func (*Camera) New() akara.Component { c := &Camera{ - Position: d2math.NewVector3(0, 0, defaultCameraZ), Size: d2math.NewVector2(defaultCameraWidth, defaultCameraHeight), Clip: d2math.NewVector2(defaultCameraNear, defaultCameraFar), } diff --git a/d2core/d2components/position.go b/d2core/d2components/position.go deleted file mode 100644 index 32ee5be3..00000000 --- a/d2core/d2components/position.go +++ /dev/null @@ -1,45 +0,0 @@ -//nolint:dupl,golint,stylecheck // component declarations are supposed to look the same -package d2components - -import ( - "github.com/gravestench/akara" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" -) - -// static check that Position implements Component -var _ akara.Component = &Position{} - -// 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 { - *d2math.Vector3 -} - -// New creates a new Position. By default, the position is (0,0) -func (*Position) New() akara.Component { - return &Position{ - Vector3: d2math.NewVector3(0, 0, 0), - } -} - -// 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 -} - -// 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 component.(*Position), found -} diff --git a/d2core/d2components/scale.go b/d2core/d2components/scale.go deleted file mode 100644 index ce70e868..00000000 --- a/d2core/d2components/scale.go +++ /dev/null @@ -1,43 +0,0 @@ -//nolint:dupl,golint,stylecheck // component declarations are supposed to look the same -package d2components - -import ( - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" - "github.com/gravestench/akara" -) - -// static check that Scale implements Component -var _ akara.Component = &Scale{} - -// Scale represents an entities x,y axis scale as a vector -type Scale struct { - *d2math.Vector3 -} - -// New creates a new Scale instance. By default, the scale is (1,1) -func (*Scale) New() akara.Component { - return &Scale{ - Vector3: d2math.NewVector3(1, 1, 1), - } -} - -// 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 -} - -// 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 component.(*Scale), found -} diff --git a/d2core/d2components/transform.go b/d2core/d2components/transform.go new file mode 100644 index 00000000..13043757 --- /dev/null +++ b/d2core/d2components/transform.go @@ -0,0 +1,57 @@ +//nolint:dupl,golint,stylecheck // component declarations are supposed to look the same +package d2components + +import ( + "github.com/gravestench/akara" + + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2math" +) + +// static check that Transform implements Component +var _ akara.Component = &Transform{} + +// Transform contains a vec3 for Translation, Rotation, and Scale +type Transform struct { + Translation *d2math.Vector3 + Rotation *d2math.Vector3 + Scale *d2math.Vector3 +} + +func (t *Transform) GetMatrix() *d2math.Matrix4 { + return d2math.NewMatrix4(nil). + Translate(t.Translation). + RotateX(t.Rotation.X). + RotateY(t.Rotation.Y). + RotateZ(t.Rotation.Z). + ScaleXYZ(t.Scale.XYZ()) +} + +// New creates a new Transform. By default, the position is (0,0) +func (*Transform) New() akara.Component { + return &Transform{ + Translation: d2math.NewVector3(0, 0, 0), + Rotation: d2math.NewVector3(0, 0, 0), + Scale: d2math.NewVector3(1, 1, 1), + } +} + +// TransformFactory is a wrapper for the generic component factory that returns Transform component instances. +// This can be embedded inside of a system to give them the methods for adding, retrieving, and removing a Transform. +type TransformFactory struct { + Transform *akara.ComponentFactory +} + +// AddTransform adds a Transform component to the given entity and returns it +func (m *TransformFactory) AddTransform(id akara.EID) *Transform { + return m.Transform.Add(id).(*Transform) +} + +// GetTransform returns the Transform component for the given entity, and a bool for whether or not it exists +func (m *TransformFactory) GetTransform(id akara.EID) (*Transform, bool) { + component, found := m.Transform.Get(id) + if !found { + return nil, found + } + + return component.(*Transform), found +} diff --git a/d2core/d2render/ebiten/ebiten_renderer.go b/d2core/d2render/ebiten/ebiten_renderer.go index 68a87a1d..faa0f528 100644 --- a/d2core/d2render/ebiten/ebiten_renderer.go +++ b/d2core/d2render/ebiten/ebiten_renderer.go @@ -23,6 +23,7 @@ const ( defaultSkewY = 0.0 defaultScaleX = 1.0 defaultScaleY = 1.0 + defaultRotate = 0.0 ) type renderCallback = func(surface d2interface.Surface) error @@ -129,6 +130,7 @@ func (r *Renderer) CreateSurface(surface d2interface.Surface) (d2interface.Surfa skewY: defaultSkewY, scaleX: defaultScaleX, scaleY: defaultScaleY, + rotate: defaultRotate, } result := createEbitenSurface(r, img, sfcState) diff --git a/d2core/d2render/ebiten/ebiten_surface.go b/d2core/d2render/ebiten/ebiten_surface.go index 6060d9a7..7541faca 100644 --- a/d2core/d2render/ebiten/ebiten_surface.go +++ b/d2core/d2render/ebiten/ebiten_surface.go @@ -89,6 +89,12 @@ func (s *ebitenSurface) PushScale(scaleX, scaleY float64) { s.stateCurrent.scaleY = scaleY } +// PushRotate pushes a rotation to the state stack +func (s *ebitenSurface) PushRotate(theta float64) { + s.stateStack = append(s.stateStack, s.stateCurrent) + s.stateCurrent.rotate = theta +} + // PushEffect pushes an effect to the state stack func (s *ebitenSurface) PushEffect(effect d2enum.DrawEffect) { s.stateStack = append(s.stateStack, s.stateCurrent) @@ -178,14 +184,9 @@ func (s *ebitenSurface) RenderSection(sfc d2interface.Surface, bound image.Recta func (s *ebitenSurface) createDrawImageOptions() *ebiten.DrawImageOptions { opts := &ebiten.DrawImageOptions{} - if s.stateCurrent.skewX != 0 || s.stateCurrent.skewY != 0 { - opts.GeoM.Skew(s.stateCurrent.skewX, s.stateCurrent.skewY) - } - - if s.stateCurrent.scaleX != 1.0 || s.stateCurrent.scaleY != 1.0 { - opts.GeoM.Scale(s.stateCurrent.scaleX, s.stateCurrent.scaleY) - } - + opts.GeoM.Skew(s.stateCurrent.skewX, s.stateCurrent.skewY) + opts.GeoM.Scale(s.stateCurrent.scaleX, s.stateCurrent.scaleY) + opts.GeoM.Rotate(s.stateCurrent.rotate) opts.GeoM.Translate(float64(s.stateCurrent.x), float64(s.stateCurrent.y)) opts.Filter = s.stateCurrent.filter diff --git a/d2core/d2render/ebiten/surface_state.go b/d2core/d2render/ebiten/surface_state.go index 37fe7471..e01895bc 100644 --- a/d2core/d2render/ebiten/surface_state.go +++ b/d2core/d2render/ebiten/surface_state.go @@ -18,4 +18,5 @@ type surfaceState struct { effect d2enum.DrawEffect skewX, skewY float64 scaleX, scaleY float64 + rotate float64 } diff --git a/d2core/d2systems/app_bootstrap.go b/d2core/d2systems/app_bootstrap.go index c0498060..8aecf7ab 100644 --- a/d2core/d2systems/app_bootstrap.go +++ b/d2core/d2systems/app_bootstrap.go @@ -1,6 +1,8 @@ package d2systems import ( + "fmt" + "gopkg.in/alecthomas/kingpin.v2" "os" "path" @@ -19,12 +21,26 @@ const ( logPrefixAppBootstrap = "App Bootstrap" ) -// static check that the game config system implements the system interface -var _ akara.System = &AppBootstrapSystem{} +const ( + sceneTestArg = "testscene" + sceneTestDesc = "test a scene" -// AppBootstrapSystem is responsible for the common initialization process between + serverArg = "server" + serverDesc = "run dedicated server" + + counterArg = "counter" + counterDesc = "print updates/sec" + + skipSplashArg = "nosplash" + skipSplashDesc = "skip the ebiten splash screen" +) + +// static check that the game config system implements the system interface +var _ akara.System = &AppBootstrap{} + +// AppBootstrap 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 { +type AppBootstrap struct { akara.BaseSubscriberSystem *d2util.Logger subscribedFiles *akara.Subscription @@ -37,7 +53,7 @@ type AppBootstrapSystem struct { } // Init will inject (or use existing) components related to setting up the config sources -func (m *AppBootstrapSystem) Init(world *akara.World) { +func (m *AppBootstrap) Init(world *akara.World) { m.World = world m.setupLogger() @@ -49,16 +65,17 @@ func (m *AppBootstrapSystem) Init(world *akara.World) { m.injectSystems() m.setupConfigSources() m.setupConfigFile() + m.parseCommandLineArgs() m.Info("... initialization complete!") } -func (m *AppBootstrapSystem) setupLogger() { +func (m *AppBootstrap) setupLogger() { m.Logger = d2util.NewLogger() m.SetPrefix(logPrefixAppBootstrap) } -func (m *AppBootstrapSystem) setupSubscriptions() { +func (m *AppBootstrap) setupSubscriptions() { m.Info("setting up component subscriptions") // we are going to check entities that dont yet have loaded asset types @@ -91,7 +108,7 @@ func (m *AppBootstrapSystem) setupSubscriptions() { m.subscribedConfigs = m.World.AddSubscription(gameConfigs) } -func (m *AppBootstrapSystem) setupFactories() { +func (m *AppBootstrap) setupFactories() { m.Info("setting up component factories") m.InjectComponent(&d2components.GameConfig{}, &m.GameConfig) @@ -101,7 +118,7 @@ func (m *AppBootstrapSystem) setupFactories() { m.InjectComponent(&d2components.FileSource{}, &m.FileSource) } -func (m *AppBootstrapSystem) injectSystems() { +func (m *AppBootstrap) injectSystems() { m.Info("injecting file type resolution system") m.AddSystem(&FileTypeResolver{}) @@ -116,9 +133,6 @@ func (m *AppBootstrapSystem) injectSystems() { 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 @@ -126,7 +140,7 @@ func (m *AppBootstrapSystem) injectSystems() { // the file type resolver system, and then the file source resolver system. At that point, // there will be sources for these two directories that can possibly resolve a config file. // A new config file is created if one is not found. -func (m *AppBootstrapSystem) setupConfigSources() { +func (m *AppBootstrap) setupConfigSources() { // make the two entities, these will be the file sources e1, e2 := m.NewEntity(), m.NewEntity() @@ -152,7 +166,7 @@ func (m *AppBootstrapSystem) setupConfigSources() { } } -func (m *AppBootstrapSystem) setupConfigFile() { +func (m *AppBootstrap) setupConfigFile() { // add an entity that will get picked up by the game config system and loaded m.AddFilePath(m.NewEntity()).Path = configFileName m.Infof("setting up config file `%s` for processing", configFileName) @@ -160,7 +174,7 @@ func (m *AppBootstrapSystem) setupConfigFile() { // Update will look for the first entity with a game config component // and then add the mpq's as file sources -func (m *AppBootstrapSystem) Update() { +func (m *AppBootstrap) Update() { configs := m.subscribedConfigs.GetEntities() if len(configs) < 1 { return @@ -182,7 +196,7 @@ func (m *AppBootstrapSystem) Update() { m.SetActive(false) } -func (m *AppBootstrapSystem) initMpqSources(cfg *d2components.GameConfig) { +func (m *AppBootstrap) initMpqSources(cfg *d2components.GameConfig) { for _, mpqFileName := range cfg.MpqLoadOrder { fullMpqFilePath := path.Join(cfg.MpqPath, mpqFileName) @@ -193,3 +207,44 @@ func (m *AppBootstrapSystem) initMpqSources(cfg *d2components.GameConfig) { mpqSource.Path = fullMpqFilePath } } + +func (m *AppBootstrap) parseCommandLineArgs() { + sceneTest := kingpin.Flag(sceneTestArg, sceneTestDesc).String() + server := kingpin.Flag(serverArg, serverDesc).Bool() + enableCounter := kingpin.Flag(counterArg, counterDesc).Bool() + _ = kingpin.Flag(skipSplashArg, skipSplashDesc).Bool() // see game client bootstrap + + kingpin.Parse() + + if *enableCounter { + m.World.AddSystem(&UpdateCounter{}) + } + + if *server { + fmt.Println("not yet implemented") + os.Exit(0) + } + + m.World.AddSystem(&RenderSystem{}) + m.World.AddSystem(&InputSystem{}) + + switch *sceneTest { + case "splash": + m.Info("running ebiten splash scene") + m.World.AddSystem(NewEbitenSplashScene()) + case "load": + m.Info("running loading scene") + m.World.AddSystem(NewLoadingScene()) + case "mouse": + m.Info("running mouse cursor scene") + m.World.AddSystem(NewMouseCursorScene()) + case "main": + m.Info("running main menu scene") + m.World.AddSystem(NewMainMenuScene()) + case "terminal": + m.Info("running terminal scene") + m.World.AddSystem(NewTerminalScene()) + default: + m.World.AddSystem(&GameClientBootstrap{}) + } +} diff --git a/d2core/d2systems/doc.go b/d2core/d2systems/doc.go index 8c46db19..9db9638c 100644 --- a/d2core/d2systems/doc.go +++ b/d2core/d2systems/doc.go @@ -1,2 +1,2 @@ -// Package d2systems provides all of the ECS baseSystems +// Package d2systems provides all of the ECS sceneSystems package d2systems diff --git a/d2core/d2systems/game_client_bootstrap.go b/d2core/d2systems/game_client_bootstrap.go index a9afd334..ddc616d7 100644 --- a/d2core/d2systems/game_client_bootstrap.go +++ b/d2core/d2systems/game_client_bootstrap.go @@ -1,9 +1,9 @@ package d2systems import ( - "github.com/gravestench/akara" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" + "github.com/gravestench/akara" + "gopkg.in/alecthomas/kingpin.v2" ) const ( @@ -11,17 +11,17 @@ const ( ) // static check that the game config system implements the system interface -var _ akara.System = &GameClientBootstrapSystem{} +var _ akara.System = &GameClientBootstrap{} -// GameClientBootstrapSystem is responsible for setting up other -// baseSystems that are common to both the game client and the headless game server -type GameClientBootstrapSystem struct { +// GameClientBootstrap is responsible for setting up other +// sceneSystems that are common to both the game client and the headless game server +type GameClientBootstrap struct { akara.BaseSubscriberSystem *d2util.Logger } -// Init injects the common baseSystems required by both the game client and headless server -func (m *GameClientBootstrapSystem) Init(world *akara.World) { +// Init injects the common sceneSystems required by both the game client and headless server +func (m *GameClientBootstrap) Init(world *akara.World) { m.World = world m.setupLogger() @@ -37,20 +37,20 @@ func (m *GameClientBootstrapSystem) Init(world *akara.World) { } } -func (m *GameClientBootstrapSystem) setupLogger() { +func (m *GameClientBootstrap) setupLogger() { m.Logger = d2util.NewLogger() m.SetPrefix(logPrefixGameClientBootstrap) } -func (m *GameClientBootstrapSystem) injectSystems() { - m.Info("injecting render system") - m.AddSystem(&RenderSystem{}) +func (m *GameClientBootstrap) injectSystems() { + m.Info("injecting terminal scene") + m.AddSystem(NewTerminalScene()) - m.Info("injecting input system") - m.AddSystem(&InputSystem{}) + m.Info("injecting timescale scene") + m.AddSystem(&TimeScaleSystem{}) - m.Info("injecting update counter system") - m.AddSystem(&UpdateCounter{}) + m.Info("injecting game object factory system") + m.AddSystem(&GameObjectFactory{}) m.Info("injecting loading scene") m.AddSystem(NewLoadingScene()) @@ -58,18 +58,20 @@ func (m *GameClientBootstrapSystem) injectSystems() { m.Info("injecting mouse cursor scene") m.AddSystem(NewMouseCursorScene()) + skipSplash := kingpin.Flag(skipSplashArg, skipSplashDesc).Bool() + kingpin.Parse() + + if !*skipSplash { + m.Info("injecting ebiten splash scene") + m.AddSystem(NewEbitenSplashScene()) + } + m.Info("injecting main menu scene") m.AddSystem(NewMainMenuScene()) - - m.Info("injecting terminal scene") - m.AddSystem(NewTerminalScene()) - - m.Info("injecting ebiten splash scene") - m.AddSystem(NewEbitenSplashScene()) } // Update does nothing, but exists to satisfy the `akara.System` interface -func (m *GameClientBootstrapSystem) Update() { +func (m *GameClientBootstrap) Update() { m.Info("game client bootstrap complete, deactivating") m.RemoveSystem(m) } diff --git a/d2core/d2systems/game_config.go b/d2core/d2systems/game_config.go index 71e01825..006bc189 100644 --- a/d2core/d2systems/game_config.go +++ b/d2core/d2systems/game_config.go @@ -24,9 +24,9 @@ const ( // to be found in, and it also adds an entity for the initial config file to be loaded. // // This system is dependant on the FileTypeResolver, FileSourceResolver, and -// FileHandleResolver baseSystems because this system subscribes to entities -// with components created by these other baseSystems. Nothing will break if these -// other baseSystems are not present in the world, but no config files will be loaded by +// FileHandleResolver sceneSystems because this system subscribes to entities +// with components created by these other sceneSystems. Nothing will break if these +// other sceneSystems are not present in the world, but no config files will be loaded by // this system either... type GameConfigSystem struct { akara.BaseSubscriberSystem diff --git a/d2core/d2systems/game_config_test.go b/d2core/d2systems/game_config_test.go index f3e2953c..abb1fdf3 100644 --- a/d2core/d2systems/game_config_test.go +++ b/d2core/d2systems/game_config_test.go @@ -26,7 +26,7 @@ func Test_GameConfigSystem_Bootstrap(t *testing.T) { cfgSys.AddFilePath(world.NewEntity()).Path = testDataPath cfgSys.AddFilePath(world.NewEntity()).Path = "config.json" - // at this point the world has initialized the baseSystems. when the world + // at this point the world has initialized the sceneSystems. when the world // updates it should process the config dir to a source and then // use the source to resolve a file handle, and finally the config file // will get loaded by the config system. diff --git a/d2core/d2systems/game_object_factory.go b/d2core/d2systems/game_object_factory.go index ff906f2d..23dd55f7 100644 --- a/d2core/d2systems/game_object_factory.go +++ b/d2core/d2systems/game_object_factory.go @@ -13,7 +13,7 @@ const ( // static check that GameObjectFactory implements the System interface var _ akara.System = &GameObjectFactory{} -// GameObjectFactory is a wrapper system for subordinate baseSystems that +// GameObjectFactory is a wrapper system for subordinate sceneSystems that // do the actual object creation work. type GameObjectFactory struct { akara.BaseSystem @@ -44,7 +44,7 @@ func (t *GameObjectFactory) injectSubSystems() { t.ShapeSystem = NewShapeSystem(t.BaseSystem, t.Logger) } -// Update updates all the sub-baseSystems +// Update updates all the sub-sceneSystems func (t *GameObjectFactory) Update() { t.SpriteFactory.Update() t.ShapeSystem.Update() diff --git a/d2core/d2systems/input_system.go b/d2core/d2systems/input_system.go index dd4aad19..1d5b495f 100644 --- a/d2core/d2systems/input_system.go +++ b/d2core/d2systems/input_system.go @@ -86,6 +86,8 @@ func (m *InputSystem) Update() { } func (m *InputSystem) updateInputState() { + m.inputState.Clear() + var keysToCheck = []d2input.Key{ d2input.Key0, d2input.Key1, d2input.Key2, d2input.Key3, d2input.Key4, d2input.Key5, d2input.Key6, d2input.Key7, d2input.Key8, d2input.Key9, d2input.KeyA, d2input.KeyB, d2input.KeyC, d2input.KeyD, @@ -118,17 +120,17 @@ func (m *InputSystem) updateInputState() { } for _, key := range keysToCheck { - truth := m.InputService.IsKeyPressed(d2enum.Key(key)) + truth := m.InputService.IsKeyJustPressed(d2enum.Key(key)) m.inputState.KeyVector.Set(key, truth) } for _, mod := range modifiersToCheck { - truth := m.InputService.IsKeyPressed(d2enum.Key(mod)) + truth := m.InputService.IsKeyJustPressed(d2enum.Key(mod)) m.inputState.ModifierVector.Set(mod, truth) } for _, btn := range buttonsToCheck { - truth := m.InputService.IsMouseButtonPressed(d2enum.MouseButton(btn)) + truth := m.InputService.IsMouseButtonJustPressed(d2enum.MouseButton(btn)) m.inputState.MouseButtonVector.Set(btn, truth) } } diff --git a/d2core/d2systems/movement.go b/d2core/d2systems/movement.go index f558b10f..81d7fe5d 100644 --- a/d2core/d2systems/movement.go +++ b/d2core/d2systems/movement.go @@ -21,7 +21,7 @@ var _ akara.System = &MovementSystem{} type MovementSystem struct { akara.BaseSubscriberSystem *d2util.Logger - d2components.PositionFactory + d2components.TransformFactory d2components.VelocityFactory movableEntities *akara.Subscription } @@ -35,11 +35,11 @@ func (m *MovementSystem) Init(world *akara.World) { m.Info("initializing ...") - m.InjectComponent(&d2components.Position{}, &m.Position) + m.InjectComponent(&d2components.Transform{}, &m.Transform) m.InjectComponent(&d2components.Velocity{}, &m.Velocity) movable := m.NewComponentFilter().Require( - &d2components.Position{}, + &d2components.Transform{}, &d2components.Velocity{}, ).Build() @@ -56,7 +56,7 @@ func (m *MovementSystem) Update() { } func (m *MovementSystem) move(id akara.EID) { - position, found := m.GetPosition(id) + transform, found := m.GetTransform(id) if !found { return } @@ -67,5 +67,5 @@ func (m *MovementSystem) move(id akara.EID) { } s := float64(m.World.TimeDelta) / float64(time.Second) - position.Add(velocity.Clone().Scale(s)) + transform.Translation.Add(velocity.Clone().Scale(s)) } diff --git a/d2core/d2systems/render.go b/d2core/d2systems/render.go index e2e9fe75..f8661854 100644 --- a/d2core/d2systems/render.go +++ b/d2core/d2systems/render.go @@ -40,6 +40,7 @@ type RenderSystem struct { d2components.TextureFactory d2components.PriorityFactory d2components.AlphaFactory + d2components.CameraFactory lastUpdate time.Time } @@ -69,6 +70,7 @@ func (m *RenderSystem) setupFactories() { m.InjectComponent(&d2components.Texture{}, &m.Texture) m.InjectComponent(&d2components.Priority{}, &m.Priority) m.InjectComponent(&d2components.Alpha{}, &m.Alpha) + m.InjectComponent(&d2components.Camera{}, &m.Camera) } func (m *RenderSystem) setupSubscriptions() { @@ -77,6 +79,7 @@ func (m *RenderSystem) setupSubscriptions() { &d2components.Viewport{}, &d2components.MainViewport{}, &d2components.Texture{}, + &d2components.Camera{}, ). Build() @@ -91,7 +94,7 @@ func (m *RenderSystem) setupSubscriptions() { // Update will initialize the renderer, start the game loop, and // disable the system (to prevent it from being called during the game loop). // -// The reason why this isn't in the init step is because we use other baseSystems +// The reason why this isn't in the init step is because we use other sceneSystems // for loading the config file, and it may take more than one iteration func (m *RenderSystem) Update() { if m.renderer != nil { @@ -108,7 +111,7 @@ func (m *RenderSystem) Update() { // this system and start the run loop. m.SetActive(false) - err := m.startGameLoop() + err := m.StartGameLoop() if err != nil { m.Fatal(err.Error()) } @@ -173,6 +176,11 @@ func (m *RenderSystem) render(screen d2interface.Surface) error { return errors.New("main viewport not found") } + cam, found := m.GetCamera(id) + if !found { + return errors.New("main viewport camera not found") + } + texture, found := m.GetTexture(id) if !found { return errors.New("main viewport doesn't have a surface") @@ -191,11 +199,13 @@ func (m *RenderSystem) render(screen d2interface.Surface) error { screen.PushColor(color.Alpha{A: uint8(alpha.Alpha * maxAlpha)}) screen.PushTranslation(vp.Left, vp.Top) + screen.PushScale(float64(vp.Width)/cam.Size.X, float64(vp.Height)/cam.Size.Y) screen.Render(texture.Texture) screen.Pop() screen.Pop() + screen.Pop() } return nil @@ -209,7 +219,7 @@ func (m *RenderSystem) updateWorld() error { return m.World.Update(elapsed) } -func (m *RenderSystem) startGameLoop() error { +func (m *RenderSystem) StartGameLoop() error { m.Infof("starting game loop ...") return m.renderer.Run(m.render, m.updateWorld, 800, 600, gameTitle) diff --git a/d2core/d2systems/scene_base.go b/d2core/d2systems/scene_base.go index 31aac6b9..52ec67ca 100644 --- a/d2core/d2systems/scene_base.go +++ b/d2core/d2systems/scene_base.go @@ -29,8 +29,7 @@ func NewBaseScene(key string) *BaseScene { Logger: d2util.NewLogger(), key: key, Viewports: make([]akara.EID, 0), - GameObjects: make([]akara.EID, 0), - baseSystems: &baseSystems{}, + SceneObjects: make([]akara.EID, 0), backgroundColor: color.Transparent, } @@ -41,20 +40,41 @@ func NewBaseScene(key string) *BaseScene { var _ akara.System = &BaseScene{} -type baseSystems struct { +type sceneSystems struct { *RenderSystem *InputSystem *GameObjectFactory } -// BaseScene encapsulates common behaviors for baseSystems that are considered "scenes", +type sceneComponents struct { + d2components.SceneGraphNodeFactory + d2components.ViewportFactory + d2components.MainViewportFactory + d2components.ViewportFilterFactory + d2components.PriorityFactory + d2components.CameraFactory + d2components.TextureFactory + d2components.InteractiveFactory + d2components.TransformFactory + d2components.SpriteFactory + d2components.OriginFactory + d2components.AlphaFactory + d2components.DrawEffectFactory + d2components.RectangleFactory + d2components.ColorFactory + d2components.CommandRegistrationFactory + d2components.DirtyFactory +} + +// BaseScene encapsulates common behaviors for systems that are considered "scenes", // such as the main menu, the in-game map, the console, etc. // // The base scene is responsible for generic behaviors common to all scenes, // like initializing the default viewport, or rendering game objects to the viewports. type BaseScene struct { *akara.BaseSystem - *baseSystems + sceneSystems + sceneComponents Geom struct { Rectangle rectangle.Namespace } @@ -64,27 +84,9 @@ type BaseScene struct { paused bool Add *sceneObjectFactory Viewports []akara.EID - GameObjects []akara.EID + SceneObjects []akara.EID Graph *d2scene.Node // the root node backgroundColor color.Color - d2components.SceneGraphNodeFactory - d2components.ViewportFactory - d2components.MainViewportFactory - d2components.ViewportFilterFactory - d2components.PriorityFactory - d2components.CameraFactory - d2components.TextureFactory - d2components.InteractiveFactory - d2components.PositionFactory - d2components.ScaleFactory - d2components.SpriteFactory - d2components.OriginFactory - d2components.AlphaFactory - d2components.DrawEffectFactory - d2components.RectangleFactory - d2components.ColorFactory - d2components.CommandRegistrationFactory - d2components.DirtyFactory } // Booted returns whether or not the scene has booted @@ -118,46 +120,46 @@ func (s *BaseScene) boot() { s.Add.SetPrefix(fmt.Sprintf("%s -> %s", s.key, "Object Factory")) for idx := range s.Systems { - if rendersys, ok := s.Systems[idx].(*RenderSystem); ok && s.baseSystems.RenderSystem == nil { - s.baseSystems.RenderSystem = rendersys + if rendersys, ok := s.Systems[idx].(*RenderSystem); ok && s.sceneSystems.RenderSystem == nil { + s.sceneSystems.RenderSystem = rendersys continue } - if inputSys, ok := s.Systems[idx].(*InputSystem); ok && s.baseSystems.InputSystem == nil { - s.baseSystems.InputSystem = inputSys + if inputSys, ok := s.Systems[idx].(*InputSystem); ok && s.sceneSystems.InputSystem == nil { + s.sceneSystems.InputSystem = inputSys continue } - if objFactory, ok := s.Systems[idx].(*GameObjectFactory); ok && s.baseSystems.GameObjectFactory == nil { - s.baseSystems.GameObjectFactory = objFactory + if objFactory, ok := s.Systems[idx].(*GameObjectFactory); ok && s.sceneSystems.GameObjectFactory == nil { + s.sceneSystems.GameObjectFactory = objFactory continue } } - if s.baseSystems.RenderSystem == nil { + if s.sceneSystems.RenderSystem == nil { s.Info("waiting for render system ...") return } - if s.baseSystems.RenderSystem.renderer == nil { + if s.sceneSystems.RenderSystem.renderer == nil { s.Info("waiting for renderer instance ...") return } - if s.baseSystems.InputSystem == nil { + if s.sceneSystems.InputSystem == nil { s.Info("waiting for input system") return } - if s.baseSystems.GameObjectFactory == nil { + if s.sceneSystems.GameObjectFactory == nil { s.Info("waiting for game object factory ...") return } s.setupFactories() - s.baseSystems.SpriteFactory.RenderSystem = s.baseSystems.RenderSystem - s.baseSystems.ShapeSystem.RenderSystem = s.baseSystems.RenderSystem + s.sceneSystems.SpriteFactory.RenderSystem = s.sceneSystems.RenderSystem + s.sceneSystems.ShapeSystem.RenderSystem = s.sceneSystems.RenderSystem const ( defaultWidth = 800 @@ -180,8 +182,7 @@ func (s *BaseScene) setupFactories() { s.InjectComponent(&d2components.Priority{}, &s.Priority) s.InjectComponent(&d2components.Texture{}, &s.Texture) s.InjectComponent(&d2components.Interactive{}, &s.Interactive) - s.InjectComponent(&d2components.Position{}, &s.Position) - s.InjectComponent(&d2components.Scale{}, &s.Scale) + s.InjectComponent(&d2components.Transform{}, &s.Transform) s.InjectComponent(&d2components.Origin{}, &s.Origin) s.InjectComponent(&d2components.Alpha{}, &s.Alpha) s.InjectComponent(&d2components.SceneGraphNode{}, &s.SceneGraphNode) @@ -208,17 +209,35 @@ func (s *BaseScene) Update() { return } - s.Graph.UpdateWorldMatrix() + s.updateSceneGraph() s.renderViewports() } +func (s *BaseScene) updateSceneGraph() { + for _, eid := range s.SceneObjects { + node, found := s.GetSceneGraphNode(eid) + if !found { + continue + } + + transform, found := s.GetTransform(eid) + if !found { + continue + } + + node.Local = transform.GetMatrix() + } + + s.Graph.UpdateWorldMatrix() +} + func (s *BaseScene) renderViewports() { - if s.baseSystems.RenderSystem == nil { + if s.sceneSystems.RenderSystem == nil { s.Warning("render system not present") return } - if s.baseSystems.RenderSystem.renderer == nil { + if s.sceneSystems.RenderSystem.renderer == nil { s.Warning("render system doesn't have a renderer instance") return } @@ -244,7 +263,7 @@ func (s *BaseScene) renderViewports() { func (s *BaseScene) binGameObjectsByViewport() map[int][]akara.EID { bins := make(map[int][]akara.EID) - for _, eid := range s.GameObjects { + for _, eid := range s.SceneObjects { vpfilter, found := s.GetViewportFilter(eid) if !found { vpfilter = s.AddViewportFilter(eid) @@ -267,116 +286,108 @@ func (s *BaseScene) binGameObjectsByViewport() map[int][]akara.EID { } func (s *BaseScene) renderViewport(idx int, objects []akara.EID) { - id := s.Viewports[idx] + viewportEID := s.Viewports[idx] // the first viewport is always the main viewport if idx == mainViewport { - s.AddMainViewport(id) + s.AddMainViewport(viewportEID) } else { - s.MainViewport.Remove(id) + s.MainViewport.Remove(viewportEID) } - camera, found := s.GetCamera(id) - if !found { + sfc, found := s.GetTexture(viewportEID) + if !found || sfc.Texture == nil { return } - node, found := s.GetSceneGraphNode(id) - if !found { - node = s.AddSceneGraphNode(id) - } - - // translate the camera position using the camera's scene graph node - cx, cy := camera.Position.Clone().ApplyMatrix4(node.Local).XY() - cw, ch := camera.Size.XY() - - sfc, found := s.GetTexture(id) - if !found { - return - } - - if sfc.Texture == nil { - sfc.Texture = s.baseSystems.RenderSystem.renderer.NewSurface(int(cw), int(ch)) - } - if idx == mainViewport { sfc.Texture.Clear(s.backgroundColor) } - sfc.Texture.PushTranslation(int(-cx), int(-cy)) // negative because we're offsetting everything that gets rendered - - for _, object := range objects { - s.renderObject(sfc.Texture, object) + for _, objectEID := range objects { + s.renderObject(viewportEID, objectEID) } - - sfc.Texture.Pop() } -func (s *BaseScene) renderObject(target d2interface.Surface, id akara.EID) { - texture, found := s.GetTexture(id) +func (s *BaseScene) renderObject(viewportEID, objectEID akara.EID) { + vpTexture, found := s.GetTexture(viewportEID) + if !found || vpTexture.Texture == nil { + return + } + + vpNode, found := s.GetSceneGraphNode(viewportEID) + if !found { + vpNode = s.AddSceneGraphNode(viewportEID) + } + + // translation, rotation, and scale vec3's + vpTrans, vpRot, vpScale := vpNode.Local.Invert().Decompose() + + objTexture, found := s.GetTexture(objectEID) if !found { return } - position, found := s.GetPosition(id) + + alpha, found := s.GetAlpha(objectEID) if !found { - position = s.AddPosition(id) + alpha = s.AddAlpha(objectEID) } - scale, found := s.GetScale(id) + origin, found := s.GetOrigin(objectEID) if !found { - scale = s.AddScale(id) + origin = s.AddOrigin(objectEID) } - alpha, found := s.GetAlpha(id) + node, found := s.GetSceneGraphNode(objectEID) if !found { - alpha = s.AddAlpha(id) - } - - origin, found := s.GetOrigin(id) - if !found { - origin = s.AddOrigin(id) - } - - node, found := s.GetSceneGraphNode(id) - if !found { - node = s.AddSceneGraphNode(id) + node = s.AddSceneGraphNode(objectEID) node.SetParent(s.Graph) } - drawEffect, found := s.GetDrawEffect(id) + drawEffect, found := s.GetDrawEffect(objectEID) if found { - target.PushEffect(drawEffect.DrawEffect) - defer target.Pop() + vpTexture.Texture.PushEffect(drawEffect.DrawEffect) + defer vpTexture.Texture.Pop() } - // translate the entity position using the scene graph node - x, y := position.Clone(). - Add(origin.Vector3). - ApplyMatrix4(node.Local). - XY() + objNode, found := s.GetSceneGraphNode(objectEID) + if !found { + objNode = s.AddSceneGraphNode(objectEID) + } - target.PushTranslation(int(x), int(y)) - defer target.Pop() + // translation, rotation, and scale vec3's + objTrans, objRot, objScale := objNode.Local.Decompose() - target.PushScale(scale.Clone().ApplyMatrix4(node.Local).XY()) - defer target.Pop() + ox, oy := origin.X, origin.Y + tx, ty := objTrans.Add(vpTrans).XY() + + vpTexture.Texture.PushTranslation(int(tx+ox), int(ty+oy)) + defer vpTexture.Texture.Pop() + + vpTexture.Texture.PushScale(objScale.Multiply(vpScale).XY()) + defer vpTexture.Texture.Pop() + + vpTexture.Texture.PushRotate(objRot.Add(vpRot).Z) + defer vpTexture.Texture.Pop() const maxAlpha = 255 - target.PushColor(color.Alpha{A: uint8(alpha.Alpha * maxAlpha)}) - defer target.Pop() + vpTexture.Texture.PushColor(color.Alpha{A: uint8(alpha.Alpha * maxAlpha)}) + defer vpTexture.Texture.Pop() - segment, found := s.baseSystems.SpriteFactory.GetSegmentedSprite(id) + segment, found := s.sceneSystems.SpriteFactory.GetSegmentedSprite(objectEID) if found { - s.renderSegmentedSprite(target, id, segment) + s.renderSegmentedSprite(vpTexture.Texture, objectEID, segment) return } - target.Render(texture.Texture) + vpTexture.Texture.Render(objTexture.Texture) } -func (s *BaseScene) renderSegmentedSprite(target d2interface.Surface, id akara.EID, seg *d2components.SegmentedSprite) { +func (s *BaseScene) renderSegmentedSprite(screen d2interface.Surface, id akara.EID, seg *d2components.SegmentedSprite) { + target := screen.Renderer().NewSurface(screen.GetSize()) + animation, found := s.GetSprite(id) if !found { return @@ -393,7 +404,7 @@ func (s *BaseScene) renderSegmentedSprite(target d2interface.Surface, id akara.E for x := 0; x < segmentsX; x++ { idx := x + y*segmentsX + frameOffset*segmentsX*segmentsY if err := animation.SetCurrentFrame(idx); err != nil { - s.Error("SetCurrentFrame error" + err.Error()) + s.Error("SetCurrentFrame error " + err.Error()) } target.PushTranslation(x+offsetX, y+offsetY) @@ -407,6 +418,8 @@ func (s *BaseScene) renderSegmentedSprite(target d2interface.Surface, id akara.E offsetY += maxFrameHeight - 1 } + + screen.Render(target) } func (s *BaseScene) renderViewportsToMainViewport() { diff --git a/d2core/d2systems/scene_ebiten_splash.go b/d2core/d2systems/scene_ebiten_splash.go index 52928654..86d8070c 100644 --- a/d2core/d2systems/scene_ebiten_splash.go +++ b/d2core/d2systems/scene_ebiten_splash.go @@ -1,11 +1,11 @@ package d2systems import ( + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2input" + "github.com/gravestench/akara" "image/color" "math" - "github.com/gravestench/akara" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" ) @@ -121,6 +121,20 @@ func (s *EbitenSplashScene) createSplash() { } } + interactive := s.AddInteractive(s.NewEntity()) + + interactive.InputVector.SetMouseButton(d2input.MouseButtonLeft) + + interactive.Callback = func() bool { + s.Info("hiding splash scene") + + s.timeElapsed = splashTimeout + + interactive.Enabled = false + + return true // prevent propagation + } + s.squares = squares } @@ -133,9 +147,11 @@ func (s *EbitenSplashScene) updateSplash() { // fade out after timeout if s.timeElapsed >= splashTimeout { vpAlpha, _ := s.GetAlpha(s.Viewports[0]) - vpAlpha.Alpha -= 0.1225 + vpAlpha.Alpha -= 0.0425 if vpAlpha.Alpha <= 0 { vpAlpha.Alpha = 0 + + s.Info("finished, deactivating") s.SetActive(false) } } @@ -145,7 +161,7 @@ func (s *EbitenSplashScene) updateSplash() { // fade all of the squares for idx, id := range s.squares { - a := math.Sin(s.timeElapsed + -90 + (float64(idx)/numSquares)) + a := math.Sin(s.timeElapsed*2 + -90 + (float64(idx)/numSquares)) a = (a+1)/2 // clamp between 0..1 alpha, found := s.GetAlpha(id) diff --git a/d2core/d2systems/scene_graph_testing.go b/d2core/d2systems/scene_graph_testing.go new file mode 100644 index 00000000..823891e4 --- /dev/null +++ b/d2core/d2systems/scene_graph_testing.go @@ -0,0 +1 @@ +package d2systems diff --git a/d2core/d2systems/scene_loading_screen.go b/d2core/d2systems/scene_loading_screen.go index 1118e6ef..37d013b9 100644 --- a/d2core/d2systems/scene_loading_screen.go +++ b/d2core/d2systems/scene_loading_screen.go @@ -234,7 +234,7 @@ func (s *LoadingScene) updateLoadingSpritePosition() { return } - position, found := s.GetPosition(s.loadingSprite) + transform, found := s.GetTransform(s.loadingSprite) if !found { return } @@ -243,7 +243,11 @@ func (s *LoadingScene) updateLoadingSpritePosition() { frameW, frameH := sprite.GetCurrentFrameSize() // we add the frameH in the Y because sprites are supposed to be drawn from bottom to top - position.X, position.Y = float64(centerX-(frameW/2)), float64(centerY+(frameH/2)) + transform.Translation.Set( + float64(centerX-(frameW/2)), + float64(centerY+(frameH/2)), + transform.Translation.Z, + ) } func (s *LoadingScene) updateLoadingSpriteFrame() { diff --git a/d2core/d2systems/scene_mouse_cursor.go b/d2core/d2systems/scene_mouse_cursor.go index 95b4d641..8c84e56d 100644 --- a/d2core/d2systems/scene_mouse_cursor.go +++ b/d2core/d2systems/scene_mouse_cursor.go @@ -38,6 +38,7 @@ type MouseCursorScene struct { debug struct { enabled bool } + test bool } func (s *MouseCursorScene) Init(world *akara.World) { @@ -77,32 +78,33 @@ func (s *MouseCursorScene) Update() { s.boot() } - s.updateCursorPosition() + s.updateCursorTransform() s.handleCursorFade() s.BaseScene.Update() } -func (s *MouseCursorScene) updateCursorPosition() { - position, found := s.GetPosition(s.cursor) +func (s *MouseCursorScene) updateCursorTransform() { + transform, found := s.GetTransform(s.cursor) if !found { return } cx, cy := s.CursorPosition() + tx, ty := transform.Translation.XY() - if int(position.X) != cx || int(position.Y) != cy { + if int(tx) != cx || int(ty) != cy { s.lastTimeMoved = time.Now() switch s.debug.enabled { case true: - s.Infof("position: (%d, %d)", int(position.X), int(position.Y)) + s.Infof("transform: (%d, %d)", int(tx), int(ty)) default: - s.Debugf("position: (%d, %d)", int(position.X), int(position.Y)) + s.Debugf("transform: (%d, %d)", int(tx), int(ty)) } } - position.X, position.Y = float64(cx), float64(cy) + transform.Translation.X, transform.Translation.Y = float64(cx), float64(cy) } func (s *MouseCursorScene) handleCursorFade() { diff --git a/d2core/d2systems/scene_object_factory.go b/d2core/d2systems/scene_object_factory.go index 4d11d36e..45fd7492 100644 --- a/d2core/d2systems/scene_object_factory.go +++ b/d2core/d2systems/scene_object_factory.go @@ -26,8 +26,8 @@ func (s *sceneObjectFactory) addBasicComponents(id akara.EID) { func (s *sceneObjectFactory) Sprite(x, y float64, imgPath, palPath string) akara.EID { s.Debugf("creating sprite: %s, %s", filepath.Base(imgPath), palPath) - eid := s.baseSystems.SpriteFactory.Sprite(x, y, imgPath, palPath) - s.GameObjects = append(s.GameObjects, eid) + eid := s.sceneSystems.SpriteFactory.Sprite(x, y, imgPath, palPath) + s.SceneObjects = append(s.SceneObjects, eid) s.addBasicComponents(eid) @@ -37,8 +37,8 @@ func (s *sceneObjectFactory) Sprite(x, y float64, imgPath, palPath string) akara func (s *sceneObjectFactory) SegmentedSprite(x, y float64, imgPath, palPath string, xseg, yseg, frame int) akara.EID { s.Debugf("creating segmented sprite: %s, %s", filepath.Base(imgPath), palPath) - eid := s.baseSystems.SpriteFactory.SegmentedSprite(x, y, imgPath, palPath, xseg, yseg, frame) - s.GameObjects = append(s.GameObjects, eid) + eid := s.sceneSystems.SpriteFactory.SegmentedSprite(x, y, imgPath, palPath, xseg, yseg, frame) + s.SceneObjects = append(s.SceneObjects, eid) s.addBasicComponents(eid) @@ -60,7 +60,7 @@ func (s *sceneObjectFactory) Viewport(priority, width, height int) akara.EID { camera.Size.X = float64(width) camera.Size.Y = float64(height) - sfc := s.baseSystems.RenderSystem.renderer.NewSurface(width, height) + sfc := s.sceneSystems.RenderSystem.renderer.NewSurface(width, height) sfc.Clear(color.Transparent) @@ -73,17 +73,17 @@ func (s *sceneObjectFactory) Viewport(priority, width, height int) akara.EID { return eid } -func (s *sceneObjectFactory) Rectangle(x, y, width, height int, color color.Color) akara.EID { +func (s *sceneObjectFactory) Rectangle(x, y, width, height int, c color.Color) akara.EID { s.Debug("creating rectangle") - eid := s.baseSystems.ShapeSystem.Rectangle(x, y, width, height, color) + eid := s.sceneSystems.ShapeSystem.Rectangle(x, y, width, height, c) s.addBasicComponents(eid) - position := s.AddPosition(eid) - position.X, position.Y = float64(x), float64(y) + transform := s.AddTransform(eid) + transform.Translation.X, transform.Translation.Y = float64(x), float64(y) - s.GameObjects = append(s.GameObjects, eid) + s.SceneObjects = append(s.SceneObjects, eid) return eid } diff --git a/d2core/d2systems/scene_render_priority.go b/d2core/d2systems/scene_render_priority.go index 42d62679..f3e30d6d 100644 --- a/d2core/d2systems/scene_render_priority.go +++ b/d2core/d2systems/scene_render_priority.go @@ -3,7 +3,7 @@ package d2systems const ( scenePriorityMainMenu = iota scenePriorityLoading - scenePriorityMouseCursor scenePriorityTerminal + scenePriorityMouseCursor scenePriorityEbitenSplash ) diff --git a/d2core/d2systems/scene_shape_system.go b/d2core/d2systems/scene_shape_system.go index 35845985..cc20a4c8 100644 --- a/d2core/d2systems/scene_shape_system.go +++ b/d2core/d2systems/scene_shape_system.go @@ -29,7 +29,7 @@ type ShapeSystem struct { akara.BaseSubscriberSystem *d2util.Logger RenderSystem *RenderSystem - d2components.PositionFactory + d2components.TransformFactory d2components.ColorFactory d2components.RectangleFactory d2components.TextureFactory @@ -54,7 +54,7 @@ func (t *ShapeSystem) Init(world *akara.World) { func (t *ShapeSystem) setupFactories() { t.InjectComponent(&d2components.Color{}, &t.ColorFactory.Color) - t.InjectComponent(&d2components.Position{}, &t.PositionFactory.Position) + t.InjectComponent(&d2components.Transform{}, &t.TransformFactory.Transform) t.InjectComponent(&d2components.Texture{}, &t.TextureFactory.Texture) t.InjectComponent(&d2components.Origin{}, &t.OriginFactory.Origin) t.InjectComponent(&d2components.Size{}, &t.SizeFactory.Size) @@ -69,7 +69,7 @@ func (t *ShapeSystem) setupSubscriptions() { shapesToUpdate := t.NewComponentFilter(). RequireOne(&d2components.Rectangle{}). - Require(&d2components.Position{}, &d2components.Size{}). + Require(&d2components.Transform{}, &d2components.Size{}). Build() t.shapesToRender = t.AddSubscription(shapesToRender) @@ -109,7 +109,7 @@ func (t *ShapeSystem) Rectangle(x, y, width, height int, color color.Color) akar } func (t *ShapeSystem) updateShape(eid akara.EID) { - position, found := t.GetPosition(eid) + transform, found := t.GetTransform(eid) if !found { return } @@ -126,7 +126,7 @@ func (t *ShapeSystem) updateShape(eid akara.EID) { rectangle, rectangleFound := t.GetRectangle(eid) if rectangleFound { - position.X, position.Y = rectangle.X, rectangle.Y + transform.Translation.X, transform.Translation.Y = rectangle.X, rectangle.Y size.X, size.Y = rectangle.Width, rectangle.Height tw, th := texture.Texture.GetSize() diff --git a/d2core/d2systems/scene_sprite_system.go b/d2core/d2systems/scene_sprite_system.go index a341ad2d..cc97747d 100644 --- a/d2core/d2systems/scene_sprite_system.go +++ b/d2core/d2systems/scene_sprite_system.go @@ -40,7 +40,7 @@ type SpriteFactory struct { *d2util.Logger RenderSystem *RenderSystem d2components.FilePathFactory - d2components.PositionFactory + d2components.TransformFactory d2components.Dc6Factory d2components.DccFactory d2components.PaletteFactory @@ -67,7 +67,7 @@ func (t *SpriteFactory) Init(world *akara.World) { func (t *SpriteFactory) setupFactories() { t.InjectComponent(&d2components.FilePath{}, &t.FilePath) - t.InjectComponent(&d2components.Position{}, &t.Position) + t.InjectComponent(&d2components.Transform{}, &t.Transform) t.InjectComponent(&d2components.Dc6{}, &t.Dc6) t.InjectComponent(&d2components.Dcc{}, &t.Dcc) t.InjectComponent(&d2components.Palette{}, &t.Palette) @@ -112,8 +112,8 @@ func (t *SpriteFactory) Update() { func (t *SpriteFactory) Sprite(x, y float64, imgPath, palPath string) akara.EID { spriteID := t.NewEntity() - position := t.AddPosition(spriteID) - position.X, position.Y = x, y + transform := t.AddTransform(spriteID) + transform.Translation.X, transform.Translation.Y = x, y imgID, palID := t.NewEntity(), t.NewEntity() t.AddFilePath(imgID).Path = imgPath diff --git a/d2core/d2systems/timescale.go b/d2core/d2systems/timescale.go index a354802a..091b648f 100644 --- a/d2core/d2systems/timescale.go +++ b/d2core/d2systems/timescale.go @@ -1,6 +1,7 @@ package d2systems import ( + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2components" "time" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2util" @@ -16,18 +17,6 @@ const ( logPrefixTimeScaleSystem = "Time Scale" ) -// NewTimeScaleSystem creates a timescale system -func NewTimeScaleSystem() *TimeScaleSystem { - m := &TimeScaleSystem{ - BaseSystem: &akara.BaseSystem{}, - Logger: d2util.NewLogger(), - } - - m.SetPrefix(logPrefixTimeScaleSystem) - - return m -} - // static check that TimeScaleSystem implements the System interface var _ akara.System = &TimeScaleSystem{} @@ -35,29 +24,50 @@ var _ akara.System = &TimeScaleSystem{} // apply a scalar the world's TimeDelta between frames. It's useful for slowing down or speeding // up the game time without affecting the render rate. type TimeScaleSystem struct { - *akara.BaseSystem + akara.BaseSystem *d2util.Logger scale float64 - lastScale float64 + d2components.DirtyFactory + d2components.CommandRegistrationFactory } // Init will initialize the TimeScale system func (t *TimeScaleSystem) Init(world *akara.World) { t.World = world + t.Logger = d2util.NewLogger() + t.SetPrefix(logPrefixTimeScaleSystem) + t.Info("initializing ...") + t.InjectComponent(&d2components.CommandRegistration{}, &t.CommandRegistration) + t.InjectComponent(&d2components.Dirty{}, &t.Dirty) + + t.registerCommands() + t.scale = defaultScale } // Update scales the worlds time delta for this frame func (t *TimeScaleSystem) Update() { - if !t.Active() || t.scale == t.lastScale { + if !t.Active() { return } - t.Infof("setting time scale to %.1f", t.scale) - t.lastScale = t.scale - - t.World.TimeDelta *= time.Duration(t.scale) + t.World.TimeDelta = time.Duration(float64(t.World.TimeDelta) * t.scale) +} + +func (t *TimeScaleSystem) registerCommands() { + e := t.NewEntity() + + reg := t.AddCommandRegistration(e) + + t.AddDirty(e) + + reg.Name = "timescale" + reg.Description = "set the time scale of the game (default is 1.0)" + reg.Callback = func(scale float64) { + t.Infof("setting time scale to %.1f", scale) + t.scale = scale + } } diff --git a/go.mod b/go.mod index b3733d25..bc0155f0 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/alecthomas/units v0.0.0-20201120081800-1786d5ef83d4 // indirect github.com/go-restruct/restruct v1.2.0-alpha github.com/google/uuid v1.1.2 - github.com/gravestench/akara v0.0.0-20201203202918-85b8a01d1130 + github.com/gravestench/akara v0.0.0-20201206061149-9be03b4110f2 github.com/gravestench/pho v0.0.0-20201029002250-f9afbd637e4d github.com/hajimehoshi/ebiten/v2 v2.0.1 github.com/pkg/profile v1.5.0 diff --git a/go.sum b/go.sum index feab778f..c54ca7dc 100644 --- a/go.sum +++ b/go.sum @@ -21,6 +21,8 @@ github.com/gravestench/akara v0.0.0-20201203202116-00c2a0dbae5d h1:8xg2B0KGOHLwp github.com/gravestench/akara v0.0.0-20201203202116-00c2a0dbae5d/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ= github.com/gravestench/akara v0.0.0-20201203202918-85b8a01d1130 h1:09fkM2hfORgZJ5hhtHzOMszOgx3Xs14+z7kpto8w7Ys= github.com/gravestench/akara v0.0.0-20201203202918-85b8a01d1130/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ= +github.com/gravestench/akara v0.0.0-20201206061149-9be03b4110f2 h1:mOIIK6AgIyaEslKsu+tsguzFWaMLGjlMuUKqOlABhGk= +github.com/gravestench/akara v0.0.0-20201206061149-9be03b4110f2/go.mod h1:fTeda1SogMg5Lkd4lXMEd/Pk/a5/gQuLGaAI2rn1PBQ= github.com/gravestench/pho v0.0.0-20201029002250-f9afbd637e4d h1:CP+/y9SAdv9LifYvicxYdQNmzugykEahAiUhYolMROM= github.com/gravestench/pho v0.0.0-20201029002250-f9afbd637e4d/go.mod h1:yi5GHMLLWtHhs9tz3q1csUlgGKz5MhZoJcxV8NFBtkk= github.com/hajimehoshi/bitmapfont/v2 v2.1.0/go.mod h1:2BnYrkTQGThpr/CY6LorYtt/zEPNzvE/ND69CRTaHMs= diff --git a/main.go b/main.go index fa508a63..81a195a8 100644 --- a/main.go +++ b/main.go @@ -1,45 +1,7 @@ package main -import ( - "log" - - "github.com/gravestench/akara" - "gopkg.in/alecthomas/kingpin.v2" - - "github.com/OpenDiablo2/OpenDiablo2/d2app" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2systems" -) - -// GitBranch is set by the CI build process to the name of the branch -//nolint:gochecknoglobals // This is filled in by the build system -var GitBranch string - -// GitCommit is set by the CI build process to the commit hash -//nolint:gochecknoglobals // This is filled in by the build system -var GitCommit string +import "github.com/OpenDiablo2/OpenDiablo2/d2app" func main() { - ecs := kingpin.Flag("ecs", "start the ecs implementation").Bool() - kingpin.Parse() - - if *ecs { - cfg := akara.NewWorldConfig() - - cfg. - With(&d2systems.AppBootstrapSystem{}). - With(&d2systems.GameClientBootstrapSystem{}) - - akara.NewWorld(cfg) - - return - } - - log.SetFlags(log.Lshortfile) - log.Println("OpenDiablo2 - Open source Diablo 2 engine") - - instance := d2app.Create(GitBranch, GitCommit) - - if err := instance.Run(); err != nil { - return - } + d2app.Run() }