doodle/pkg/main_scene.go
Noah Petherbridge cb02feff1d Add Switches, Fire/Water Collision and Play Menu
* New doodads: Switches.
  * They come in four varieties: wall switch (background element, with
    "ON/OFF" text) and three side-profile switches for the floor, left
    or right walls.
  * On collision with the player, they flip their state from "OFF" to
    "ON" or vice versa. If the player walks away and then collides
    again, the switch flips again.
  * Can be used to open/close Electric Doors when turned on/off. Their
    default state is "off"
  * If a switch receives a power signal from another linked switch, it
    sets its own state to match. So, two "on/off" switches that are
    connected to a door AND to each other will both flip on/off when one
    of them flips.
* Update the Level Collision logic to support Decoration, Fire and Water
  pixel collisions.
  * Previously, ALL pixels in the level were acting as though solid.
  * Non-solid pixels don't count for collision detection, but their
    attributes (fire and water) are collected and returned.
* Updated the MenuScene to support loading a map file in Play Mode
  instead of Edit Mode. Updated the title screen menu to add a button
  for playing levels instead of editing them.
* Wrote some documentation.
2019-07-06 18:30:03 -07:00

186 lines
4.0 KiB
Go

package doodle
import (
"git.kirsle.net/apps/doodle/lib/events"
"git.kirsle.net/apps/doodle/lib/render"
"git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/branding"
"git.kirsle.net/apps/doodle/pkg/filesystem"
"git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/apps/doodle/pkg/scripting"
"git.kirsle.net/apps/doodle/pkg/uix"
)
// MainScene implements the main menu of Doodle.
type MainScene struct {
Supervisor *ui.Supervisor
frame *ui.Frame
// Background wallpaper canvas.
scripting *scripting.Supervisor
canvas *uix.Canvas
}
// Name of the scene.
func (s *MainScene) Name() string {
return "Main"
}
// Setup the scene.
func (s *MainScene) Setup(d *Doodle) error {
s.Supervisor = ui.NewSupervisor()
if err := s.SetupDemoLevel(d); err != nil {
return err
}
// Main UI button frame.
frame := ui.NewFrame("frame")
s.frame = frame
var buttons = []struct {
Name string
Func func()
}{
{
Name: "Play a Level",
Func: d.GotoPlayMenu,
},
{
Name: "Create a New Level",
Func: d.GotoNewMenu,
},
{
Name: "Edit a Level",
Func: d.GotoLoadMenu,
},
}
for _, button := range buttons {
button := button
btn := ui.NewButton(button.Name, ui.NewLabel(ui.Label{
Text: button.Name,
Font: balance.StatusFont,
}))
btn.Handle(ui.Click, func(p render.Point) {
button.Func()
})
s.Supervisor.Add(btn)
frame.Pack(btn, ui.Pack{
Anchor: ui.N,
PadY: 8,
// Fill: true,
FillX: true,
})
}
return nil
}
// SetupDemoLevel configures the wallpaper behind the New screen,
// which demos a title screen demo level.
func (s *MainScene) SetupDemoLevel(d *Doodle) error {
// Set up the background wallpaper canvas.
s.canvas = uix.NewCanvas(100, false)
s.canvas.Scrollable = true
s.canvas.Resize(render.Rect{
W: int32(d.width),
H: int32(d.height),
})
// Title screen level to load.
lvlName, _ := filesystem.FindFile("example1.level")
lvl, err := level.LoadJSON(lvlName)
if err != nil {
log.Error("Error loading title-screen.level: %s", err)
}
s.canvas.LoadLevel(d.Engine, lvl)
s.canvas.InstallActors(lvl.Actors)
// Load all actor scripts.
s.scripting = scripting.NewSupervisor()
s.canvas.SetScriptSupervisor(s.scripting)
if err := s.scripting.InstallScripts(lvl); err != nil {
log.Error("Error with title screen level scripts: %s", err)
}
// Run all actors scripts main function to start them off.
if err := s.canvas.InstallScripts(); err != nil {
log.Error("Error running actor main() functions: %s", err)
}
return nil
}
// Loop the editor scene.
func (s *MainScene) Loop(d *Doodle, ev *events.State) error {
s.Supervisor.Loop(ev)
if err := s.scripting.Loop(); err != nil {
log.Error("MainScene.Loop: scripting.Loop: %s", err)
}
s.canvas.Loop(ev)
if resized := ev.Resized.Read(); resized {
w, h := d.Engine.WindowSize()
d.width = w
d.height = h
log.Info("Resized to %dx%d", d.width, d.height)
s.canvas.Resize(render.Rect{
W: int32(d.width),
H: int32(d.height),
})
}
return nil
}
// Draw the pixels on this frame.
func (s *MainScene) Draw(d *Doodle) error {
// Clear the canvas and fill it with white.
d.Engine.Clear(render.White)
s.canvas.Present(d.Engine, render.Origin)
// Draw a sheen over the level for clarity.
d.Engine.DrawBox(render.RGBA(255, 255, 254, 128), render.Rect{
X: 0,
Y: 0,
W: int32(d.width),
H: int32(d.height),
})
label := ui.NewLabel(ui.Label{
Text: branding.AppName,
Font: render.Text{
Size: 46,
Color: render.Pink,
Stroke: render.SkyBlue,
Shadow: render.Black,
},
})
label.Compute(d.Engine)
label.MoveTo(render.Point{
X: (int32(d.width) / 2) - (label.Size().W / 2),
Y: 120,
})
label.Present(d.Engine, label.Point())
s.frame.Compute(d.Engine)
s.frame.MoveTo(render.Point{
X: (int32(d.width) / 2) - (s.frame.Size().W / 2),
Y: 260,
})
s.frame.Present(d.Engine, s.frame.Point())
return nil
}
// Destroy the scene.
func (s *MainScene) Destroy() error {
return nil
}