From 1d30efc07254863f4464952a35164bb2a7ae421c Mon Sep 17 00:00:00 2001 From: Futility Date: Mon, 29 Jan 2018 21:29:25 -0500 Subject: [PATCH 01/37] Created an interface for things with CIDs. Moved in and updated a simple set of UI button functions to an x package for entities. This btn package is still being ironed out. --- entities/x/btn/box.go | 36 ++++++++ entities/x/btn/btn.go | 13 +++ entities/x/btn/button.go | 173 ++++++++++++++++++++++++++++++++++++++ entities/x/btn/group.go | 12 +++ entities/x/btn/option.go | 169 +++++++++++++++++++++++++++++++++++++ entities/x/btn/text.go | 30 +++++++ entities/x/btn/textBox.go | 77 +++++++++++++++++ event/caller.go | 12 +++ 8 files changed, 522 insertions(+) create mode 100644 entities/x/btn/box.go create mode 100644 entities/x/btn/btn.go create mode 100644 entities/x/btn/button.go create mode 100644 entities/x/btn/group.go create mode 100644 entities/x/btn/option.go create mode 100644 entities/x/btn/text.go create mode 100644 entities/x/btn/textBox.go create mode 100644 event/caller.go diff --git a/entities/x/btn/box.go b/entities/x/btn/box.go new file mode 100644 index 00000000..17a4b02b --- /dev/null +++ b/entities/x/btn/box.go @@ -0,0 +1,36 @@ +package btn + +import ( + "github.com/oakmound/oak/entities" + "github.com/oakmound/oak/event" + "github.com/oakmound/oak/mouse" + "github.com/oakmound/oak/render" +) + +// Box is a basic implementation of btn +type Box struct { + entities.Solid + mouse.CollisionPhase +} + +// NewBox creates a new btn.box +func NewBox(cid event.CID, x, y, w, h float64, r render.Renderable, layers ...int) *Box { + b := Box{} + cid = cid.Parse(&b) + b.Solid = *entities.NewSolid(x, y, w, h, r, mouse.DefTree, cid) + if b.R != nil && len(layers) > 0 { + render.Draw(b.R, layers...) + } + return &b +} + +// Init intializes the btn.box +func (b *Box) Init() event.CID { + b.CID = event.NextID(b) + return b.CID +} + +// GetRenderable returns the box's renderable +func (b *Box) GetRenderable() render.Renderable { + return b.R +} diff --git a/entities/x/btn/btn.go b/entities/x/btn/btn.go new file mode 100644 index 00000000..89e5425a --- /dev/null +++ b/entities/x/btn/btn.go @@ -0,0 +1,13 @@ +package btn + +import ( + "github.com/oakmound/oak/event" + "github.com/oakmound/oak/render" +) + +// Btn defines a button for use in the UI +type Btn interface { + event.Caller + render.Positional + GetRenderable() render.Renderable +} diff --git a/entities/x/btn/button.go b/entities/x/btn/button.go new file mode 100644 index 00000000..7fdceb77 --- /dev/null +++ b/entities/x/btn/button.go @@ -0,0 +1,173 @@ +package btn + +import ( + "image/color" + "strconv" + + "github.com/oakmound/oak/event" + "github.com/oakmound/oak/mouse" + "github.com/oakmound/oak/render" + "github.com/oakmound/oak/render/mod" +) + +// A Generator defines the variables used to create buttons from optional arguments +type Generator struct { + X, Y float64 + W, H float64 + TxtX, TxtY float64 + Color color.Color + Color2 color.Color + ProgressFunc func(x, y, w, h int) float64 + Mod mod.Transform + R1 render.Modifiable + R2 render.Modifiable + RS []render.Modifiable + Cid event.CID + Font *render.Font + Layer int + Text string + // This should be a map + Binding event.Bindable + Trigger string + Toggle *bool + ListChoice *int + Group *Group +} + +var ( + // A number of these fields could be removed, because they are the zero + // value, but are left for documentation + defaultGenerator = Generator{ + X: 0, + Y: 0, + W: 1, + H: 1, + TxtX: 0, + TxtY: 0, + Color: color.RGBA{255, 0, 0, 255}, + Mod: nil, + R1: nil, + R2: nil, + + Cid: 0, + Font: nil, + Layer: 0, + Text: "Button", + Binding: nil, + Trigger: "MouseClickOn", + + Toggle: nil, + } +) + +// Generate creates a Button from a generator. +func (g Generator) Generate() *TextBox { + var box render.Modifiable + if g.Toggle != nil { + //Handles checks and other toggle situations + start := "on" + if !(*g.Toggle) { + start = "off" + } + box = render.NewSwitch(start, map[string]render.Modifiable{ + "on": g.R1, + "off": g.R2, + }) + g.Binding = func(id int, nothing interface{}) int { + btn := event.GetEntity(id).(Btn) + if btn.GetRenderable().(*render.Switch).Get() == "on" { + if g.Group != nil && g.Group.active == btn { + g.Group.active = nil + } + btn.GetRenderable().(*render.Switch).Set("off") + } else { + // We can pull this out to seperate binding if group != nil + if g.Group != nil { + g.Group.active = btn + for _, b := range g.Group.members { + if b.GetRenderable().(*render.Switch).Get() == "on" { + b.Trigger("MouseClickOn", nil) + } + } + } + btn.GetRenderable().(*render.Switch).Set("on") + + } + *g.Toggle = !*g.Toggle + + return 0 + } + g.Trigger = "MouseClickOn" + } else if g.ListChoice != nil { + + start := "list" + strconv.Itoa(*g.ListChoice) + mp := make(map[string]render.Modifiable) + for i, r := range g.RS { + mp["list"+strconv.Itoa(i)] = r + } + box = render.NewSwitch(start, mp) + + g.Binding = func(id int, button interface{}) int { + btn := event.GetEntity(id).(*TextBox) + i := *g.ListChoice + mEvent := button.(mouse.Event) + + if mEvent.Button == "LeftMouse" { + i++ + if i == len(g.RS) { + i = 0 + } + + } else if mEvent.Button == "RightMouse" { + i-- + if i < 0 { + i += len(g.RS) + } + } + + btn.R.(*render.Switch).Set("list" + strconv.Itoa(i)) + + *g.ListChoice = i + + return 0 + } + g.Trigger = "MouseClickOn" + } else if g.ProgressFunc != nil { + box = render.NewGradientBox(int(g.W), int(g.H), g.Color, g.Color2, g.ProgressFunc) + } else { + box = render.NewColorBox(int(g.W), int(g.H), g.Color) + } + + if g.Mod != nil { + box.Modify(g.Mod) + } + font := g.Font + if font == nil { + font = render.DefFont() + } + // Todo: if no string is defined, don't do this + btn := NewTextBox(g.Cid, g.X, g.Y, g.W, g.H, g.TxtX, g.TxtY, font, box, g.Layer) + btn.SetString(g.Text) + + if g.Binding != nil { + btn.Bind(g.Binding, g.Trigger) + } + + if g.Group != nil { + g.Group.members = append(g.Group.members, btn) + } + + return btn +} + +// An Option is used to populate generator fields prior to generation of a button +type Option func(Generator) Generator + +// New creates a button with the given options and defaults for all variables not set. +func New(opts ...Option) *TextBox { + g := defaultGenerator + for _, opt := range opts { + g = opt(g) + } + return g.Generate() +} diff --git a/entities/x/btn/group.go b/entities/x/btn/group.go new file mode 100644 index 00000000..9213dd82 --- /dev/null +++ b/entities/x/btn/group.go @@ -0,0 +1,12 @@ +package btn + +// Group links several btns together +type Group struct { + members []Btn + active Btn +} + +// GetActive returns the active btn from the group +func (g *Group) GetActive() Btn { + return g.active +} diff --git a/entities/x/btn/option.go b/entities/x/btn/option.go new file mode 100644 index 00000000..3ace77e5 --- /dev/null +++ b/entities/x/btn/option.go @@ -0,0 +1,169 @@ +package btn + +import ( + "image/color" + + "github.com/oakmound/oak/event" + "github.com/oakmound/oak/render" + "github.com/oakmound/oak/render/mod" +) + +// And combines a variadic number of options +func And(opts ...Option) Option { + return func(g Generator) Generator { + for _, opt := range opts { + g = opt(g) + } + return g + } +} + +//Width sets the Width of the button to be generated +func Width(w float64) Option { + return func(g Generator) Generator { + g.W = w + return g + } +} + +//Height sets the Height of the button to be generated +func Height(h float64) Option { + return func(g Generator) Generator { + g.H = h + return g + } +} + +//Pos sets the position of the button to be generated +func Pos(x, y float64) Option { + return func(g Generator) Generator { + g.X = x + g.Y = y + return g + } +} + +//TxtOff sets the text offset of the button generator from the bottom left +func TxtOff(x, y float64) Option { + return func(g Generator) Generator { + g.TxtX = x + g.TxtY = y + return g + } +} + +//CID sets the starting CID of the button to be generated +func CID(c event.CID) Option { + return func(g Generator) Generator { + g.Cid = c + return g + } +} + +//Color sets the colorboxes color for the button to be generated +func Color(c color.Color) Option { + return func(g Generator) Generator { + g.Color = c + return g + } +} + +// VGradient creates a vertical color gradient for the btn +func VGradient(c1, c2 color.Color) Option { + return func(g Generator) Generator { + g.Color = c1 + g.Color2 = c2 + g.ProgressFunc = render.VerticalProgress + return g + } +} + +//Mod sets the modifications to apply to the initial color box for the button to be generated +func Mod(m mod.Transform) Option { + return func(g Generator) Generator { + g.Mod = m + return g + } +} + +// AndMod combines the input modification with whatever existing modifications +// exist for the generator, as opposed to Mod which resets previous modifications. +func AndMod(m mod.Transform) Option { + return func(g Generator) Generator { + if g.Mod == nil { + g.Mod = m + } else { + g.Mod = mod.And(g.Mod, m) + } + return g + } +} + +//Font sets the font for the text of the button to be generated +func Font(f *render.Font) Option { + return func(g Generator) Generator { + g.Font = f + return g + } +} + +//Layer sets the layer of the button to be generated +func Layer(l int) Option { + return func(g Generator) Generator { + g.Layer = l + return g + } +} + +//Text sets the text of the button to be generated +func Text(s string) Option { + return func(g Generator) Generator { + g.Text = s + return g + } +} + +// Toggle sets that the type of the button toggles between two +// modifiables when it is clicked. The boolean behind isChecked +// is updated according to the state of the button. +func Toggle(r1, r2 render.Modifiable, isChecked *bool) Option { + return func(g Generator) Generator { + g.R1 = r1.Copy() + g.R2 = r2.Copy() + g.Toggle = isChecked + return g + } +} + +// ToggleGroup sets the group that this button is linked with +func ToggleGroup(gr *Group) Option { + return func(g Generator) Generator { + g.Group = gr + return g + } +} + +// ToggleList sets the togglable choices for a button +func ToggleList(chosen *int, rs ...render.Modifiable) Option { + return func(g Generator) Generator { + g.ListChoice = chosen + g.RS = rs + return g + } +} + +//Binding sets the Binding of the button to be generated +func Binding(bnd event.Bindable) Option { + return func(g Generator) Generator { + g.Binding = bnd + return g + } +} + +//Trigger sets the trigger for the Binding on the button to be generated +func Trigger(s string) Option { + return func(g Generator) Generator { + g.Trigger = s + return g + } +} diff --git a/entities/x/btn/text.go b/entities/x/btn/text.go new file mode 100644 index 00000000..5d80b681 --- /dev/null +++ b/entities/x/btn/text.go @@ -0,0 +1,30 @@ +package btn + +import ( + "fmt" + + "github.com/oakmound/oak/entities" + + "github.com/oakmound/oak/render" +) + +// NewText creates some uitext +func NewText(f *render.Font, str string, x, y float64, layers ...int) *entities.Doodad { + d := entities.NewDoodad(x, y, f.NewStrText(str, x, y), 0) + render.Draw(d.R, layers...) + return d +} + +// NewIntText creates some uitext from an integer +func NewIntText(f *render.Font, str *int, x, y float64, layers ...int) *entities.Doodad { + d := entities.NewDoodad(x, y, f.NewIntText(str, x, y), 0) + render.Draw(d.R, layers...) + return d +} + +// NewRawText creates some uitext from a stringer +func NewRawText(f *render.Font, str fmt.Stringer, x, y float64, layers ...int) *entities.Doodad { + d := entities.NewDoodad(x, y, f.NewText(str, x, y), 0) + render.Draw(d.R, layers...) + return d +} diff --git a/entities/x/btn/textBox.go b/entities/x/btn/textBox.go new file mode 100644 index 00000000..bf562548 --- /dev/null +++ b/entities/x/btn/textBox.go @@ -0,0 +1,77 @@ +package btn + +import ( + "github.com/oakmound/oak/collision" + "github.com/oakmound/oak/event" + "github.com/oakmound/oak/mouse" + "github.com/oakmound/oak/render" +) + +// TextBox is a Box with an associated text element +type TextBox struct { + Box + *render.Text +} + +// Init creates the CID +func (b *TextBox) Init() event.CID { + b.CID = event.NextID(b) + return b.CID +} + +// NewTextBox creates a textbox +func NewTextBox(cid event.CID, x, y, w, h, txtX, txtY float64, + f *render.Font, r render.Renderable, layers ...int) *TextBox { + + b := new(TextBox) + + cid = cid.Parse(b) + + b.Box = *NewBox(cid, x, y, w, h, r, layers...) + b.Text = f.NewStrText("Init", 0, 0) + b.Text.Vector = b.Text.Attach(b.Box.Vector, txtX, (-txtY)+b.H) + + // Add one to the layer so that the text shows up above the box itself + layers[len(layers)-1]++ + render.Draw(b.Text, layers...) + return b +} + +// Y pulls the y of the composed Box (disambiguation with the y of the text component) +func (b *TextBox) Y() float64 { + return b.Box.Y() +} + +// X pulls the x of the composed Box (disambiguation with the x of the text component) +func (b *TextBox) X() float64 { + return b.Box.X() +} + +// ShiftX shifts the box by x. The associated text is attached and so will be moved along by default +func (b *TextBox) ShiftX(x float64) { + b.Box.ShiftX(x) +} + +// ShiftY shifts the box by y. The associated text is attached and so will be moved along by default +func (b *TextBox) ShiftY(y float64) { + b.Box.ShiftY(y) +} + +// SetSpace overwrites entities.Solid, +// pointing this button to use the mouse collision Rtree +// instead of the entity collision space. +func (b *TextBox) SetSpace(sp *collision.Space) { + mouse.Remove(b.Space) + b.Space = sp + mouse.Add(b.Space) +} + +// SetPos acts as SetSpace does, overwriting entities.Solid. +func (b *TextBox) SetPos(x, y float64) { + b.Box.SetPos(x, y) +} + +// SetOffsets changes the text position within the box +func (b *TextBox) SetOffsets(txtX, txtY float64) { + b.Text.Vector = b.Text.Attach(b.Box.Vector, txtX, -txtY+b.H) +} diff --git a/event/caller.go b/event/caller.go new file mode 100644 index 00000000..8e514940 --- /dev/null +++ b/event/caller.go @@ -0,0 +1,12 @@ +package event + +// Caller can bind and trigger events +type Caller interface { + Trigger(string, interface{}) + Bind(Bindable, string) + BindPriority(Bindable, string, int) + UnbindAll() + UnbindAllAndRebind([]Bindable, []string) + E() interface{} + Parse(Entity) CID +} From 1531867f1fa365e14f16f8517eb710c5f7dbabd8 Mon Sep 17 00:00:00 2001 From: Futility Date: Mon, 29 Jan 2018 22:08:33 -0500 Subject: [PATCH 02/37] Updating buttons to be cleaner. Starting support for nested buttons and inheritance. Plans to support cascading style settings. --- entities/x/btn/button.go | 21 +++++++++++++-------- entities/x/btn/group.go | 8 ++++++++ entities/x/btn/option.go | 33 --------------------------------- entities/x/btn/textBox.go | 4 ++++ entities/x/btn/textOptions.go | 28 ++++++++++++++++++++++++++++ entities/x/btn/tree.go | 9 +++++++++ 6 files changed, 62 insertions(+), 41 deletions(-) create mode 100644 entities/x/btn/textOptions.go create mode 100644 entities/x/btn/tree.go diff --git a/entities/x/btn/button.go b/entities/x/btn/button.go index 7fdceb77..27e50d33 100644 --- a/entities/x/btn/button.go +++ b/entities/x/btn/button.go @@ -26,6 +26,7 @@ type Generator struct { Font *render.Font Layer int Text string + Children []Generator // This should be a map Binding event.Bindable Trigger string @@ -49,19 +50,23 @@ var ( R1: nil, R2: nil, - Cid: 0, - Font: nil, - Layer: 0, - Text: "Button", - Binding: nil, - Trigger: "MouseClickOn", + Children: []Generator{}, + Cid: 0, + Font: nil, + Layer: 0, + Text: "Button", + Binding: nil, + Trigger: "MouseClickOn", Toggle: nil, } ) // Generate creates a Button from a generator. -func (g Generator) Generate() *TextBox { +func (g Generator) Generate() Btn { + return g.generate(nil) +} +func (g Generator) generate(parent *Generator) Btn { var box render.Modifiable if g.Toggle != nil { //Handles checks and other toggle situations @@ -164,7 +169,7 @@ func (g Generator) Generate() *TextBox { type Option func(Generator) Generator // New creates a button with the given options and defaults for all variables not set. -func New(opts ...Option) *TextBox { +func New(opts ...Option) Btn { g := defaultGenerator for _, opt := range opts { g = opt(g) diff --git a/entities/x/btn/group.go b/entities/x/btn/group.go index 9213dd82..e8bfcc24 100644 --- a/entities/x/btn/group.go +++ b/entities/x/btn/group.go @@ -10,3 +10,11 @@ type Group struct { func (g *Group) GetActive() Btn { return g.active } + +// ToggleGroup sets the group that this button is linked with +func ToggleGroup(gr *Group) Option { + return func(g Generator) Generator { + g.Group = gr + return g + } +} diff --git a/entities/x/btn/option.go b/entities/x/btn/option.go index 3ace77e5..3d68d5ed 100644 --- a/entities/x/btn/option.go +++ b/entities/x/btn/option.go @@ -43,15 +43,6 @@ func Pos(x, y float64) Option { } } -//TxtOff sets the text offset of the button generator from the bottom left -func TxtOff(x, y float64) Option { - return func(g Generator) Generator { - g.TxtX = x - g.TxtY = y - return g - } -} - //CID sets the starting CID of the button to be generated func CID(c event.CID) Option { return func(g Generator) Generator { @@ -99,14 +90,6 @@ func AndMod(m mod.Transform) Option { } } -//Font sets the font for the text of the button to be generated -func Font(f *render.Font) Option { - return func(g Generator) Generator { - g.Font = f - return g - } -} - //Layer sets the layer of the button to be generated func Layer(l int) Option { return func(g Generator) Generator { @@ -115,14 +98,6 @@ func Layer(l int) Option { } } -//Text sets the text of the button to be generated -func Text(s string) Option { - return func(g Generator) Generator { - g.Text = s - return g - } -} - // Toggle sets that the type of the button toggles between two // modifiables when it is clicked. The boolean behind isChecked // is updated according to the state of the button. @@ -135,14 +110,6 @@ func Toggle(r1, r2 render.Modifiable, isChecked *bool) Option { } } -// ToggleGroup sets the group that this button is linked with -func ToggleGroup(gr *Group) Option { - return func(g Generator) Generator { - g.Group = gr - return g - } -} - // ToggleList sets the togglable choices for a button func ToggleList(chosen *int, rs ...render.Modifiable) Option { return func(g Generator) Generator { diff --git a/entities/x/btn/textBox.go b/entities/x/btn/textBox.go index bf562548..84842ba2 100644 --- a/entities/x/btn/textBox.go +++ b/entities/x/btn/textBox.go @@ -23,6 +23,10 @@ func (b *TextBox) Init() event.CID { func NewTextBox(cid event.CID, x, y, w, h, txtX, txtY float64, f *render.Font, r render.Renderable, layers ...int) *TextBox { + if f == nil { + f = render.DefFont() + } + b := new(TextBox) cid = cid.Parse(b) diff --git a/entities/x/btn/textOptions.go b/entities/x/btn/textOptions.go new file mode 100644 index 00000000..732c559e --- /dev/null +++ b/entities/x/btn/textOptions.go @@ -0,0 +1,28 @@ +package btn + +import "github.com/oakmound/oak/render" + +//Text sets the text of the button to be generated +func Text(s string) Option { + return func(g Generator) Generator { + g.Text = s + return g + } +} + +//Font sets the font for the text of the button to be generated +func Font(f *render.Font) Option { + return func(g Generator) Generator { + g.Font = f + return g + } +} + +//TxtOff sets the text offset of the button generator from the bottom left +func TxtOff(x, y float64) Option { + return func(g Generator) Generator { + g.TxtX = x + g.TxtY = y + return g + } +} diff --git a/entities/x/btn/tree.go b/entities/x/btn/tree.go new file mode 100644 index 00000000..b3e65c6b --- /dev/null +++ b/entities/x/btn/tree.go @@ -0,0 +1,9 @@ +package btn + +// AddChildren adds a generator to create a child btn +func AddChildren(cg ...Generator) Option { + return func(g Generator) Generator { + g.Children = append(g.Children, cg...) + return g + } +} From 080b5ad30b22fa83f2aef7ad374921957ec6f750 Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Tue, 30 Jan 2018 16:39:37 -0600 Subject: [PATCH 03/37] Some additional utilities to the debug console and scene shorthands --- debugConsole.go | 6 ++++++ render/text.go | 2 +- scene/scene.go | 23 ++++++++++++++++++++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/debugConsole.go b/debugConsole.go index de4d4140..1892e72d 100644 --- a/debugConsole.go +++ b/debugConsole.go @@ -39,6 +39,12 @@ func AddCommand(s string, fn func([]string)) error { return nil } +// ResetCommands will throw out all existing debug commands from the +// debug console. +func ResetCommands() { + commands = map[string]func([]string){} +} + func debugConsole(resetCh, skipScene chan bool, input io.Reader) { scanner := bufio.NewScanner(input) spew.Config.DisableMethods = true diff --git a/render/text.go b/render/text.go index dbbb87dd..fa7a1239 100644 --- a/render/text.go +++ b/render/text.go @@ -145,7 +145,7 @@ func (t *Text) Wrap(charLimit int, vertInc float64) []*Text { // ToSprite converts this text into a sprite, so that it is no longer // modifiable in terms of its text content, but is modifiable in terms -// of Modifications. +// of mod.Transform or mod.Filter. func (t *Text) ToSprite() *Sprite { width := t.d.MeasureString(t.text.String()).Round() height := t.d.bounds.Max.Y() diff --git a/scene/scene.go b/scene/scene.go index 37be4698..c99015d5 100644 --- a/scene/scene.go +++ b/scene/scene.go @@ -23,6 +23,27 @@ type Start func(prevScene string, data interface{}) // should continue to loop. type Loop func() bool -// End is a function returning the next scene and a SceneResult of +// End is a function returning the next scene and a SceneResult of // input settings for the next scene. type End func() (string, *Result) + +// BooleanLoop returns a Loop function that will end a scene as soon as the +// input boolean is false, resetting it to true in the process for the +// next scene +func BooleanLoop(b *bool) Loop { + return func() bool { + if !(*b) { + *b = true + return false + } + return true + } +} + +// GoTo returns an End function that, without any other customization possible, +// will change to the input next scene. +func GoTo(nextScene string) End { + return func() (string, *Result) { + return nextScene, nil + } +} From 3abbf0367a8eab9d2c509a5d2ea9cba4b239c47f Mon Sep 17 00:00:00 2001 From: Futility Date: Mon, 5 Feb 2018 21:56:45 -0500 Subject: [PATCH 04/37] Updating x package's buttons so that they can use multiple layers. --- entities/x/btn/button.go | 6 +++--- entities/x/btn/option.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/entities/x/btn/button.go b/entities/x/btn/button.go index 27e50d33..2a5159d4 100644 --- a/entities/x/btn/button.go +++ b/entities/x/btn/button.go @@ -24,7 +24,7 @@ type Generator struct { RS []render.Modifiable Cid event.CID Font *render.Font - Layer int + Layers []int Text string Children []Generator // This should be a map @@ -53,7 +53,7 @@ var ( Children: []Generator{}, Cid: 0, Font: nil, - Layer: 0, + Layers: []int{0}, Text: "Button", Binding: nil, Trigger: "MouseClickOn", @@ -151,7 +151,7 @@ func (g Generator) generate(parent *Generator) Btn { font = render.DefFont() } // Todo: if no string is defined, don't do this - btn := NewTextBox(g.Cid, g.X, g.Y, g.W, g.H, g.TxtX, g.TxtY, font, box, g.Layer) + btn := NewTextBox(g.Cid, g.X, g.Y, g.W, g.H, g.TxtX, g.TxtY, font, box, g.Layers...) btn.SetString(g.Text) if g.Binding != nil { diff --git a/entities/x/btn/option.go b/entities/x/btn/option.go index 3d68d5ed..942b5555 100644 --- a/entities/x/btn/option.go +++ b/entities/x/btn/option.go @@ -90,10 +90,10 @@ func AndMod(m mod.Transform) Option { } } -//Layer sets the layer of the button to be generated -func Layer(l int) Option { +//Layers sets the layer of the button to be generated +func Layers(ls ...int) Option { return func(g Generator) Generator { - g.Layer = l + g.Layers = ls return g } } From 8fc99a54a0603217234ead29f195040087fb7e1e Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Wed, 14 Feb 2018 23:05:42 -0600 Subject: [PATCH 05/37] Moving over some extensions to render/mod in entities/x/mods --- entities/x/mods/dir.go | 28 ++++++ entities/x/mods/highlight.go | 165 +++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 entities/x/mods/dir.go create mode 100644 entities/x/mods/highlight.go diff --git a/entities/x/mods/dir.go b/entities/x/mods/dir.go new file mode 100644 index 00000000..fa89528d --- /dev/null +++ b/entities/x/mods/dir.go @@ -0,0 +1,28 @@ +package mods + +import "github.com/oakmound/oak/alg/intgeom" + +type Dir intgeom.Point2 + +var ( + Up = Dir(intgeom.Point2{0, -1}) + Down = Dir(intgeom.Point2{0, 1}) + Left = Dir(intgeom.Point2{-1, 0}) + Right = Dir(intgeom.Point2{1, 0}) + UpRight = Up.And(Right) + DownRight = Down.And(Right) + DownLeft = Down.And(Left) + UpLeft = Up.And(Left) +) + +func (d Dir) And(d2 Dir) Dir { + return Dir(intgeom.Point2(d).Add(intgeom.Point2(d2))) +} + +func (d Dir) X() int { + return intgeom.Point2(d).X() +} + +func (d Dir) Y() int { + return intgeom.Point2(d).Y() +} diff --git a/entities/x/mods/highlight.go b/entities/x/mods/highlight.go new file mode 100644 index 00000000..25e02be9 --- /dev/null +++ b/entities/x/mods/highlight.go @@ -0,0 +1,165 @@ +package mods + +import ( + "image" + "image/color" + + "github.com/oakmound/oak/render/mod" +) + +func HighlightOff(c color.Color, thickness, xOff, yOff int) mod.Mod { + return func(img image.Image) *image.RGBA { + bds := img.Bounds() + + w := bds.Max.X + thickness*2 + xOff + h := bds.Max.Y + thickness*2 + yOff + + newRgba := image.NewRGBA(image.Rect(0, 0, w, h)) + highlight := image.NewRGBA(image.Rect(0, 0, w, h)) + + for x := thickness; x < w-thickness; x++ { + for y := thickness; y < h-thickness; y++ { + newRgba.Set(x, y, img.At(x-thickness, y-thickness)) + } + } + for x := thickness; x < w-thickness; x++ { + for y := thickness; y < h-thickness; y++ { + if _, _, _, a := newRgba.At(x, y).RGBA(); a > 0 { + for x2 := x - thickness; x2 <= x+thickness; x2++ { + for y2 := y - thickness; y2 <= y+thickness; y2++ { + highlight.Set(x2, y2, c) + } + } + } + } + } + for x := 0; x < w; x++ { + for y := 0; y < h; y++ { + hc := highlight.At(x, y) + if _, _, _, a := hc.RGBA(); a != 0 { + if _, _, _, a2 := newRgba.At(x+xOff, y+yOff).RGBA(); a2 == 0 { + newRgba.Set(x+xOff, y+yOff, hc) + } + } + } + } + return newRgba + } +} + +func InnerHighlightOff(c color.Color, thickness, xOff, yOff int) mod.Mod { + return func(img image.Image) *image.RGBA { + bds := img.Bounds() + + w := bds.Max.X + h := bds.Max.Y + + newRgba := image.NewRGBA(image.Rect(0, 0, w, h)) + highlight := image.NewRGBA(image.Rect(0, 0, w, h)) + + for x := thickness; x < w-thickness; x++ { + for y := thickness; y < h-thickness; y++ { + newRgba.Set(x, y, img.At(x-thickness, y-thickness)) + } + } + for x := thickness; x < w-thickness; x++ { + for y := thickness; y < h-thickness; y++ { + if _, _, _, a := newRgba.At(x, y).RGBA(); a == 0 { + for x2 := x - thickness; x2 <= x+thickness; x2++ { + for y2 := y - thickness; y2 <= y+thickness; y2++ { + highlight.Set(x2, y2, c) + } + } + } + } + } + for x := 0; x < w; x++ { + for y := 0; y < h; y++ { + hc := highlight.At(x, y) + if _, _, _, a := hc.RGBA(); a != 0 { + if _, _, _, a2 := newRgba.At(x+xOff, y+yOff).RGBA(); a2 != 0 { + newRgba.Set(x+xOff, y+yOff, hc) // todo overlay instead + } + } + } + } + return newRgba + } +} + +func InnerHighlight(c color.Color, thickness int) mod.Mod { + return InnerHighlightOff(c, thickness, 0, 0) +} + +func Highlight(c color.Color, thickness int) mod.Mod { + return HighlightOff(c, thickness, 0, 0) +} + +type Filter func(color.Color) color.Color + +func Inset(fn Filter, dir Dir) mod.Mod { + return func(img image.Image) *image.RGBA { + bds := img.Bounds() + + w := bds.Max.X + h := bds.Max.Y + + newRgba := image.NewRGBA(image.Rect(0, 0, w, h)) + + for x := 0; x < w; x++ { + for y := 0; y < h; y++ { + // todo: depth + _, _, _, a := img.At(x+dir.X(), y+dir.Y()).RGBA() + if a == 0 { + newRgba.Set(x, y, fn(img.At(x, y))) + } else { + newRgba.Set(x, y, img.At(x, y)) + } + } + } + return newRgba + } +} + +// Darker produces a darker color by f percentage (0 to 1) difference +func Darker(c color.Color, f float64) color.Color { + r, g, b, a := c.RGBA() + diff := uint32(65535 * f) + r -= diff + g -= diff + b -= diff + // Don't touch alpha + if r > 65535 { + r = 0 + } + if g > 65535 { + g = 0 + } + if b > 65535 { + b = 0 + } + return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} +} + +// Fade produces a color with more transparency by f percentage (0 to 1) +func Fade(c color.Color, f float64) color.Color { + r, g, b, a := c.RGBA() + diff := uint32(65535 * f) + r -= diff + g -= diff + b -= diff + a -= diff + if r > 65535 { + r = 0 + } + if g > 65535 { + g = 0 + } + if b > 65535 { + b = 0 + } + if a > 65535 { + a = 0 + } + return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)} +} From 784b1fd61d35e8103f40457a1ff5c48a15fecb4d Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Fri, 16 Feb 2018 07:44:07 -0600 Subject: [PATCH 06/37] Add OnStop as an event when the engine closes --- event/handler.go | 6 ++++++ event/strings.go | 10 ++++++++++ inputLoop.go | 7 +++++++ 3 files changed, 23 insertions(+) diff --git a/event/handler.go b/event/handler.go index 7983fea1..8666fc45 100644 --- a/event/handler.go +++ b/event/handler.go @@ -25,6 +25,12 @@ type Handler interface { Trigger(event string, data interface{}) } +// A FullHandler will receive TriggerBack events from the engine +// when sent (currently only OnStop, when the engine closes) +type FullHandler interface { + TriggerBack(event string, data interface{}) chan bool +} + // UpdateLoop is expected to internally call Update() // or do something equivalent at the given frameRate, // sending signals to the sceneCh after each Update(). diff --git a/event/strings.go b/event/strings.go index 52452234..8dc680c7 100644 --- a/event/strings.go +++ b/event/strings.go @@ -28,4 +28,14 @@ const ( // ViewportUpdate: Triggered when the position fo of the viewport changes // Payload: []float64{viewportX, viewportY} ViewportUpdate = "ViewportUpdate" + // OnStop: Triggered when the engine is stopped. + // Payload: nil + OnStop = "OnStop" ) + +// +// Note all events built in to oak are CapitalizedCamelCase. Although our adding of new +// built in events is rare, we don't consider the addition of these events breaking +// changes for versioning. If a game has many events with generalized names, making +// them uncapitalizedCamelCase is perhaps the best approach to guarantee that builtin +// event names will never conflict with custom events. diff --git a/inputLoop.go b/inputLoop.go index 8443bb73..e32c8020 100644 --- a/inputLoop.go +++ b/inputLoop.go @@ -3,6 +3,8 @@ package oak import ( "runtime" + "github.com/oakmound/oak/event" + "github.com/oakmound/oak/dlog" okey "github.com/oakmound/oak/key" omouse "github.com/oakmound/oak/mouse" @@ -35,6 +37,11 @@ func inputLoop() { // We only currently respond to death lifecycle events. case lifecycle.Event: if e.To == lifecycle.StageDead { + // OnStop needs to be sent through TriggerBack, otherwise the + // program will close before the stop events get propagated. + if fh, ok := logicHandler.(event.FullHandler); ok { + fh.TriggerBack(event.OnStop, nil) + } quitCh <- true return } From 47388c222ce1d5166ff2aa03a76fcbff03cc8fb0 Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Sun, 4 Mar 2018 20:18:50 -0600 Subject: [PATCH 07/37] Add a log when commands are added to the debug console --- debugConsole.go | 1 + 1 file changed, 1 insertion(+) diff --git a/debugConsole.go b/debugConsole.go index 1892e72d..831d9859 100644 --- a/debugConsole.go +++ b/debugConsole.go @@ -35,6 +35,7 @@ func AddCommand(s string, fn func([]string)) error { Overwritten: false, } } + dlog.Info("Adding command", s) commands[s] = fn return nil } From 7da25c8a4284c69b3ea0462cfb049e15f33a3c2a Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Mon, 2 Apr 2018 21:08:05 -0500 Subject: [PATCH 08/37] Better arg names for drawColor, add drawPoint --- render/draw.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/render/draw.go b/render/draw.go index cc4eee68..3e9b1059 100644 --- a/render/draw.go +++ b/render/draw.go @@ -33,13 +33,19 @@ func LoadSpriteAndDraw(filename string, layers ...int) (Renderable, error) { // DrawColor is equivalent to LoadSpriteAndDraw, // but with colorboxes. -func DrawColor(c color.Color, x1, y1, x2, y2 float64, layers ...int) (Renderable, error) { - cb := NewColorBox(int(x2), int(y2), c) - cb.ShiftX(x1) - cb.ShiftY(y1) +func DrawColor(c color.Color, x, y, w, h float64, layers ...int) (Renderable, error) { + cb := NewColorBox(int(w), int(h), c) + cb.ShiftX(x) + cb.ShiftY(y) return Draw(cb, layers...) } +// DrawPoint draws a color on the screen as a single-widthed +// pixel (box) +func DrawPoint(c color.Color, x1, y1 float64, layers ...int) (Renderable, error) { + return DrawColor(c, x1, y1, 1, 1, layers...) +} + // DrawForTime draws and after d undraws an element func DrawForTime(r Renderable, d time.Duration, layers ...int) error { _, err := Draw(r, layers...) From d786d637a2357ae0ec28f0e1fd4ebaa676f93ee6 Mon Sep 17 00:00:00 2001 From: Futility Date: Sat, 7 Apr 2018 13:28:56 -0400 Subject: [PATCH 09/37] Added Cross for Point3s and made Rectangles able to be shifted by some point value. --- alg/floatgeom/point.go | 5 +++++ alg/floatgeom/rect.go | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/alg/floatgeom/point.go b/alg/floatgeom/point.go index ec360a40..c7d09c89 100644 --- a/alg/floatgeom/point.go +++ b/alg/floatgeom/point.go @@ -160,6 +160,11 @@ func (p Point2) MulConst(fs ...float64) Point2 { return p } +// Cross gets the cross product of two Point 3s +func (p Point3) Cross(p2 Point3) Point3 { + return Point3{p.Y()*p2.Z() - p.Z()*p2.Y(), p.Z()*p2.X() - p.X()*p2.Z(), p.X()*p2.Y() - p.Y()*p2.X()} +} + // Div combines the input points via division. // Div does not check that the inputs are non zero before operating, // and can panic if that is not true. diff --git a/alg/floatgeom/rect.go b/alg/floatgeom/rect.go index fb528320..d3566cbe 100644 --- a/alg/floatgeom/rect.go +++ b/alg/floatgeom/rect.go @@ -115,6 +115,20 @@ func NewBoundingRect3(pts ...Point3) Rect3 { } } +// Shift moves the rectangle by a point returns a new instance +func (r Rect2) Shift(p Point2) Rect2 { + r.Min = r.Min.Add(p) + r.Max = r.Max.Add(p) + return r +} + +// Shift moves the rectangle by a point returns a new instance +func (r Rect3) Shift(p Point3) Rect3 { + r.Min = r.Min.Add(p) + r.Max = r.Max.Add(p) + return r +} + // Area returns W * H. func (r Rect2) Area() float64 { return r.W() * r.H() From f65a0a4277127418a419f92cb27618ca401b75e3 Mon Sep 17 00:00:00 2001 From: Futility Date: Sat, 14 Apr 2018 12:50:02 -0400 Subject: [PATCH 10/37] Added a new function for shape that returns points adjacent to a shape in addition to the holes inside of the shape itself. --- shape/holes.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/shape/holes.go b/shape/holes.go index 747a25b6..383da4d9 100644 --- a/shape/holes.go +++ b/shape/holes.go @@ -7,7 +7,19 @@ import ( // GetHoles finds sets of points which are not In this shape that // are adjacent. func GetHoles(sh Shape, w, h int) [][]intgeom.Point2 { + return getHoles(sh, w, h, false) +} + +// GetBorderHoles finds sets of points which are not In this shape that +// are adjacent in addition to the space around the shape +// (ie points that border the shape) +func GetBorderHoles(sh Shape, w, h int) [][]intgeom.Point2 { + return getHoles(sh, w, h, true) +} +// getHoles is an internal function that finds sets of points which are not In this shape that +// are adjacent. +func getHoles(sh Shape, w, h int, includeBorder bool) [][]intgeom.Point2 { flooding := make(map[intgeom.Point2]bool) for x := 0; x < w; x++ { @@ -17,18 +29,17 @@ func GetHoles(sh Shape, w, h int) [][]intgeom.Point2 { } } } - - border := borderPoints(w, h) - - for _, p := range border { - if !sh.In(p.X(), p.Y()) { - bfsFlood(flooding, p) + if !includeBorder { + border := borderPoints(w, h) + for _, p := range border { + if !sh.In(p.X(), p.Y()) { + bfsFlood(flooding, p) + } } + // flooding is now a map of holes, points which are false + // but not on the border. } - // flooding is now a map of holes, points which are false - // but not on the border. - out := make([][]intgeom.Point2, 0) for len(flooding) > 0 { From d125c4aa673456cf7aa76c0f54ba43928f2c19a5 Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Mon, 16 Apr 2018 20:32:09 -0500 Subject: [PATCH 11/37] Add the Center method to floatgeom Rects --- alg/floatgeom/rect.go | 17 +++++++++++++++++ alg/intgeom/rect.go | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/alg/floatgeom/rect.go b/alg/floatgeom/rect.go index d3566cbe..8bd07644 100644 --- a/alg/floatgeom/rect.go +++ b/alg/floatgeom/rect.go @@ -184,6 +184,23 @@ func (r Rect3) Midpoint(i int) float64 { return (r.Min[i] + r.Max[i]) / 2 } +// Center returns the center of this rectangle +func (r Rect2) Center() Point2 { + return Point2{ + r.Midpoint(0), + r.Midpoint(1), + } +} + +// Center returns the center of this rectangle +func (r Rect3) Center() Point3 { + return Point3{ + r.Midpoint(0), + r.Midpoint(1), + r.Midpoint(2), + } +} + // Perimeter computes the sum of the edge lengths of a rectangle. func (r Rect2) Perimeter() float64 { // The number of edges in an n-dimensional rectangle is n * 2^(n-1) diff --git a/alg/intgeom/rect.go b/alg/intgeom/rect.go index 386a4fa7..f7535205 100644 --- a/alg/intgeom/rect.go +++ b/alg/intgeom/rect.go @@ -170,6 +170,23 @@ func (r Rect3) Midpoint(i int) int { return (r.Min[i] + r.Max[i]) / 2 } +// Center returns the center of this rectangle. +func (r Rect2) Center() Point2 { + return Point2{ + r.Midpoint(0), + r.Midpoint(1), + } +} + +// Center returns the center of this rectangle. +func (r Rect3) Center() Point3 { + return Point3{ + r.Midpoint(0), + r.Midpoint(1), + r.Midpoint(2), + } +} + // Perimeter computes the sum of the edge lengths of a rectangle. func (r Rect2) Perimeter() int { // The number of edges in an n-dimensional rectangle is n * 2^(n-1) From 9faec3642147417b892e532061f7eb913fe13f86 Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Mon, 16 Apr 2018 21:01:50 -0500 Subject: [PATCH 12/37] More docs on NearestNeighbors --- collision/rtree.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/collision/rtree.go b/collision/rtree.go index 536ea880..2e0367e9 100644 --- a/collision/rtree.go +++ b/collision/rtree.go @@ -489,6 +489,8 @@ func (tree *Rtree) nearestNeighbor(p floatgeom.Point3, n *node, d float64, neare } // NearestNeighbors returns the k nearest neighbors in the rtree to the input point. +// Returns a slice of exactly k elements, some of which will be nil if there are not +// enough elements in the tree. Todo: fix this func (tree *Rtree) NearestNeighbors(k int, p floatgeom.Point3) []*Space { dists := make([]float64, k) objs := make([]*Space, k) From 8aced11101c2f1d3a6784119fa3ec0c5c322a584 Mon Sep 17 00:00:00 2001 From: Futility Date: Sat, 21 Apr 2018 12:44:45 -0400 Subject: [PATCH 13/37] Pulled in changes for rtree to make nearestNeighbors return an appropriately sized slice rather than being filled with nil entries when there are insufficient elements. --- collision/rtree.go | 57 +++++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/collision/rtree.go b/collision/rtree.go index 2e0367e9..8bf9a169 100644 --- a/collision/rtree.go +++ b/collision/rtree.go @@ -464,6 +464,16 @@ func pruneEntries(p floatgeom.Point3, entries []entry, minDists []float64) []ent return pruned } +func pruneEntriesMinDist(d float64, entries []entry, minDists []float64) []entry { + var i int + for ; i < len(entries); i++ { + if minDists[i] > d { + break + } + } + return entries[:i] +} + func (tree *Rtree) nearestNeighbor(p floatgeom.Point3, n *node, d float64, nearest *Space) (*Space, float64) { if n.leaf { for _, e := range n.entries { @@ -488,54 +498,53 @@ func (tree *Rtree) nearestNeighbor(p floatgeom.Point3, n *node, d float64, neare return nearest, d } -// NearestNeighbors returns the k nearest neighbors in the rtree to the input point. -// Returns a slice of exactly k elements, some of which will be nil if there are not -// enough elements in the tree. Todo: fix this +// NearestNeighbors returns the k nearest neighbors in the rtree to the input point func (tree *Rtree) NearestNeighbors(k int, p floatgeom.Point3) []*Space { - dists := make([]float64, k) - objs := make([]*Space, k) - for i := 0; i < k; i++ { - dists[i] = math.MaxFloat64 - objs[i] = nil - } + dists := make([]float64, 0, k) + objs := make([]*Space, 0, k) objs, _ = tree.nearestNeighbors(k, p, tree.root, dists, objs) return objs } // insert obj into nearest and return the first k elements in increasing order. func insertNearest(k int, dists []float64, nearest []*Space, dist float64, obj *Space) ([]float64, []*Space) { - i := 0 - for i < k && dist >= dists[i] { + i := sort.SearchFloat64s(dists, dist) + for i < len(nearest) && dist >= dists[i] { i++ } if i >= k { return dists, nearest } - left, right := dists[:i], dists[i:k-1] - updatedDists := make([]float64, k) - copy(updatedDists, left) - updatedDists[i] = dist - copy(updatedDists[i+1:], right) + if len(nearest) < k { + dists = append(dists, 0) + nearest = append(nearest, nil) + } + + left, right := dists[:i], dists[i:len(dists)-1] + copy(dists, left) + copy(dists[i+1:], right) + dists[i] = dist - leftObjs, rightObjs := nearest[:i], nearest[i:k-1] - updatedNearest := make([]*Space, k) - copy(updatedNearest, leftObjs) - updatedNearest[i] = obj - copy(updatedNearest[i+1:], rightObjs) + leftObjs, rightObjs := nearest[:i], nearest[i:len(nearest)-1] + copy(nearest, leftObjs) + copy(nearest[i+1:], rightObjs) + nearest[i] = obj - return updatedDists, updatedNearest + return dists, nearest } func (tree *Rtree) nearestNeighbors(k int, p floatgeom.Point3, n *node, dists []float64, nearest []*Space) ([]*Space, []float64) { if n.leaf { for _, e := range n.entries { - dist := math.Sqrt(minDist(p, e.bb)) + dist := minDist(p, e.bb) dists, nearest = insertNearest(k, dists, nearest, dist, e.obj) } } else { branches, branchDists := sortEntries(p, n.entries) - branches = pruneEntries(p, branches, branchDists) + if l := len(dists); l >= k { + branches = pruneEntriesMinDist(dists[l-1], branches, branchDists) + } for _, e := range branches { nearest, dists = tree.nearestNeighbors(k, p, e.child, dists, nearest) } From 6ed4c1a30c3fa1e88dae4e372e70f892d5576518 Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Mon, 23 Apr 2018 19:46:30 -0500 Subject: [PATCH 14/37] Add a surface normal calculation function for floatgeom.Triangle --- alg/floatgeom/triangle.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/alg/floatgeom/triangle.go b/alg/floatgeom/triangle.go index 1eb2fb2b..38b10a43 100644 --- a/alg/floatgeom/triangle.go +++ b/alg/floatgeom/triangle.go @@ -22,3 +22,15 @@ func (t Tri3) Barycentric(x, y float64) Point3 { u := 1.0 - v - w return Point3{v, w, u} } + +// Normal calculates the surface normal of a triangle +func (t Tri3) Normal() Point3 { + u := t[1].Sub(t[0]) + v := t[2].Sub(t[0]) + + return Point3{ + (u.Z() * v.Y()) - (u.Y() * v.Z()), + (u.X() * v.Z()) - (u.Z() * v.X()), + (u.Y() * v.X()) - (u.X() * v.Y()), + } +} From 62a15b1e01407ffea5d7740d4e32b1c048976e98 Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Mon, 23 Apr 2018 20:36:24 -0500 Subject: [PATCH 15/37] Trying another tri3 normal implementation --- alg/floatgeom/triangle.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/alg/floatgeom/triangle.go b/alg/floatgeom/triangle.go index 38b10a43..0ce35a3c 100644 --- a/alg/floatgeom/triangle.go +++ b/alg/floatgeom/triangle.go @@ -29,8 +29,8 @@ func (t Tri3) Normal() Point3 { v := t[2].Sub(t[0]) return Point3{ - (u.Z() * v.Y()) - (u.Y() * v.Z()), - (u.X() * v.Z()) - (u.Z() * v.X()), - (u.Y() * v.X()) - (u.X() * v.Y()), - } + (u.Y() * v.Z()) - (u.Z() * v.Y()), + (u.Z() * v.X()) - (u.X() * v.Z()), + (u.X() * v.Y()) - (u.Y() * v.X()), + }.Normalize() } From 34d4cdab883c82e50ec766c2e2a30ea13167ffac Mon Sep 17 00:00:00 2001 From: Futility Date: Sat, 28 Apr 2018 13:55:39 -0400 Subject: [PATCH 16/37] Added a condense function for shapes. This returns a set of rectangles that cover the shape without overlapping. Added a new type of shape that is an arbitrary set of points. --- shape/condense.go | 96 +++++++++++++++++++++++++++++++++++++++++++++++ shape/points.go | 34 +++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 shape/condense.go create mode 100644 shape/points.go diff --git a/shape/condense.go b/shape/condense.go new file mode 100644 index 00000000..2a6d1973 --- /dev/null +++ b/shape/condense.go @@ -0,0 +1,96 @@ +package shape + +import "github.com/oakmound/oak/alg/intgeom" + +// Condense finds a set of rectangles that covers the shape. Used to return a minimal set of rectangles in an appropriate time. +func Condense(sh Shape, w, h int) []intgeom.Rect2 { + condensed := []intgeom.Rect2{} + remainingSpaces := make(map[intgeom.Point2]struct{}) + + for x := 0; x < w; x++ { + for y := 0; y < h; y++ { + if sh.In(x, y) { + remainingSpaces[intgeom.Point2{x, y}] = struct{}{} + } + } + } + + for k := range remainingSpaces { + topLeft := k + w := 0 + h := 0 + right := true + left := true + up := true + down := true + xIncrement := intgeom.Point2{1, 0} + yIncrement := intgeom.Point2{0, 1} + xDecrement := intgeom.Point2{-1, 0} + yDecrement := intgeom.Point2{0, -1} + for right || left || up || down { + var toCheck intgeom.Point2 + if right { + toCheck = topLeft.Add(intgeom.Point2{w + 1, 0}) + for i := 0; i <= h; i++ { + if _, ok := remainingSpaces[toCheck]; !ok { + right = false + break + } + toCheck = toCheck.Add(yIncrement) + } + if right { + w++ + } + } + if left { + toCheck = topLeft.Add(intgeom.Point2{-1, 0}) + for i := 0; i <= h; i++ { + if _, ok := remainingSpaces[toCheck]; !ok { + left = false + break + } + toCheck = toCheck.Add(yIncrement) + } + if left { + w++ + topLeft = topLeft.Add(xDecrement) + } + } + if up { + toCheck = topLeft.Add(intgeom.Point2{0, -1}) + for i := 0; i <= w; i++ { + if _, ok := remainingSpaces[toCheck]; !ok { + up = false + break + } + toCheck = toCheck.Add(xIncrement) + } + if up { + h++ + topLeft = topLeft.Add(yDecrement) + } + } + if down { + toCheck = topLeft.Add(intgeom.Point2{0, h + 1}) + for i := 0; i <= w; i++ { + if _, ok := remainingSpaces[toCheck]; !ok { + down = false + break + } + toCheck = toCheck.Add(xIncrement) + } + if down { + h++ + } + } + + } + condensed = append(condensed, intgeom.NewRect2WH(topLeft.X(), topLeft.Y(), w, h)) + for x := topLeft.X(); x <= topLeft.X()+w; x++ { + for y := topLeft.Y(); y <= topLeft.Y()+h; y++ { + delete(remainingSpaces, intgeom.Point2{x, y}) + } + } + } + return condensed +} diff --git a/shape/points.go b/shape/points.go new file mode 100644 index 00000000..3229cf05 --- /dev/null +++ b/shape/points.go @@ -0,0 +1,34 @@ +package shape + +import ( + "github.com/oakmound/oak/alg/intgeom" +) + +// Points is a shape defined by a set of points. It ignores input width and height given to it as it only cares about its points. +type Points map[intgeom.Point2]struct{} + +// NewPoints creates a Points shape from any number of intgeom Points +func NewPoints(ps ...intgeom.Point2) Shape { + points := make(map[intgeom.Point2]struct{}, len(ps)) + for _, p := range ps { + points[p] = struct{}{} + } + return Points(points) +} + +// In returns whether the input x and y are a point in the point map +func (p Points) In(x, y int, sizes ...int) bool { + _, ok := p[intgeom.Point2{x, y}] + return ok +} + +// Outline returns the set of points along the point map's outline, if +// one exists +func (p Points) Outline(sizes ...int) ([]intgeom.Point2, error) { + return ToOutline(p)() +} + +// Rect returns a double slice of booleans representing the output of the In function in that rectangle +func (p Points) Rect(sizes ...int) [][]bool { + return InToRect(p.In)(sizes...) +} From c2ddab64cb10897f49f3e505bf5bdccd35e8487d Mon Sep 17 00:00:00 2001 From: Futility Date: Sat, 5 May 2018 14:11:34 -0400 Subject: [PATCH 17/37] Updated the Flooding mechanic for shape/holes to no longer have duplicate entries. --- shape/holes.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/shape/holes.go b/shape/holes.go index 383da4d9..bb899ba8 100644 --- a/shape/holes.go +++ b/shape/holes.go @@ -69,19 +69,20 @@ func borderPoints(w, h int) []intgeom.Point2 { func bfsFlood(m map[intgeom.Point2]bool, start intgeom.Point2) []intgeom.Point2 { visited := []intgeom.Point2{} - toVisit := []intgeom.Point2{start} - for len(toVisit) > 0 { - next := toVisit[0] - delete(m, next) - toVisit = toVisit[1:] - visited = append(visited, next) + toVisit := map[intgeom.Point2]bool{start: true} - // literally adjacent points for adjacency - for x := -1; x <= 1; x++ { - for y := -1; y <= 1; y++ { - p := intgeom.Point2{x + next.X(), y + next.Y()} - if _, ok := m[p]; ok { - toVisit = append(toVisit, p) + for len(toVisit) > 0 { + for next := range toVisit { + delete(m, next) + delete(toVisit, next) + visited = append(visited, next) + // literally adjacent points for adjacency + for x := -1; x <= 1; x++ { + for y := -1; y <= 1; y++ { + p := intgeom.Point2{x + next.X(), y + next.Y()} + if _, ok := m[p]; ok { + toVisit[p] = true + } } } } From 32636fe7ecca82f2dd94c88cd260584d82f4e689 Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Mon, 2 Jul 2018 16:18:55 -0500 Subject: [PATCH 18/37] Add "KeyHeld" for repeating key events --- inputLoop.go | 3 +++ key/events.go | 1 + 2 files changed, 4 insertions(+) diff --git a/inputLoop.go b/inputLoop.go index e32c8020..d4827cde 100644 --- a/inputLoop.go +++ b/inputLoop.go @@ -65,6 +65,9 @@ func inputLoop() { setUp(k) logicHandler.Trigger(okey.Up, k) logicHandler.Trigger(okey.Up+k, nil) + } else { + logicHandler.Trigger(okey.Held, k) + logicHandler.Trigger(okey.Held+k, nil) } // Send mouse events diff --git a/key/events.go b/key/events.go index fc63d278..bece316d 100644 --- a/key/events.go +++ b/key/events.go @@ -4,4 +4,5 @@ package key const ( Down = "KeyDown" Up = "KeyUp" + Held = "KeyHeld" ) From dd47dcd0580f5499e931b68ec0f460c175def366 Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Mon, 9 Jul 2018 20:08:36 -0500 Subject: [PATCH 19/37] Add floatgeom.Point4 --- alg/floatgeom/point.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/alg/floatgeom/point.go b/alg/floatgeom/point.go index c7d09c89..216c8763 100644 --- a/alg/floatgeom/point.go +++ b/alg/floatgeom/point.go @@ -12,6 +12,9 @@ type Point2 [2]float64 // Point3 represents a 3D point in space. type Point3 [3]float64 +// Point4 represents a 4D point, in space + some additional dimension. +type Point4 [4]float64 + // AnglePoint creates a unit vector from the given angle in degrees as a Point2. func AnglePoint(angle float64) Point2 { return RadianPoint(angle * math.Pi / 180) @@ -36,6 +39,13 @@ func (p Point3) Dim(i int) float64 { return p[i] } +// Dim returns the value of p in the ith dimension. +// Panics if i > 3. No check is made for efficiency's sake, pending benchmarks, +// but adding an error here would significantly worsen the API. +func (p Point4) Dim(i int) float64 { + return p[i] +} + // X returns p's value on the X axis. func (p Point2) X() float64 { return p.Dim(0) @@ -61,6 +71,26 @@ func (p Point3) Z() float64 { return p.Dim(2) } +// X returns p's value on the X axis. +func (p Point4) X() float64 { + return p.Dim(0) +} + +// Y returns p's value on the Y axis. +func (p Point4) Y() float64 { + return p.Dim(1) +} + +// Z returns p's value on the Z axis. +func (p Point4) Z() float64 { + return p.Dim(2) +} + +// W returns p's value on the W axis. +func (p Point4) W() float64 { + return p.Dim(3) +} + // Distance calculates the distance between this Point2 and another. func (p Point2) Distance(p2 Point2) float64 { return Distance2(p.X(), p.Y(), p2.X(), p2.Y()) From ea4cd0678e995bb0d6bc57353e96a761fd8cd373 Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Tue, 10 Jul 2018 12:54:32 -0500 Subject: [PATCH 20/37] Condense / specify docs in floatgeom points --- alg/floatgeom/point.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/alg/floatgeom/point.go b/alg/floatgeom/point.go index 216c8763..02c0f657 100644 --- a/alg/floatgeom/point.go +++ b/alg/floatgeom/point.go @@ -6,7 +6,7 @@ import ( "github.com/oakmound/oak/alg" ) -// Point2 represents a 2D point in space. +// Point2 represents a 2D point on a plane. type Point2 [2]float64 // Point3 represents a 3D point in space. @@ -26,22 +26,19 @@ func RadianPoint(radians float64) Point2 { } // Dim returns the value of p in the ith dimension. -// Panics if i > 1. No check is made for efficiency's sake, pending benchmarks, -// but adding an error here would significantly worsen the API. +// Panics if i > 1. func (p Point2) Dim(i int) float64 { return p[i] } // Dim returns the value of p in the ith dimension. -// Panics if i > 2. No check is made for efficiency's sake, pending benchmarks, -// but adding an error here would significantly worsen the API. +// Panics if i > 2. func (p Point3) Dim(i int) float64 { return p[i] } // Dim returns the value of p in the ith dimension. -// Panics if i > 3. No check is made for efficiency's sake, pending benchmarks, -// but adding an error here would significantly worsen the API. +// Panics if i > 3. func (p Point4) Dim(i int) float64 { return p[i] } From 853255c8001c5f552a77c2ac97732b81fffe11ce Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Tue, 10 Jul 2018 21:17:54 -0500 Subject: [PATCH 21/37] Add TriangulateConvex for simple convex polygon triangulation --- alg/triangulate.go | 35 +++++++++++++++++++++++++++++++++++ alg/triangulate_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 alg/triangulate.go create mode 100644 alg/triangulate_test.go diff --git a/alg/triangulate.go b/alg/triangulate.go new file mode 100644 index 00000000..c3e882dc --- /dev/null +++ b/alg/triangulate.go @@ -0,0 +1,35 @@ +package alg + +// TriangulateConvex takes a face, in the form of a slice +// of indices, and outputs those indicies split into triangles +// based on the assumption that the face is convex. This involves +// forming pairs of indices and drawing an edge back to the first +// index repeatedly. +// +// If given less than 3 indices, returns an empty slice. +// +// Example input: [0,1,2,3,4] +// Example output: [[0,1,2][0,2,3][0,3,4]] +// +// Visual Example: +// ____0____ +// 4 1 +// \ / +// 3-----2 +// - - - +// ____0____ +// 4 / \ 1 +// \ / \ / +// 3-----2 +func TriangulateConvex(face []int) [][3]int { + if len(face) < 3 { + return [][3]int{} + } + tris := make([][3]int, len(face)-2) + for i := 0; i < len(tris); i++ { + tris[i][0] = face[0] + tris[i][1] = face[i+1] + tris[i][2] = face[i+2] + } + return tris +} diff --git a/alg/triangulate_test.go b/alg/triangulate_test.go new file mode 100644 index 00000000..aed81020 --- /dev/null +++ b/alg/triangulate_test.go @@ -0,0 +1,36 @@ +package alg + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTriangulateConvex(t *testing.T) { + type testCase struct { + in []int + out [][3]int + } + testCases := []testCase{ + { + []int{0, 1}, + [][3]int{}, + }, + { + []int{0}, + [][3]int{}, + }, + { + []int{0, 1, 2}, + [][3]int{{0, 1, 2}}, + }, + { + []int{0, 1, 2, 3, 4}, + [][3]int{{0, 1, 2}, {0, 2, 3}, {0, 3, 4}}, + }, + } + for _, tc := range testCases { + out := TriangulateConvex(tc.in) + assert.Equal(t, tc.out, out) + } +} From cbce9f2e674fc0f82186adbed98710d806c7babb Mon Sep 17 00:00:00 2001 From: Patrick Stephen Date: Tue, 10 Jul 2018 21:20:17 -0500 Subject: [PATCH 22/37] Additional notes on TriangulateConvex --- alg/triangulate.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/alg/triangulate.go b/alg/triangulate.go index c3e882dc..1ca0181f 100644 --- a/alg/triangulate.go +++ b/alg/triangulate.go @@ -21,6 +21,10 @@ package alg // 4 / \ 1 // \ / \ / // 3-----2 +// +// This makes additional assumptions that the points represented +// by the indices are coplanar, and that there are no holes present +// in the face. func TriangulateConvex(face []int) [][3]int { if len(face) < 3 { return [][3]int{} From 5d53544d9795c3c84dc654ef2b353bf86cd91815 Mon Sep 17 00:00:00 2001 From: Futility Date: Sun, 29 Jul 2018 14:44:57 -0400 Subject: [PATCH 23/37] Triangles now appropriately use crossproduct function. --- alg/floatgeom/triangle.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/alg/floatgeom/triangle.go b/alg/floatgeom/triangle.go index 0ce35a3c..c586d4ec 100644 --- a/alg/floatgeom/triangle.go +++ b/alg/floatgeom/triangle.go @@ -27,10 +27,7 @@ func (t Tri3) Barycentric(x, y float64) Point3 { func (t Tri3) Normal() Point3 { u := t[1].Sub(t[0]) v := t[2].Sub(t[0]) + // Check that the triangle is defined in a clockwise fashion. - return Point3{ - (u.Y() * v.Z()) - (u.Z() * v.Y()), - (u.Z() * v.X()) - (u.X() * v.Z()), - (u.X() * v.Y()) - (u.Y() * v.X()), - }.Normalize() + return u.Cross(v).Normalize() } From e11be2ebce6943d8ef0fafe1b0e7dfddc272a529 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 24 Sep 2018 20:56:47 -0500 Subject: [PATCH 24/37] New methods for Point4, swap X and W for Point4 --- alg/floatgeom/point.go | 70 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/alg/floatgeom/point.go b/alg/floatgeom/point.go index 02c0f657..3a49d761 100644 --- a/alg/floatgeom/point.go +++ b/alg/floatgeom/point.go @@ -68,23 +68,23 @@ func (p Point3) Z() float64 { return p.Dim(2) } +// W returns p's value on the W axis. +func (p Point4) W() float64 { + return p.Dim(0) +} + // X returns p's value on the X axis. func (p Point4) X() float64 { - return p.Dim(0) + return p.Dim(1) } // Y returns p's value on the Y axis. func (p Point4) Y() float64 { - return p.Dim(1) + return p.Dim(2) } // Z returns p's value on the Z axis. func (p Point4) Z() float64 { - return p.Dim(2) -} - -// W returns p's value on the W axis. -func (p Point4) W() float64 { return p.Dim(3) } @@ -278,6 +278,19 @@ func (p Point3) DivConst(fs ...float64) Point3 { return p } +// DivConst divides all elements of a point by the input floats +// DivConst does not check that the inputs are non zero before operating, +// and can panic if that is not true. +func (p Point4) DivConst(fs ...float64) Point4 { + for _, f := range fs { + p[0] /= f + p[1] /= f + p[2] /= f + p[3] /= f + } + return p +} + // Dot returns the dot product of the input points func (p Point2) Dot(p2 Point2) float64 { return p[0]*p2[0] + p[1]*p2[1] @@ -288,6 +301,11 @@ func (p Point3) Dot(p2 Point3) float64 { return p[0]*p2[0] + p[1]*p2[1] + p[2]*p2[2] } +// Dot returns the dot product of the input points +func (p Point4) Dot(p2 Point4) float64 { + return p[0]*p2[0] + p[1]*p2[1] + p[2]*p2[2] + p[3]*p2[3] +} + // Magnitude returns the magnitude of the combined components of a Point func (p Point2) Magnitude() float64 { return math.Sqrt(p.Dot(p)) @@ -298,6 +316,11 @@ func (p Point3) Magnitude() float64 { return math.Sqrt(p.Dot(p)) } +// Magnitude returns the magnitude of the combined components of a Point +func (p Point4) Magnitude() float64 { + return math.Sqrt(p.Dot(p)) +} + // Normalize converts this point into a unit vector. func (p Point2) Normalize() Point2 { mgn := p.Magnitude() @@ -316,6 +339,15 @@ func (p Point3) Normalize() Point3 { return p.DivConst(mgn) } +// Normalize converts this point into a unit vector. +func (p Point4) Normalize() Point4 { + mgn := p.Magnitude() + if mgn == 0 { + return p + } + return p.DivConst(mgn) +} + // Rotate takes in a set of angles and rotates v by their sum // the input angles are expected to be in degrees. func (p Point2) Rotate(fs ...float64) Point2 { @@ -391,3 +423,27 @@ func (p Point2) AngleTo(p2 Point2) float64 { func (p Point2) RadiansTo(p2 Point2) float64 { return p.Sub(p2).ToRadians() } + +func (p Point4) Conjugate() Point4 { + return Point4{ + p[0], + -1 * p[1], + -1 * p[2], + -1 * p[3], + } +} + +func (p Point4) Inverse() Point4 { + cng := p.Conjugate() + return cng.Normalize() +} + +// ref: https://www.mathworks.com/help/aeroblks/quaternionmultiplication.html +func (p Point4) MulQuat(p2 Point4) Point4 { + return Point4{ + p2[0]*p[0] - p2[1]*p[1] - p2[2]*p[2] - p2[3]*p[3], + p2[0]*p[1] + p2[1]*p[0] - p2[2]*p[3] + p2[3]*p[2], + p2[0]*p[2] + p2[1]*p[3] + p2[2]*p[0] - p2[3]*p[1], + p2[0]*p[3] - p2[1]*p[2] + p2[2]*p[1] + p2[3]*p[0], + } +} From af7db59306cecde3b4b85bb51ee3c3e7c6d99c6d Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 18 Feb 2019 20:16:52 -0600 Subject: [PATCH 25/37] Add ScanForEntity function to the event package --- event/entity.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/event/entity.go b/event/entity.go index 1505a0e5..3432e13c 100644 --- a/event/entity.go +++ b/event/entity.go @@ -41,6 +41,18 @@ func GetEntity(i int) interface{} { return nil } +// ScanForEntity returns the first created entity that returns true for the given +// comparator function. If no entity satisfying the condition is found, this +// returns (-1, nil). +func ScanForEntity(by func(interface{}) bool) (int, interface{}) { + for i, e := range callers { + if by(e) { + return i, e + } + } + return -1, nil +} + // HasEntity returns whether the given caller id is an initialized entity func HasEntity(i int) bool { return len(callers) >= i && i != 0 From 48dbc4c90206274a6e8b445a68b60893c1766b73 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 18 Feb 2019 20:17:29 -0600 Subject: [PATCH 26/37] Add entities/x/move package, utilities for reusable character movement --- entities/moving.go | 5 +++++ entities/x/move/mover.go | 18 +++++++++++++++++ entities/x/move/topdown.go | 41 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 entities/x/move/mover.go create mode 100644 entities/x/move/topdown.go diff --git a/entities/moving.go b/entities/moving.go index a98b83a3..7d95bddb 100644 --- a/entities/moving.go +++ b/entities/moving.go @@ -65,3 +65,8 @@ type vMoving struct { func (v vMoving) GetDelta() physics.Vector { return v.Delta } + +// GetSpeed returns this moving's speed +func (v vMoving) GetSpeed() physics.Vector { + return v.Speed +} diff --git a/entities/x/move/mover.go b/entities/x/move/mover.go new file mode 100644 index 00000000..ba6ab02e --- /dev/null +++ b/entities/x/move/mover.go @@ -0,0 +1,18 @@ +package move + +import ( + "github.com/oakmound/oak/collision" + "github.com/oakmound/oak/physics" + "github.com/oakmound/oak/render" +) + +// A Mover can move its position, renderable, and space. Unless otherwise documented, +// functions effecting a mover move all of its logical position, renderable, and space +// simultaneously. +type Mover interface { + Vec() physics.Vector + GetRenderable() render.Renderable + GetDelta() physics.Vector + GetSpace() *collision.Space + GetSpeed() physics.Vector +} diff --git a/entities/x/move/topdown.go b/entities/x/move/topdown.go new file mode 100644 index 00000000..bbf698eb --- /dev/null +++ b/entities/x/move/topdown.go @@ -0,0 +1,41 @@ +package move + +import ( + "github.com/oakmound/oak" + "github.com/oakmound/oak/key" + "github.com/oakmound/oak/physics" +) + +// WASD moves the given mover based on its speed as W,A,S, and D are pressed +func WASD(mvr Mover) { + TopDown(mvr, key.W, key.S, key.A, key.D) +} + +// Arrows moves the given mover based on its speed as the arrow keys are pressed +func Arrows(mvr Mover) { + TopDown(mvr, key.UpArrow, key.DownArrow, key.LeftArrow, key.RightAlt) +} + +// TopDown moves the given mover based on its speed as the given keys are pressed +func TopDown(mvr Mover, up, down, left, right string) { + delta := mvr.GetDelta() + vec := mvr.Vec() + spd := mvr.GetSpeed() + + delta.Zero() + if oak.IsDown(up) { + delta.Add(physics.NewVector(0, -spd.Y())) + } + if oak.IsDown(down) { + delta.Add(physics.NewVector(0, spd.Y())) + } + if oak.IsDown(left) { + delta.Add(physics.NewVector(-spd.X(), 0)) + } + if oak.IsDown(right) { + delta.Add(physics.NewVector(spd.X(), 0)) + } + vec.Add(delta) + mvr.GetRenderable().SetPos(vec.X(), vec.Y()) + mvr.GetSpace().Update(vec.X(), vec.Y(), 16, 16) +} From 57d8273e774b72da528881affbe7abf53ead2b6d Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 18 Feb 2019 20:17:48 -0600 Subject: [PATCH 27/37] Add LoadSprites to render --- render/loadsheet.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/render/loadsheet.go b/render/loadsheet.go index ac306a71..1176eb4b 100644 --- a/render/loadsheet.go +++ b/render/loadsheet.go @@ -7,6 +7,15 @@ import ( "github.com/oakmound/oak/oakerr" ) +// LoadSprites calls LoadSheet and then Sheet.ToSprites. +func LoadSprites(directory, fileName string, w, h, pad int) ([][]*Sprite, error) { + sh, err := LoadSheet(directory, fileName, w, h, pad) + if sh != nil { + return sh.ToSprites(), err + } + return nil, err +} + // LoadSheet loads a file in some directory with sheets of (w,h) sized sprites, // where there is pad pixels of vertical/horizontal pad between each sprite. // This will blow away any cached sheet with the same fileName. From fd5961dbd9dea7fdcd862469f00dcc4e3e7b5565 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 18 Feb 2019 20:19:17 -0600 Subject: [PATCH 28/37] Add alias for key.FullStop: key.Period --- key/keys.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/key/keys.go b/key/keys.go index 7244b390..668fcf97 100644 --- a/key/keys.go +++ b/key/keys.go @@ -63,7 +63,8 @@ const ( GraveAccent = "GraveAccent" //` Comma = "Comma" //, FullStop = "FullStop" //. - Slash = "Slash" /// + Period = FullStop + Slash = "Slash" /// CapsLock = "CapsLock" F1 = "F1" @@ -108,7 +109,8 @@ const ( Keypad8 = "Keypad8" Keypad9 = "Keypad9" Keypad0 = "Keypad0" - KeypadFullStop = "KeypadFullStop" //. + KeypadFullStop = "KeypadFullStop" //. + KeypadPeriod = KeypadFullStop KeypadEqualSign = "KeypadEqualSign" //= F13 = "F13" From 0640b733f0b970b4054d72f85d2e7937f0bb12a3 Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 18 Feb 2019 20:20:06 -0600 Subject: [PATCH 29/37] Add entities/x/stat, for tracking ongoing events over a game --- entities/x/stat/default.go | 50 ++++++++++++++++ entities/x/stat/statistic.go | 113 +++++++++++++++++++++++++++++++++++ entities/x/stat/stats.go | 63 +++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 entities/x/stat/default.go create mode 100644 entities/x/stat/statistic.go create mode 100644 entities/x/stat/stats.go diff --git a/entities/x/stat/default.go b/entities/x/stat/default.go new file mode 100644 index 00000000..cd519ffd --- /dev/null +++ b/entities/x/stat/default.go @@ -0,0 +1,50 @@ +package stat + +var ( + // DefStatistics is a base set of statistics used by package-level calls + // When using multiple statistics, avoid using overlapping event names + DefStatistics = NewStatistics() +) + +// Inc triggers an event, incrementing the given statistic by one +func Inc(eventName string) { + DefStatistics.Inc(eventName) +} + +// Trigger triggers the given event with a given increment to update a statistic +func Trigger(eventName string, inc int) { + DefStatistics.Trigger(eventName, inc) +} + +// TriggerOn triggers the given event, toggling it on +func TriggerOn(eventName string) { + DefStatistics.TriggerOn(eventName) +} + +// TriggerOff triggers the given event, toggling it off +func TriggerOff(eventName string) { + DefStatistics.TriggerOff(eventName) +} + +// TriggerTimed triggers the given event, toggling it on or off +func TriggerTimed(eventName string, on bool) { + DefStatistics.TriggerTimed(eventName, on) +} + +// TrackStats records a stat event to the Statistics map and creates the statistic if it does not already exist +func TrackStats(no int, data interface{}) int { + return DefStatistics.TrackStats(no, data) +} + +// TrackTimeStats acts like TrackStats, but tracks durations of events. If the +// event has not started, it logs a start time, and then when the event ends +// it will log the delta since the start. +func TrackTimeStats(no int, data interface{}) int { + return DefStatistics.TrackTimeStats(no, data) +} + +// IsTimedStat returns whether the given stat name is a part of this statistics' +// set of timed stats +func IsTimedStat(s string) bool { + return DefStatistics.IsTimedStat(s) +} diff --git a/entities/x/stat/statistic.go b/entities/x/stat/statistic.go new file mode 100644 index 00000000..05cf28fd --- /dev/null +++ b/entities/x/stat/statistic.go @@ -0,0 +1,113 @@ +package stat + +import ( + "sync" + "time" + + "github.com/oakmound/oak/dlog" + "github.com/oakmound/oak/event" +) + +// Statistics stores the ongoing results of TrackStats and TrackTimeStats +type Statistics struct { + stats map[string]*History + statLock sync.Mutex + + statTimes map[string]time.Time + statTimeLock sync.Mutex +} + +// NewStatistics creates an empty statistics set +func NewStatistics() *Statistics { + return &Statistics{ + stats: make(map[string]*History), + statLock: sync.Mutex{}, + statTimes: make(map[string]time.Time), + statTimeLock: sync.Mutex{}, + } +} + +// A History keeps track of any recorded occurances of this statstic and their magnitude +type History struct { + Name string + Events []Event +} + +// An Event ties a value to a timestamp +type Event struct { + Timestamp time.Time + Val int +} + +// NewHistory creates a stat +func NewHistory(statName string, time time.Time) *History { + return &History{Name: statName, Events: []Event{{time, 0}}} +} + +// track adds a tracked event to the stat's history +func (h *History) track(t time.Time, v int) *History { + if len(h.Events) > 0 { + v += h.Events[len(h.Events)-1].Val + } + h.Events = append(h.Events, Event{t, v}) + return h +} + +// Total takes a statistics history and finds the sum. +func (h *History) Total() int { + return h.Events[len(h.Events)-1].Val +} + +func (st *Statistics) trackStats(name string, val int) { + st.statLock.Lock() + stat, ok := st.stats[name] + if !ok { + stat = NewHistory(name, time.Now()) + st.stats[name] = stat + } + stat.track(time.Now(), val) + st.statLock.Unlock() +} + +// TrackStats records a stat event to the Statistics map and creates the statistic if it does not already exist +func (st *Statistics) TrackStats(no int, data interface{}) int { + stat, ok := data.(stat) + if !ok { + dlog.Error("TrackStats called with a non-stat payload") + return event.UnbindEvent + } + st.trackStats(stat.name, stat.inc) + return 0 +} + +// TrackTimeStats acts like TrackStats, but tracks durations of events. If the +// event has not started, it logs a start time, and then when the event ends +// it will log the delta since the start. +func (st *Statistics) TrackTimeStats(no int, data interface{}) int { + timed, ok := data.(timedStat) + if !ok { + dlog.Error("TrackTimeStats called with a non-timedStat payload") + return event.UnbindEvent + } + if timed.on { //Turning on a thing to time track + st.statTimeLock.Lock() + st.statTimes[timed.name] = time.Now() + st.statTimeLock.Unlock() + } else { + st.statTimeLock.Lock() + timeDiff := int(time.Since(st.statTimes[timed.name])) + st.statTimeLock.Unlock() + if timeDiff < 0 { + return 0 + } + st.trackStats(timed.name, timeDiff) + } + return 0 +} + +// IsTimedStat returns whether the given stat name is a part of this statistics' +// set of timed stats +func (st *Statistics) IsTimedStat(s string) bool { + _, ok := st.statTimes[s] + return ok +} diff --git a/entities/x/stat/stats.go b/entities/x/stat/stats.go new file mode 100644 index 00000000..f0b78850 --- /dev/null +++ b/entities/x/stat/stats.go @@ -0,0 +1,63 @@ +package stat + +import "github.com/oakmound/oak/event" + +type timedStat struct { + name string + on bool +} +type stat struct { + name string + inc int +} + +// TimedOn returns a binding that will trigger toggling on the given event +func TimedOn(eventName string) event.Bindable { + return TimedBind(eventName, true) +} + +// TimedOff returns a binding that will trigger toggling off the given event +func TimedOff(eventName string) event.Bindable { + return TimedBind(eventName, false) +} + +// TimedBind returns a binding that will trigger toggling on or off the given event +func TimedBind(eventName string, on bool) event.Bindable { + return func(int, interface{}) int { + event.Trigger(eventName, timedStat{eventName, on}) + return 0 + } +} + +// Bind returns a binding that will increment the given event by 'inc' +func Bind(eventName string, inc int) event.Bindable { + return func(int, interface{}) int { + event.Trigger(eventName, stat{eventName, inc}) + return 0 + } +} + +// Inc triggers an event, incrementing the given statistic by one +func (st *Statistics) Inc(eventName string) { + st.Trigger(eventName, 1) +} + +// Trigger triggers the given event with a given increment to update a statistic +func (st *Statistics) Trigger(eventName string, inc int) { + event.Trigger(eventName, stat{eventName, inc}) +} + +// TriggerOn triggers the given event, toggling it on +func (st *Statistics) TriggerOn(eventName string) { + st.TriggerTimed(eventName, true) +} + +// TriggerOff triggers the given event, toggling it off +func (st *Statistics) TriggerOff(eventName string) { + st.TriggerTimed(eventName, false) +} + +// TriggerTimed triggers the given event, toggling it on or off +func (st *Statistics) TriggerTimed(eventName string, on bool) { + event.Trigger(eventName, timedStat{eventName, on}) +} From 812c5ed642d328c07c0eea7f0ed9929be114b64f Mon Sep 17 00:00:00 2001 From: Patrick Date: Mon, 18 Feb 2019 20:48:43 -0600 Subject: [PATCH 30/37] Add entities/x/force, holding some utilties for dealing with hurt boxes --- entities/x/force/directionSpace.go | 28 +++++++++++++++++ entities/x/force/hurtBox.go | 49 ++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 entities/x/force/directionSpace.go create mode 100644 entities/x/force/hurtBox.go diff --git a/entities/x/force/directionSpace.go b/entities/x/force/directionSpace.go new file mode 100644 index 00000000..b75a24fa --- /dev/null +++ b/entities/x/force/directionSpace.go @@ -0,0 +1,28 @@ +package force + +import ( + "github.com/oakmound/oak/collision" + "github.com/oakmound/oak/event" + "github.com/oakmound/oak/physics" +) + +// A DirectionSpace combines collision and a intended direction collision should imply +type DirectionSpace struct { + *collision.Space + physics.ForceVector +} + +// Init initalizes the DirectionSpace as an entity +func (ds *DirectionSpace) Init() event.CID { + return event.NextID(ds) +} + +// NewDirectionSpace creates a DirectionSpace and initializes it as an entity. +func NewDirectionSpace(s *collision.Space, v physics.ForceVector) *DirectionSpace { + ds := &DirectionSpace{ + Space: s, + ForceVector: v, + } + s.CID = ds.Init() + return ds +} diff --git a/entities/x/force/hurtBox.go b/entities/x/force/hurtBox.go new file mode 100644 index 00000000..a10c5280 --- /dev/null +++ b/entities/x/force/hurtBox.go @@ -0,0 +1,49 @@ +package force + +import ( + "image/color" + "time" + + "github.com/oakmound/lowrez17/game/layers" + "github.com/oakmound/oak/collision" + "github.com/oakmound/oak/physics" + "github.com/oakmound/oak/render" + "github.com/oakmound/oak/timing" +) + +type hurtBox struct { + *DirectionSpace +} + +// NewHurtBox creates a temporary collision space with a given force it should +// apply to objects it collides with +func NewHurtBox(x, y, w, h float64, duration time.Duration, l collision.Label, fv physics.ForceVector) { + hb := new(hurtBox) + hb.DirectionSpace = NewDirectionSpace(collision.NewLabeledSpace(x, y, w, h, l), fv) + collision.Add(hb.Space) + go timing.DoAfter(duration, func() { + collision.Remove(hb.Space) + }) +} + +// NewHurtColor creates a temporary collision space with a given force it should +// apply to objects it collides with. The box is rendered as the given color +func NewHurtColor(x, y, w, h float64, duration time.Duration, l collision.Label, fv physics.ForceVector, c color.Color) { + cb := render.NewColorBox(int(w), int(h), c) + NewHurtDisplay(x, y, w, h, duration, l, fv, cb) +} + +// NewHurtDisplay creates a temporary collision space with a given force it should +// apply to objects it collides with. The box is rendered as the given renderable. +// The input renderable is not copied before it is drawn. +func NewHurtDisplay(x, y, w, h float64, duration time.Duration, l collision.Label, fv physics.ForceVector, r render.Renderable) { + hb := new(hurtBox) + hb.DirectionSpace = NewDirectionSpace(collision.NewLabeledSpace(x, y, w, h, l), fv) + collision.Add(hb.Space) + r.SetPos(x, y) + render.Draw(r, layers.DebugLayer) + go timing.DoAfter(duration, func() { + collision.Remove(hb.Space) + r.Undraw() + }) +} From 6ff717bfeed7c48bcb5673722de05bb02335b271 Mon Sep 17 00:00:00 2001 From: Futility Date: Mon, 25 Feb 2019 20:10:14 -0500 Subject: [PATCH 31/37] starting to pull over radar mechanics from agent and general player bind mechanics --- examples/radarDemo/core.go | 178 ++++++++++++++++++++++++++++++ examples/radarDemo/radar/radar.go | 75 +++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 examples/radarDemo/core.go create mode 100644 examples/radarDemo/radar/radar.go diff --git a/examples/radarDemo/core.go b/examples/radarDemo/core.go new file mode 100644 index 00000000..2f51468b --- /dev/null +++ b/examples/radarDemo/core.go @@ -0,0 +1,178 @@ +package main + +import ( + "fmt" + "image/color" + "math" + "math/rand" + + "github.com/oakmound/oak" + "github.com/oakmound/oak/collision" + "github.com/oakmound/oak/entities" + "github.com/oakmound/oak/entities/x/move" + "github.com/oakmound/oak/event" + "github.com/oakmound/oak/examples/radarDemo/radar" + "github.com/oakmound/oak/physics" + "github.com/oakmound/oak/render" + "github.com/oakmound/oak/scene" +) + +const ( + _ = iota + RED collision.Label = iota + BLUE +) + +func main() { + oak.Add("demo", func(string, interface{}) { + act := &AttachCollisionTest{} + + act.Moving = entities.NewMoving(50, 50, 50, 50, render.NewColorBox(50, 50, color.RGBA{0, 0, 0, 255}), nil, act.Init(), 1) + act.Moving.Speed = physics.NewVector(1, 1) + + collision.Attach(act.Vector, act.Space, 0, 0) + + act.Bind(func(int, interface{}) int { + move.WASD(act) + + // Normally this should be farmed out on a type such as a character type + act.ShiftPos(act.Delta.X(), act.Delta.Y()) + act.R.ShiftX(act.Delta.X()) + act.R.ShiftY(act.Delta.Y()) + act.Delta = physics.NewVector(0, 0) + if act.ShouldUpdate { + act.ShouldUpdate = false + act.R.Undraw() + act.R = act.nextR + render.Draw(act.R, 0) + } + return 0 + }, "EnterFrame") + render.Draw(act.R, 0) + act.R.SetLayer(1) + + // Set collision and the functions to update color on collision + collision.PhaseCollision(act.Space) + act.Bind(func(id int, label interface{}) int { + updateOnCollision(act, true, label.(collision.Label)) + return 0 + }, "CollisionStart") + act.Bind(func(id int, label interface{}) int { + updateOnCollision(act, false, label.(collision.Label)) + return 0 + }, "CollisionStop") + + // Create two colors to continue collision-demo + left := entities.NewSolid(0, 0, 320, 480, render.NewColorBox(320, 480, color.RGBA{100, 0, 0, 10}), nil, 0) + left.Space.UpdateLabel(RED) + left.R.SetLayer(0) + render.Draw(left.R, 0) + + right := entities.NewSolid(320, 0, 320, 480, render.NewColorBox(320, 480, color.RGBA{0, 100, 100, 10}), nil, 0) + right.Space.UpdateLabel(BLUE) + right.R.SetLayer(0) + render.Draw(right.R, 0) + + // Create the Radar + center := radar.RadarPoint{act.Xp(), act.Yp()} + points := make(map[radar.RadarPoint]color.Color) + r := radar.NewRadar(100, 100, points, center) + + enemy := NewEnemyOnRadar(float64(200)) + enemy.CID.Bind(standardEnemyMove, "EnterFrame") + render.Draw(enemy.R, 2) + r.AddPoint(radar.RadarPoint{enemy.Xp(), enemy.Yp()}, color.RGBA{255, 255, 255, 255}) + + render.Draw(r, 1) + + }, func() bool { + return true + }, func() (string, *scene.Result) { + return "demo", nil + }) + render.SetDrawStack( + render.NewHeap(false), + render.NewDrawFPS(), + ) + oak.Init("demo") +} + +type EnemyOnRadar struct { + *entities.Moving +} + +func (eor *EnemyOnRadar) Init() event.CID { + return event.NextID(eor) +} +func NewEnemyOnRadar(y float64) *EnemyOnRadar { + fmt.Println("Sigh") + eor := new(EnemyOnRadar) + eor.Moving = entities.NewMoving(640, y, 80, 80, render.NewColorBox(320, 480, color.RGBA{0, 0, 0, 200}), nil, eor.Init(), 0) + eor.Speed = physics.NewVector(-1*(rand.Float64()*2+1), rand.Float64()*2-1) + return eor +} + +func standardEnemyMove(id int, nothing interface{}) int { + + eor := event.GetEntity(id).(*EnemyOnRadar) + fmt.Println("woof", eor.Speed.X(), eor.X(), eor.Y()) + eor.ShiftX(eor.Speed.X()) + eor.ShiftY(eor.Speed.Y()) + if eor.X() < 0 { + eor.Speed = physics.NewVector(math.Abs(eor.Speed.X()), (eor.Speed.Y())) + } + if eor.X() > 100 { + eor.Speed = physics.NewVector(-1*math.Abs(eor.Speed.X()), (eor.Speed.Y())) + } + if eor.Y() < 0 { + eor.Speed = physics.NewVector(eor.Speed.X(), math.Abs(eor.Speed.Y())) + } + if eor.Y() > 100 { + eor.Speed = physics.NewVector(eor.Speed.X(), -1*math.Abs(eor.Speed.Y())) + } + return 0 +} + +// updateOnCollision helper function to update color for this small example file +func updateOnCollision(obj *AttachCollisionTest, start bool, label collision.Label) { + updateValue := 125 + if !start { + updateValue *= -1 + } + switch label { + case RED: + obj.r += updateValue + case BLUE: + obj.b += updateValue + + default: + return + } + obj.UpdateR() +} + +type AttachCollisionTest struct { + *entities.Moving + // AttachSpace is a composable struct that allows + // spaces to be attached to vectors + collision.AttachSpace + // Phase is a composable struct that enables the call + // collision.CollisionPhase on this struct's space, + // which will start sending signals when that space + // starts and stops touching given labels + collision.Phase + r, g, b int + ShouldUpdate bool + nextR render.Renderable +} + +func (act *AttachCollisionTest) Init() event.CID { + return event.NextID(act) +} + +func (act *AttachCollisionTest) UpdateR() { + act.nextR = render.NewColorBox(50, 50, color.RGBA{uint8(act.r), uint8(act.g), uint8(act.b), 255}) + act.nextR.SetPos(act.X(), act.Y()) + act.nextR.SetLayer(1) + act.ShouldUpdate = true +} diff --git a/examples/radarDemo/radar/radar.go b/examples/radarDemo/radar/radar.go new file mode 100644 index 00000000..4354b41c --- /dev/null +++ b/examples/radarDemo/radar/radar.go @@ -0,0 +1,75 @@ +package radar + +import ( + "image" + "image/color" + "image/draw" + + "github.com/oakmound/oak/render" +) + +type RadarPoint struct { + X, Y *float64 +} + +type Radar struct { + render.LayeredPoint + points map[RadarPoint]color.Color + center RadarPoint + width, height int + r *image.RGBA + outline *render.Sprite +} + +const ( + ratio = 10.0 +) + +var ( + centerColor = color.RGBA{255, 255, 0, 255} +) + +/* Sets up the radar display */ +func NewRadar(w, h int, points map[RadarPoint]color.Color, center RadarPoint) *Radar { + r := new(Radar) + r.LayeredPoint = render.NewLayeredPoint(0, 0, 0) + r.points = points + r.width = w + r.height = h + r.center = center + r.r = image.NewRGBA(image.Rect(0, 0, w, h)) + r.outline = render.NewColorBox(400, 400, color.RGBA{0, 0, 200, 0}) + return r +} + +func (r *Radar) SetPos(x, y float64) { + r.LayeredPoint.SetPos(x, y) + r.outline.SetPos(x, y) +} + +func (r *Radar) GetRGBA() *image.RGBA { + return r.r +} + +func (r *Radar) Draw(buff draw.Image) { + r.DrawOffset(buff, 0, 0) +} + +func (r *Radar) DrawOffset(buff draw.Image, xOff, yOff float64) { + // Draw each point p in r.points + // at r.X() + center.X() - p.X(), r.Y() + center.Y() - p.Y() + // IF that value is < r.width/2, > -r.width/2, < r.height/2, > -r.height/2 + for p, c := range r.points { + x := int((*p.X-*r.center.X)/ratio) + r.width/2 + y := int((*p.Y-*r.center.Y)/ratio) + r.height/2 + r.r.Set(x, y, c) + } + r.r.Set(r.width/2, r.height/2, centerColor) + render.ShinyDraw(buff, r.r, int(xOff+r.X()), int(yOff+r.Y())) + r.outline.DrawOffset(buff, xOff, yOff) + r.r = image.NewRGBA(image.Rect(0, 0, r.width, r.height)) +} + +func (r *Radar) AddPoint(loc RadarPoint, c color.Color) { + r.points[loc] = c +} From e10260cbfa8ae6db4cdf6f6249be0699d63b6931 Mon Sep 17 00:00:00 2001 From: Futility Date: Mon, 25 Feb 2019 21:52:26 -0500 Subject: [PATCH 32/37] Updated oak examples to actually show radar point --- examples/radarDemo/core.go | 21 ++++++++++----------- examples/radarDemo/radar/radar.go | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/examples/radarDemo/core.go b/examples/radarDemo/core.go index 2f51468b..5d8e92d4 100644 --- a/examples/radarDemo/core.go +++ b/examples/radarDemo/core.go @@ -27,7 +27,7 @@ func main() { oak.Add("demo", func(string, interface{}) { act := &AttachCollisionTest{} - act.Moving = entities.NewMoving(50, 50, 50, 50, render.NewColorBox(50, 50, color.RGBA{0, 0, 0, 255}), nil, act.Init(), 1) + act.Moving = entities.NewMoving(200, 200, 50, 50, render.NewColorBox(50, 50, color.RGBA{0, 0, 0, 255}), nil, act.Init(), 1) act.Moving.Speed = physics.NewVector(1, 1) collision.Attach(act.Vector, act.Space, 0, 0) @@ -66,24 +66,24 @@ func main() { left := entities.NewSolid(0, 0, 320, 480, render.NewColorBox(320, 480, color.RGBA{100, 0, 0, 10}), nil, 0) left.Space.UpdateLabel(RED) left.R.SetLayer(0) - render.Draw(left.R, 0) + //render.Draw(left.R, 0) right := entities.NewSolid(320, 0, 320, 480, render.NewColorBox(320, 480, color.RGBA{0, 100, 100, 10}), nil, 0) right.Space.UpdateLabel(BLUE) right.R.SetLayer(0) - render.Draw(right.R, 0) + //render.Draw(right.R, 0) // Create the Radar center := radar.RadarPoint{act.Xp(), act.Yp()} points := make(map[radar.RadarPoint]color.Color) - r := radar.NewRadar(100, 100, points, center) + r := radar.NewRadar(25, 25, points, center) enemy := NewEnemyOnRadar(float64(200)) enemy.CID.Bind(standardEnemyMove, "EnterFrame") - render.Draw(enemy.R, 2) - r.AddPoint(radar.RadarPoint{enemy.Xp(), enemy.Yp()}, color.RGBA{255, 255, 255, 255}) - render.Draw(r, 1) + r.AddPoint(radar.RadarPoint{enemy.Xp(), enemy.Yp()}, color.RGBA{255, 255, 0, 0}) + render.Draw(enemy.R, 0) + render.Draw(r, 0) }, func() bool { return true @@ -107,7 +107,7 @@ func (eor *EnemyOnRadar) Init() event.CID { func NewEnemyOnRadar(y float64) *EnemyOnRadar { fmt.Println("Sigh") eor := new(EnemyOnRadar) - eor.Moving = entities.NewMoving(640, y, 80, 80, render.NewColorBox(320, 480, color.RGBA{0, 0, 0, 200}), nil, eor.Init(), 0) + eor.Moving = entities.NewMoving(50, y, 50, 50, render.NewColorBox(25, 25, color.RGBA{0, 200, 0, 0}), nil, eor.Init(), 0) eor.Speed = physics.NewVector(-1*(rand.Float64()*2+1), rand.Float64()*2-1) return eor } @@ -115,19 +115,18 @@ func NewEnemyOnRadar(y float64) *EnemyOnRadar { func standardEnemyMove(id int, nothing interface{}) int { eor := event.GetEntity(id).(*EnemyOnRadar) - fmt.Println("woof", eor.Speed.X(), eor.X(), eor.Y()) eor.ShiftX(eor.Speed.X()) eor.ShiftY(eor.Speed.Y()) if eor.X() < 0 { eor.Speed = physics.NewVector(math.Abs(eor.Speed.X()), (eor.Speed.Y())) } - if eor.X() > 100 { + if eor.X() > 400 { eor.Speed = physics.NewVector(-1*math.Abs(eor.Speed.X()), (eor.Speed.Y())) } if eor.Y() < 0 { eor.Speed = physics.NewVector(eor.Speed.X(), math.Abs(eor.Speed.Y())) } - if eor.Y() > 100 { + if eor.Y() > 400 { eor.Speed = physics.NewVector(eor.Speed.X(), -1*math.Abs(eor.Speed.Y())) } return 0 diff --git a/examples/radarDemo/radar/radar.go b/examples/radarDemo/radar/radar.go index 4354b41c..cfddbce5 100644 --- a/examples/radarDemo/radar/radar.go +++ b/examples/radarDemo/radar/radar.go @@ -38,7 +38,7 @@ func NewRadar(w, h int, points map[RadarPoint]color.Color, center RadarPoint) *R r.height = h r.center = center r.r = image.NewRGBA(image.Rect(0, 0, w, h)) - r.outline = render.NewColorBox(400, 400, color.RGBA{0, 0, 200, 0}) + r.outline = render.NewColorBox(100, 100, color.RGBA{0, 0, 200, 0}) return r } From 6f40e6c4e63512a6d120e3cbaf0005f9a0df59d9 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 2 Mar 2019 11:17:07 -0600 Subject: [PATCH 33/37] Add Default statistics operations --- entities/x/stat/default.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/entities/x/stat/default.go b/entities/x/stat/default.go index cd519ffd..fe3e17d8 100644 --- a/entities/x/stat/default.go +++ b/entities/x/stat/default.go @@ -39,7 +39,7 @@ func TrackStats(no int, data interface{}) int { // TrackTimeStats acts like TrackStats, but tracks durations of events. If the // event has not started, it logs a start time, and then when the event ends // it will log the delta since the start. -func TrackTimeStats(no int, data interface{}) int { +func TrackTimeStats(no int, data interface{}) int { return DefStatistics.TrackTimeStats(no, data) } From e648c4a3262c9bd7b3c1e4408708d9e2468b876d Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 2 Mar 2019 12:33:19 -0600 Subject: [PATCH 34/37] Add the Limit and CenterScreenOn utilities to x/move --- entities/x/move/topdown.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/entities/x/move/topdown.go b/entities/x/move/topdown.go index bbf698eb..8325f4dd 100644 --- a/entities/x/move/topdown.go +++ b/entities/x/move/topdown.go @@ -2,6 +2,7 @@ package move import ( "github.com/oakmound/oak" + "github.com/oakmound/oak/alg/floatgeom" "github.com/oakmound/oak/key" "github.com/oakmound/oak/physics" ) @@ -39,3 +40,31 @@ func TopDown(mvr Mover, up, down, left, right string) { mvr.GetRenderable().SetPos(vec.X(), vec.Y()) mvr.GetSpace().Update(vec.X(), vec.Y(), 16, 16) } + +// CenterScreenOn will cause the screen to center on the given mover, obeying +// viewport limits if they have been set previously +func CenterScreenOn(mvr Mover) { + vec := mvr.Vec() + oak.SetScreen( + int(vec.X())-oak.ScreenWidth/2, + int(vec.Y())-oak.ScreenHeight/2, + ) +} + +// Limit restricts the movement of the mover to stay within a given rectangle +func Limit(mvr Mover, rect floatgeom.Rect2) { + vec := mvr.Vec() + w, h := mvr.GetRenderable().GetDims() + wf := float64(w) + hf := float64(h) + if vec.X() < rect.Min.X() { + vec.SetX(0) + } else if vec.X() > rect.Max.X()-wf { + vec.SetX(rect.Max.X() - wf) + } + if vec.Y() < rect.Min.Y() { + vec.SetY(0) + } else if vec.Y() > rect.Max.Y()-hf { + vec.SetY(rect.Max.Y() - hf) + } +} From 6106efe3c20047e41489f645fed3ef011cc4ce6e Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 2 Mar 2019 12:33:33 -0600 Subject: [PATCH 35/37] Rename flappyBird to flappy-bird --- examples/{flappyBird => flappy-bird}/core.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{flappyBird => flappy-bird}/core.go (100%) diff --git a/examples/flappyBird/core.go b/examples/flappy-bird/core.go similarity index 100% rename from examples/flappyBird/core.go rename to examples/flappy-bird/core.go From 5ffe65a5001a940314fa8157adb71ea9e17b17bc Mon Sep 17 00:00:00 2001 From: Patrick Date: Sat, 2 Mar 2019 12:33:49 -0600 Subject: [PATCH 36/37] Rename and refactor radar-demo --- examples/radar-demo/core.go | 118 ++++++++++++ .../{radarDemo => radar-demo}/radar/radar.go | 37 ++-- examples/radarDemo/core.go | 177 ------------------ 3 files changed, 141 insertions(+), 191 deletions(-) create mode 100644 examples/radar-demo/core.go rename examples/{radarDemo => radar-demo}/radar/radar.go (54%) delete mode 100644 examples/radarDemo/core.go diff --git a/examples/radar-demo/core.go b/examples/radar-demo/core.go new file mode 100644 index 00000000..236caaaa --- /dev/null +++ b/examples/radar-demo/core.go @@ -0,0 +1,118 @@ +package main + +import ( + "image/color" + "math" + "math/rand" + + "github.com/oakmound/oak/alg/floatgeom" + + "github.com/oakmound/oak" + "github.com/oakmound/oak/entities" + "github.com/oakmound/oak/entities/x/move" + "github.com/oakmound/oak/event" + "github.com/oakmound/oak/examples/radar-demo/radar" + "github.com/oakmound/oak/physics" + "github.com/oakmound/oak/render" + "github.com/oakmound/oak/scene" +) + +const ( + xLimit = 1000 + yLimit = 1000 +) + +// This example demonstrates making a basic radar or other custom renderable +// type. The radar here acts as a UI element, staying on screen, and follows +// around a player character. + +func main() { + oak.Add("demo", func(string, interface{}) { + char := entities.NewMoving(200, 200, 50, 50, render.NewColorBox(50, 50, color.RGBA{125, 125, 0, 255}), nil, 0, 1) + char.Speed = physics.NewVector(3, 3) + + oak.SetViewportBounds(0, 0, xLimit, yLimit) + moveRect := floatgeom.NewRect2(0, 0, xLimit, yLimit) + + char.Bind(func(int, interface{}) int { + move.WASD(char) + move.Limit(char, moveRect) + move.CenterScreenOn(char) + return 0 + }, "EnterFrame") + render.Draw(char.R, 1, 2) + + // Create the Radar + center := radar.Point{X: char.Xp(), Y: char.Yp()} + points := make(map[radar.Point]color.Color) + w := 100 + h := 100 + r := radar.NewRadar(w, h, points, center, 10) + r.SetPos(float64(oak.ScreenWidth-w), 0) + + for i := 0; i < 5; i++ { + x, y := rand.Float64()*400, rand.Float64()*400 + enemy := NewEnemyOnRadar(x, y) + enemy.CID.Bind(standardEnemyMove, "EnterFrame") + render.Draw(enemy.R, 1, 1) + r.AddPoint(radar.Point{X: enemy.Xp(), Y: enemy.Yp()}, color.RGBA{255, 255, 0, 255}) + } + + render.Draw(r, 2) + + for x := 0; x < xLimit; x += 64 { + for y := 0; y < yLimit; y += 64 { + r := uint8(rand.Intn(120)) + b := uint8(rand.Intn(120)) + cb := render.NewColorBox(64, 64, color.RGBA{r, 0, b, 255}) + cb.SetPos(float64(x), float64(y)) + render.Draw(cb, 0) + } + } + + }, func() bool { + return true + }, scene.GoTo("demo")) + + render.SetDrawStack( + render.NewCompositeR(), + render.NewHeap(false), + render.NewHeap(true), + render.NewDrawFPS(), + ) + oak.Init("demo") +} + +type EnemyOnRadar struct { + *entities.Moving +} + +func (eor *EnemyOnRadar) Init() event.CID { + return event.NextID(eor) +} +func NewEnemyOnRadar(x, y float64) *EnemyOnRadar { + eor := new(EnemyOnRadar) + eor.Moving = entities.NewMoving(50, y, 50, 50, render.NewColorBox(25, 25, color.RGBA{0, 200, 0, 0}), nil, eor.Init(), 0) + eor.Speed = physics.NewVector(-1*(rand.Float64()*2+1), rand.Float64()*2-1) + eor.Delta = eor.Speed + return eor +} + +func standardEnemyMove(id int, nothing interface{}) int { + eor := event.GetEntity(id).(*EnemyOnRadar) + if eor.X() < 0 { + eor.Delta.SetPos(math.Abs(eor.Speed.X()), (eor.Speed.Y())) + } + if eor.X() > xLimit-eor.W { + eor.Delta.SetPos(-1*math.Abs(eor.Speed.X()), (eor.Speed.Y())) + } + if eor.Y() < 0 { + eor.Delta.SetPos(eor.Speed.X(), math.Abs(eor.Speed.Y())) + } + if eor.Y() > yLimit-eor.H { + eor.Delta.SetPos(eor.Speed.X(), -1*math.Abs(eor.Speed.Y())) + } + eor.ShiftX(eor.Delta.X()) + eor.ShiftY(eor.Delta.Y()) + return 0 +} diff --git a/examples/radarDemo/radar/radar.go b/examples/radar-demo/radar/radar.go similarity index 54% rename from examples/radarDemo/radar/radar.go rename to examples/radar-demo/radar/radar.go index cfddbce5..d478eae4 100644 --- a/examples/radarDemo/radar/radar.go +++ b/examples/radar-demo/radar/radar.go @@ -8,29 +8,28 @@ import ( "github.com/oakmound/oak/render" ) -type RadarPoint struct { +type Point struct { X, Y *float64 } type Radar struct { render.LayeredPoint - points map[RadarPoint]color.Color - center RadarPoint + points map[Point]color.Color + center Point width, height int r *image.RGBA outline *render.Sprite + ratio float64 } -const ( - ratio = 10.0 -) - var ( centerColor = color.RGBA{255, 255, 0, 255} ) -/* Sets up the radar display */ -func NewRadar(w, h int, points map[RadarPoint]color.Color, center RadarPoint) *Radar { +// NewRadar creates a radar that will display at 0,0 with the given dimensions. +// The points given will be displayed on the radar relative to the center point, +// With the absolute distance reduced by the given ratio +func NewRadar(w, h int, points map[Point]color.Color, center Point, ratio float64) *Radar { r := new(Radar) r.LayeredPoint = render.NewLayeredPoint(0, 0, 0) r.points = points @@ -38,31 +37,40 @@ func NewRadar(w, h int, points map[RadarPoint]color.Color, center RadarPoint) *R r.height = h r.center = center r.r = image.NewRGBA(image.Rect(0, 0, w, h)) - r.outline = render.NewColorBox(100, 100, color.RGBA{0, 0, 200, 0}) + r.outline = render.NewColorBox(w, h, color.RGBA{0, 0, 125, 125}) + r.ratio = ratio return r } +// SetPos sets the position of the radar on the screen func (r *Radar) SetPos(x, y float64) { r.LayeredPoint.SetPos(x, y) r.outline.SetPos(x, y) } +// GetRGBA returns this radar's image func (r *Radar) GetRGBA() *image.RGBA { return r.r } +// Draw draws the radar, satisfying render.Renderable func (r *Radar) Draw(buff draw.Image) { r.DrawOffset(buff, 0, 0) } +// DrawOffset draws the radar at a given offset func (r *Radar) DrawOffset(buff draw.Image, xOff, yOff float64) { // Draw each point p in r.points // at r.X() + center.X() - p.X(), r.Y() + center.Y() - p.Y() // IF that value is < r.width/2, > -r.width/2, < r.height/2, > -r.height/2 for p, c := range r.points { - x := int((*p.X-*r.center.X)/ratio) + r.width/2 - y := int((*p.Y-*r.center.Y)/ratio) + r.height/2 - r.r.Set(x, y, c) + x := int((*p.X-*r.center.X)/r.ratio) + r.width/2 + y := int((*p.Y-*r.center.Y)/r.ratio) + r.height/2 + for x2 := x - 1; x2 < x+1; x2++ { + for y2 := y - 1; y2 < y+1; y2++ { + r.r.Set(x2, y2, c) + } + } } r.r.Set(r.width/2, r.height/2, centerColor) render.ShinyDraw(buff, r.r, int(xOff+r.X()), int(yOff+r.Y())) @@ -70,6 +78,7 @@ func (r *Radar) DrawOffset(buff draw.Image, xOff, yOff float64) { r.r = image.NewRGBA(image.Rect(0, 0, r.width, r.height)) } -func (r *Radar) AddPoint(loc RadarPoint, c color.Color) { +// AddPoint adds an additional point to the radar to be tracked +func (r *Radar) AddPoint(loc Point, c color.Color) { r.points[loc] = c } diff --git a/examples/radarDemo/core.go b/examples/radarDemo/core.go deleted file mode 100644 index 5d8e92d4..00000000 --- a/examples/radarDemo/core.go +++ /dev/null @@ -1,177 +0,0 @@ -package main - -import ( - "fmt" - "image/color" - "math" - "math/rand" - - "github.com/oakmound/oak" - "github.com/oakmound/oak/collision" - "github.com/oakmound/oak/entities" - "github.com/oakmound/oak/entities/x/move" - "github.com/oakmound/oak/event" - "github.com/oakmound/oak/examples/radarDemo/radar" - "github.com/oakmound/oak/physics" - "github.com/oakmound/oak/render" - "github.com/oakmound/oak/scene" -) - -const ( - _ = iota - RED collision.Label = iota - BLUE -) - -func main() { - oak.Add("demo", func(string, interface{}) { - act := &AttachCollisionTest{} - - act.Moving = entities.NewMoving(200, 200, 50, 50, render.NewColorBox(50, 50, color.RGBA{0, 0, 0, 255}), nil, act.Init(), 1) - act.Moving.Speed = physics.NewVector(1, 1) - - collision.Attach(act.Vector, act.Space, 0, 0) - - act.Bind(func(int, interface{}) int { - move.WASD(act) - - // Normally this should be farmed out on a type such as a character type - act.ShiftPos(act.Delta.X(), act.Delta.Y()) - act.R.ShiftX(act.Delta.X()) - act.R.ShiftY(act.Delta.Y()) - act.Delta = physics.NewVector(0, 0) - if act.ShouldUpdate { - act.ShouldUpdate = false - act.R.Undraw() - act.R = act.nextR - render.Draw(act.R, 0) - } - return 0 - }, "EnterFrame") - render.Draw(act.R, 0) - act.R.SetLayer(1) - - // Set collision and the functions to update color on collision - collision.PhaseCollision(act.Space) - act.Bind(func(id int, label interface{}) int { - updateOnCollision(act, true, label.(collision.Label)) - return 0 - }, "CollisionStart") - act.Bind(func(id int, label interface{}) int { - updateOnCollision(act, false, label.(collision.Label)) - return 0 - }, "CollisionStop") - - // Create two colors to continue collision-demo - left := entities.NewSolid(0, 0, 320, 480, render.NewColorBox(320, 480, color.RGBA{100, 0, 0, 10}), nil, 0) - left.Space.UpdateLabel(RED) - left.R.SetLayer(0) - //render.Draw(left.R, 0) - - right := entities.NewSolid(320, 0, 320, 480, render.NewColorBox(320, 480, color.RGBA{0, 100, 100, 10}), nil, 0) - right.Space.UpdateLabel(BLUE) - right.R.SetLayer(0) - //render.Draw(right.R, 0) - - // Create the Radar - center := radar.RadarPoint{act.Xp(), act.Yp()} - points := make(map[radar.RadarPoint]color.Color) - r := radar.NewRadar(25, 25, points, center) - - enemy := NewEnemyOnRadar(float64(200)) - enemy.CID.Bind(standardEnemyMove, "EnterFrame") - - r.AddPoint(radar.RadarPoint{enemy.Xp(), enemy.Yp()}, color.RGBA{255, 255, 0, 0}) - render.Draw(enemy.R, 0) - render.Draw(r, 0) - - }, func() bool { - return true - }, func() (string, *scene.Result) { - return "demo", nil - }) - render.SetDrawStack( - render.NewHeap(false), - render.NewDrawFPS(), - ) - oak.Init("demo") -} - -type EnemyOnRadar struct { - *entities.Moving -} - -func (eor *EnemyOnRadar) Init() event.CID { - return event.NextID(eor) -} -func NewEnemyOnRadar(y float64) *EnemyOnRadar { - fmt.Println("Sigh") - eor := new(EnemyOnRadar) - eor.Moving = entities.NewMoving(50, y, 50, 50, render.NewColorBox(25, 25, color.RGBA{0, 200, 0, 0}), nil, eor.Init(), 0) - eor.Speed = physics.NewVector(-1*(rand.Float64()*2+1), rand.Float64()*2-1) - return eor -} - -func standardEnemyMove(id int, nothing interface{}) int { - - eor := event.GetEntity(id).(*EnemyOnRadar) - eor.ShiftX(eor.Speed.X()) - eor.ShiftY(eor.Speed.Y()) - if eor.X() < 0 { - eor.Speed = physics.NewVector(math.Abs(eor.Speed.X()), (eor.Speed.Y())) - } - if eor.X() > 400 { - eor.Speed = physics.NewVector(-1*math.Abs(eor.Speed.X()), (eor.Speed.Y())) - } - if eor.Y() < 0 { - eor.Speed = physics.NewVector(eor.Speed.X(), math.Abs(eor.Speed.Y())) - } - if eor.Y() > 400 { - eor.Speed = physics.NewVector(eor.Speed.X(), -1*math.Abs(eor.Speed.Y())) - } - return 0 -} - -// updateOnCollision helper function to update color for this small example file -func updateOnCollision(obj *AttachCollisionTest, start bool, label collision.Label) { - updateValue := 125 - if !start { - updateValue *= -1 - } - switch label { - case RED: - obj.r += updateValue - case BLUE: - obj.b += updateValue - - default: - return - } - obj.UpdateR() -} - -type AttachCollisionTest struct { - *entities.Moving - // AttachSpace is a composable struct that allows - // spaces to be attached to vectors - collision.AttachSpace - // Phase is a composable struct that enables the call - // collision.CollisionPhase on this struct's space, - // which will start sending signals when that space - // starts and stops touching given labels - collision.Phase - r, g, b int - ShouldUpdate bool - nextR render.Renderable -} - -func (act *AttachCollisionTest) Init() event.CID { - return event.NextID(act) -} - -func (act *AttachCollisionTest) UpdateR() { - act.nextR = render.NewColorBox(50, 50, color.RGBA{uint8(act.r), uint8(act.g), uint8(act.b), 255}) - act.nextR.SetPos(act.X(), act.Y()) - act.nextR.SetLayer(1) - act.ShouldUpdate = true -} From af95e9d337d8d81f76a3e12557d0d31157f298a0 Mon Sep 17 00:00:00 2001 From: Patrick Date: Sun, 3 Mar 2019 19:13:25 -0600 Subject: [PATCH 37/37] Add dep and go mod dependency files --- Gopkg.lock | 277 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Gopkg.toml | 82 ++++++++++++++++ go.mod | 26 +++++ 3 files changed, 385 insertions(+) create mode 100644 Gopkg.lock create mode 100644 Gopkg.toml create mode 100644 go.mod diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 00000000..1a026f4f --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,277 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + digest = "1:b4cf4077e6123e315ad327b9578b6aac5253b74095c9eb16bbb0479b3676805a" + name = "github.com/200sc/go-dist" + packages = [ + "colorrange", + "floatrange", + "intrange", + ] + pruneopts = "UT" + revision = "8bdf90fe4b023f2e176be67c7c27ea31dd89d324" + +[[projects]] + digest = "1:7a13753f465bbc2b6c141de05f94dbdcaa066da0f49f226b94b6c46b4bba0ddf" + name = "github.com/200sc/klangsynthese" + packages = [ + "audio", + "audio/filter", + "audio/filter/supports", + "audio/manip", + "font", + "mp3", + "synth", + "wav", + ] + pruneopts = "UT" + revision = "edd134f4ff94e91c8ad140de3e064db8334aef63" + version = "v0.1.1" + +[[projects]] + digest = "1:9f3b30d9f8e0d7040f729b82dcbc8f0dead820a133b3147ce355fc451f32d761" + name = "github.com/BurntSushi/toml" + packages = ["."] + pruneopts = "UT" + revision = "3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005" + version = "v0.3.1" + +[[projects]] + branch = "master" + digest = "1:130593c6d3a71ec66faec36cbe0017d56755ddc38c638e675b31552b6f1b9708" + name = "github.com/BurntSushi/xgb" + packages = [ + ".", + "render", + "shm", + "xproto", + ] + pruneopts = "UT" + revision = "27f122750802c950b2c869a5b63dafcf590ced95" + +[[projects]] + branch = "master" + digest = "1:554fc9ace442cb49137a7790fa8d6264d8987c1fb90925cf02899d4663538f9d" + name = "github.com/akavel/polyclip-go" + packages = ["."] + pruneopts = "UT" + revision = "2cfdb71461bde9115450eb65688337e3f8ee715e" + +[[projects]] + digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "UT" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" + +[[projects]] + digest = "1:b3af453d2bd5c7f1c6242b38d0416aa6cf96485a90b404fe638abeadb7ddd501" + name = "github.com/disintegration/gift" + packages = ["."] + pruneopts = "UT" + revision = "e7b6d79ff1929f7f71e2de01198850f4e50db244" + version = "v1.2.0" + +[[projects]] + branch = "master" + digest = "1:62c57507df491b657e9ae2645f30958d9964c8eeb380600469eedc951ebb3a0e" + name = "github.com/golang/freetype" + packages = [ + "raster", + "truetype", + ] + pruneopts = "UT" + revision = "e2365dfdc4a05e4b8299a783240d4a7d5a65d4e4" + +[[projects]] + digest = "1:52430c9cb1b419d4d7cc21b6536732a492376820e4d6aa43bc2dd0b53ae491a5" + name = "github.com/hajimehoshi/go-mp3" + packages = [ + ".", + "internal/bits", + "internal/consts", + "internal/frame", + "internal/frameheader", + "internal/huffman", + "internal/imdct", + "internal/maindata", + "internal/sideinfo", + ] + pruneopts = "UT" + revision = "90191ee719ddc192865cd96bdc8013ef1ed6dbea" + version = "v0.1.1" + +[[projects]] + branch = "master" + digest = "1:0aeaaf2037e67cfb35f8623b2164ca4469cd5c112521b75e19f34fc9e3a287b9" + name = "github.com/oakmound/alsa-go" + packages = ["."] + pruneopts = "UT" + revision = "ba5351447e1209bb9726d4330341eb07661e7200" + +[[projects]] + branch = "master" + digest = "1:aef6e035cb47dc2a202386f7599928ecd6525251ad746e6c8e7cc11cea996d5a" + name = "github.com/oakmound/lowrez17" + packages = ["game/layers"] + pruneopts = "UT" + revision = "da6171067fff33956a6af7f46e2226d6acd9cf9d" + +[[projects]] + digest = "1:5946d50a5b3ad30b970c44bfe3d5867c30398f25d95b87c96181dd2cad75e374" + name = "github.com/oakmound/shiny" + packages = [ + "driver", + "driver/gldriver", + "driver/internal/drawer", + "driver/internal/errscreen", + "driver/internal/event", + "driver/internal/lifecycler", + "driver/internal/swizzle", + "driver/internal/win32", + "driver/internal/x11key", + "driver/windriver", + "driver/x11driver", + "gesture", + "screen", + ] + pruneopts = "UT" + revision = "c45f2e40187a7a4659e19f7fcef90086db41801e" + version = "v0.1.0" + +[[projects]] + digest = "1:17b5368f859a5ee0bc4521b9cb29b25ab1a69c86eab2ab2883b60efe3a3ad093" + name = "github.com/oakmound/w32" + packages = ["."] + pruneopts = "UT" + revision = "b7f1cf6a58d56773f641d191180a5d81c5679ab1" + version = "v1.0.0" + +[[projects]] + branch = "master" + digest = "1:ffbc3e14a414a0a5256fdcf92b7f52f218613318e6f17eca7b9c6ac48a1072cf" + name = "github.com/oov/directsound-go" + packages = ["dsound"] + pruneopts = "UT" + revision = "e53e59c700bfd3a54be020aa361349351b1221aa" + +[[projects]] + digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" + name = "github.com/pkg/errors" + packages = ["."] + pruneopts = "UT" + revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" + version = "v0.8.1" + +[[projects]] + digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + pruneopts = "UT" + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + digest = "1:972c2427413d41a1e06ca4897e8528e5a1622894050e2f527b38ddf0f343f759" + name = "github.com/stretchr/testify" + packages = ["assert"] + pruneopts = "UT" + revision = "ffdc059bfe9ce6a4e144ba849dbedead332c6053" + version = "v1.3.0" + +[[projects]] + branch = "master" + digest = "1:e4955d8aa1a09ceb900f2c2df004b638a74c6c7588fff643863f779eebfd1b5d" + name = "golang.org/x/image" + packages = [ + "bmp", + "colornames", + "font", + "math/f64", + "math/fixed", + ] + pruneopts = "UT" + revision = "0694c2d4d067f97ebef574d63a763ee8ab559da7" + +[[projects]] + branch = "master" + digest = "1:97b3ccf91aec42b36b53970c65da2f0aaebae2f22f50459eeb7b23a4a56cf71a" + name = "golang.org/x/mobile" + packages = [ + "event/key", + "event/lifecycle", + "event/mouse", + "event/paint", + "event/size", + "geom", + "gl", + ] + pruneopts = "UT" + revision = "b8c6dab863a6bb7fceeb28cd882e272336b5d63d" + +[[projects]] + branch = "master" + digest = "1:de4815ce3ca5b624af2733716ecd471de1ef50cda8afec39491aab517f73139c" + name = "golang.org/x/net" + packages = [ + "html", + "html/atom", + ] + pruneopts = "UT" + revision = "16b79f2e4e95ea23b2bf9903c9809ff7b013ce85" + +[[projects]] + branch = "master" + digest = "1:9f303486d623f840492bfeb48eb906a94e9d3fe638a761639b72ce64bf7bfcc3" + name = "golang.org/x/sync" + packages = ["syncmap"] + pruneopts = "UT" + revision = "e225da77a7e68af35c70ccbf71af2b83e6acac3c" + +[[projects]] + branch = "master" + digest = "1:237e8294fc988c135a81cfce460263ac3806d86ba9faef52130ddcc1ddd22fd4" + name = "golang.org/x/sys" + packages = ["windows"] + pruneopts = "UT" + revision = "c2f5717e611cf8e89a852a41081355194d80c943" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/200sc/go-dist/colorrange", + "github.com/200sc/go-dist/floatrange", + "github.com/200sc/go-dist/intrange", + "github.com/200sc/klangsynthese/audio", + "github.com/200sc/klangsynthese/audio/filter", + "github.com/200sc/klangsynthese/audio/filter/supports", + "github.com/200sc/klangsynthese/font", + "github.com/200sc/klangsynthese/mp3", + "github.com/200sc/klangsynthese/synth", + "github.com/200sc/klangsynthese/wav", + "github.com/BurntSushi/toml", + "github.com/akavel/polyclip-go", + "github.com/davecgh/go-spew/spew", + "github.com/disintegration/gift", + "github.com/golang/freetype/truetype", + "github.com/oakmound/lowrez17/game/layers", + "github.com/oakmound/shiny/driver", + "github.com/oakmound/shiny/gesture", + "github.com/oakmound/shiny/screen", + "github.com/stretchr/testify/assert", + "golang.org/x/image/bmp", + "golang.org/x/image/colornames", + "golang.org/x/image/font", + "golang.org/x/image/math/fixed", + "golang.org/x/mobile/event/key", + "golang.org/x/mobile/event/lifecycle", + "golang.org/x/mobile/event/mouse", + "golang.org/x/mobile/event/size", + "golang.org/x/sync/syncmap", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 00000000..26bb8841 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,82 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + branch = "master" + name = "github.com/200sc/go-dist" + +[[constraint]] + name = "github.com/200sc/klangsynthese" + version = "0.1.1" + +[[constraint]] + name = "github.com/BurntSushi/toml" + version = "0.3.1" + +[[constraint]] + branch = "master" + name = "github.com/akavel/polyclip-go" + +[[constraint]] + name = "github.com/davecgh/go-spew" + version = "1.1.1" + +[[constraint]] + name = "github.com/disintegration/gift" + version = "1.2.0" + +[[constraint]] + branch = "master" + name = "github.com/golang/freetype" + +[[constraint]] + branch = "master" + name = "github.com/oakmound/lowrez17" + +[[constraint]] + name = "github.com/oakmound/shiny" + version = "0.1.0" + +[[constraint]] + name = "github.com/stretchr/testify" + version = "1.3.0" + +[[constraint]] + branch = "master" + name = "golang.org/x/image" + +[[constraint]] + branch = "master" + name = "golang.org/x/mobile" + +[[constraint]] + branch = "master" + name = "golang.org/x/sync" + +[prune] + go-tests = true + unused-packages = true diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..f177f5f7 --- /dev/null +++ b/go.mod @@ -0,0 +1,26 @@ +module github.com/oakmound/oak + +require ( + github.com/200sc/go-dist v0.0.0-20180217184639-8bdf90fe4b02 + github.com/200sc/klangsynthese v0.1.1 + github.com/BurntSushi/toml v0.3.1 + github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 + github.com/akavel/polyclip-go v0.0.0-20160111220610-2cfdb71461bd + github.com/davecgh/go-spew v1.1.1 + github.com/disintegration/gift v1.2.0 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 + github.com/hajimehoshi/go-mp3 v0.1.1 + github.com/oakmound/alsa-go v0.0.0-20190302183935-ba5351447e12 + github.com/oakmound/lowrez17 v0.0.0-20180207115707-da6171067fff + github.com/oakmound/shiny v0.1.0 + github.com/oakmound/w32 v1.0.0 + github.com/oov/directsound-go v0.0.0-20141101201356-e53e59c700bf + github.com/pkg/errors v0.8.1 + github.com/pmezard/go-difflib v1.0.0 + github.com/stretchr/testify v1.3.0 + golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 + golang.org/x/mobile v0.0.0-20190302063618-b8c6dab863a6 + golang.org/x/net v0.0.0-20190301231341-16b79f2e4e95 + golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 + golang.org/x/sys v0.0.0-20190303192550-c2f5717e611c +)