Compare commits

...

10 Commits

32 changed files with 847 additions and 56 deletions

505
.ackrc Normal file
View File

@ -0,0 +1,505 @@
--ignore-ack-defaults
# This is the default ackrc for ack version v3.7.0.
# There are four different ways to match
#
# is: Match the filename exactly
#
# ext: Match the extension of the filename exactly
#
# match: Match the filename against a Perl regular expression
#
# firstlinematch: Match the first 250 characters of the first line
# of text against a Perl regular expression. This is only for
# the --type-add option.
### Directories to ignore
# Bazaar
# https://bazaar.canonical.com/
--ignore-directory=is:.bzr
# Codeville
# http://freshmeat.sourceforge.net/projects/codeville
--ignore-directory=is:.cdv
# Interface Builder (Xcode)
# https://en.wikipedia.org/wiki/Interface_Builder
--ignore-directory=is:~.dep
--ignore-directory=is:~.dot
--ignore-directory=is:~.nib
--ignore-directory=is:~.plst
# Git
# https://git-scm.com/
--ignore-directory=is:.git
# When submodules are used, .git is a file.
--ignore-file=is:.git
# Mercurial
# https://www.mercurial-scm.org/
--ignore-directory=is:.hg
# Quilt
# https://directory.fsf.org/wiki/Quilt
--ignore-directory=is:.pc
# Subversion
# https://subversion.apache.org/
--ignore-directory=is:.svn
# Monotone
# https://www.monotone.ca/
--ignore-directory=is:_MTN
# CVS
# https://savannah.nongnu.org/projects/cvs
--ignore-directory=is:CVS
# RCS
# https://www.gnu.org/software/rcs/
--ignore-directory=is:RCS
# SCCS
# https://en.wikipedia.org/wiki/Source_Code_Control_System
--ignore-directory=is:SCCS
# darcs
# http://darcs.net/
--ignore-directory=is:_darcs
# Vault/Fortress
--ignore-directory=is:_sgbak
# autoconf
# https://www.gnu.org/software/autoconf/
--ignore-directory=is:autom4te.cache
# Perl module building
--ignore-directory=is:blib
--ignore-directory=is:_build
# Perl Devel::Cover module's output directory
# https://metacpan.org/release/Devel-Cover
--ignore-directory=is:cover_db
# Node modules created by npm
--ignore-directory=is:node_modules
# CMake cache
# https://www.cmake.org/
--ignore-directory=is:CMakeFiles
# Eclipse workspace folder
# https://eclipse.org/
--ignore-directory=is:.metadata
# Cabal (Haskell) sandboxes
# https://www.haskell.org/cabal/users-guide/installing-packages.html
--ignore-directory=is:.cabal-sandbox
# Python caches
# https://docs.python.org/3/tutorial/modules.html
--ignore-directory=is:__pycache__
--ignore-directory=is:.pytest_cache
# macOS Finder remnants
--ignore-directory=is:__MACOSX
--ignore-file=is:.DS_Store
### Files to ignore
# Backup files
--ignore-file=ext:bak
--ignore-file=match:/~$/
# Emacs swap files
--ignore-file=match:/^#.+#$/
# vi/vim swap files https://www.vim.org/
--ignore-file=match:/[._].*[.]swp$/
# core dumps
--ignore-file=match:/core[.]\d+$/
# minified JavaScript
--ignore-file=match:/[.-]min[.]js$/
--ignore-file=match:/[.]js[.]min$/
# minified CSS
--ignore-file=match:/[.]min[.]css$/
--ignore-file=match:/[.]css[.]min$/
# JS and CSS source maps
--ignore-file=match:/[.]js[.]map$/
--ignore-file=match:/[.]css[.]map$/
# PDFs, because they pass Perl's -T detection
--ignore-file=ext:pdf
# Common graphics, just as an optimization
--ignore-file=ext:gif,jpg,jpeg,png
# Common archives, as an optimization
--ignore-file=ext:gz,tar,tgz,zip
# Python compiled modules
--ignore-file=ext:pyc,pyd,pyo
# Python's pickle serialization format
# https://docs.python.org/2/library/pickle.html#example
# https://docs.python.org/3.7/library/pickle.html#examples
--ignore-file=ext:pkl,pickle
# C extensions
--ignore-file=ext:so
# Compiled gettext files
--ignore-file=ext:mo
# Visual Studio user and workspace settings
# https://code.visualstudio.com/docs/getstarted/settings
--ignore-dir=is:.vscode
### Filetypes defined
# Makefiles
# https://www.gnu.org/s/make/
--type-add=make:ext:mk
--type-add=make:ext:mak
--type-add=make:is:makefile
--type-add=make:is:Makefile
--type-add=make:is:Makefile.Debug
--type-add=make:is:Makefile.Release
--type-add=make:is:GNUmakefile
# Rakefiles
# https://rake.rubyforge.org/
--type-add=rake:is:Rakefile
# CMake
# https://cmake.org/
--type-add=cmake:is:CMakeLists.txt
--type-add=cmake:ext:cmake
# Bazel build tool
# https://docs.bazel.build/versions/master/skylark/bzl-style.html
--type-add=bazel:ext:bzl
# https://docs.bazel.build/versions/master/guide.html#bazelrc-the-bazel-configuration-file
--type-add=bazel:ext:bazelrc
# https://docs.bazel.build/versions/master/build-ref.html#BUILD_files
--type-add=bazel:is:BUILD
# https://docs.bazel.build/versions/master/build-ref.html#workspace
--type-add=bazel:is:WORKSPACE
# Actionscript
--type-add=actionscript:ext:as,mxml
# Ada
# https://www.adaic.org/
--type-add=ada:ext:ada,adb,ads
# ASP
# https://docs.microsoft.com/en-us/previous-versions/office/developer/server-technologies/aa286483(v=msdn.10)
--type-add=asp:ext:asp
# ASP.Net
# https://dotnet.microsoft.com/apps/aspnet
--type-add=aspx:ext:master,ascx,asmx,aspx,svc
# Assembly
--type-add=asm:ext:asm,s
# DOS/Windows batch
--type-add=batch:ext:bat,cmd
# ColdFusion
# https://en.wikipedia.org/wiki/ColdFusion
--type-add=cfmx:ext:cfc,cfm,cfml
# Clojure
# https://clojure.org/
--type-add=clojure:ext:clj,cljs,edn,cljc
# C
# .xs are Perl C files
--type-add=cc:ext:c,h,xs
# C header files
--type-add=hh:ext:h
# CoffeeScript
# https://coffeescript.org/
--type-add=coffeescript:ext:coffee
# C++
--type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx
# C++ header files
--type-add=hpp:ext:hpp,hh,h,hxx
# C#
--type-add=csharp:ext:cs
# Crystal-lang
# https://crystal-lang.org/
--type-add=crystal:ext:cr,ecr
# CSS
# https://www.w3.org/Style/CSS/
--type-add=css:ext:css
# Dart
# https://dart.dev/
--type-add=dart:ext:dart
# Delphi
# https://en.wikipedia.org/wiki/Embarcadero_Delphi
--type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj
# Elixir
# https://elixir-lang.org/
--type-add=elixir:ext:ex,exs
# Elm
# https://elm-lang.org
--type-add=elm:ext:elm
# Emacs Lisp
# https://www.gnu.org/software/emacs
--type-add=elisp:ext:el
# Erlang
# https://www.erlang.org/
--type-add=erlang:ext:erl,hrl
# Fortran
# https://en.wikipedia.org/wiki/Fortran
--type-add=fortran:ext:f,f77,f90,f95,f03,for,ftn,fpp
# Go
# https://golang.org/
--type-add=go:ext:go
# Groovy
# https://www.groovy-lang.org/
--type-add=groovy:ext:groovy,gtmpl,gpp,grunit,gradle
# GSP
# https://gsp.grails.org/
--type-add=gsp:ext:gsp
# Haskell
# https://www.haskell.org/
--type-add=haskell:ext:hs,lhs
# HTML
--type-add=html:ext:htm,html,xhtml
# Jade
# http://jade-lang.com/
--type-add=jade:ext:jade
# Java
# https://www.oracle.com/technetwork/java/index.html
--type-add=java:ext:java,properties
# JavaScript
--type-add=js:ext:js
# JSP
# https://www.oracle.com/technetwork/java/javaee/jsp/index.html
--type-add=jsp:ext:jsp,jspx,jspf,jhtm,jhtml
# JSON
# https://json.org/
--type-add=json:ext:json
# Kotlin
# https://kotlinlang.org/
--type-add=kotlin:ext:kt,kts
# Less
# http://www.lesscss.org/
--type-add=less:ext:less
# Common Lisp
# https://common-lisp.net/
--type-add=lisp:ext:lisp,lsp
# Lua
# https://www.lua.org/
--type-add=lua:ext:lua
--type-add=lua:firstlinematch:/^#!.*\blua(jit)?/
# Markdown
# https://en.wikipedia.org/wiki/Markdown
--type-add=markdown:ext:md,markdown
# We understand that there are many ad hoc extensions for markdown
# that people use. .md and .markdown are the two that ack recognizes.
# You are free to add your own in your ackrc file.
# Matlab
# https://en.wikipedia.org/wiki/MATLAB
--type-add=matlab:ext:m
# Objective-C
--type-add=objc:ext:m,h
# Objective-C++
--type-add=objcpp:ext:mm,h
# OCaml
# https://ocaml.org/
--type-add=ocaml:ext:ml,mli,mll,mly
# Perl
# https://perl.org/
--type-add=perl:ext:pl,pm,pod,t,psgi
--type-add=perl:firstlinematch:/^#!.*\bperl/
# Perl tests
--type-add=perltest:ext:t
# Perl's Plain Old Documentation format, POD
--type-add=pod:ext:pod
# PHP
# https://www.php.net/
--type-add=php:ext:php,phpt,php3,php4,php5,phtml
--type-add=php:firstlinematch:/^#!.*\bphp/
# Plone
# https://plone.org/
--type-add=plone:ext:pt,cpt,metadata,cpy,py
# PowerShell
# https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_scripts
# https://learn.microsoft.com/en-us/powershell/scripting/developer/module/understanding-a-windows-powershell-module
--type-add=powershell:ext:ps1,psm1
# PureScript
# https://www.purescript.org
--type-add=purescript:ext:purs
# Python
# https://www.python.org/
--type-add=python:ext:py
--type-add=python:firstlinematch:/^#!.*\bpython/
# R
# https://www.r-project.org/
# https://r4ds.had.co.nz/r-markdown.html
--type-add=rr:ext:R,Rmd
# reStructured Text
# https://docutils.sourceforge.io/rst.html
--type-add=rst:ext:rst
# Ruby
# https://www.ruby-lang.org/
--type-add=ruby:ext:rb,rhtml,rjs,rxml,erb,rake,spec
--type-add=ruby:is:Rakefile
--type-add=ruby:firstlinematch:/^#!.*\bruby/
# Rust
# https://www.rust-lang.org/
--type-add=rust:ext:rs
# Sass
# https://sass-lang.com
--type-add=sass:ext:sass,scss
# Scala
# https://www.scala-lang.org/
--type-add=scala:ext:scala,sbt
# Scheme
# https://groups.csail.mit.edu/mac/projects/scheme/
--type-add=scheme:ext:scm,ss
# Shell
--type-add=shell:ext:sh,bash,csh,tcsh,ksh,zsh,fish
--type-add=shell:firstlinematch:/^#!.*\b(?:ba|t?c|k|z|fi)?sh\b/
# Smalltalk
# http://www.smalltalk.org/
--type-add=smalltalk:ext:st
# Smarty
# https://www.smarty.net/
--type-add=smarty:ext:tpl
# SQL
# https://www.iso.org/standard/45498.html
--type-add=sql:ext:sql,ctl
# Stylus
# http://stylus-lang.com/
--type-add=stylus:ext:styl
# SVG
# https://en.wikipedia.org/wiki/Scalable_Vector_Graphics
--type-add=svg:ext:svg
# Swift
# https://developer.apple.com/swift/
--type-add=swift:ext:swift
--type-add=swift:firstlinematch:/^#!.*\bswift/
# Tcl
# https://www.tcl.tk/
--type-add=tcl:ext:tcl,itcl,itk
# TeX & LaTeX
# https://www.latex-project.org/
--type-add=tex:ext:tex,cls,sty
# Template Toolkit (Perl)
# http//template-toolkit.org/
--type-add=ttml:ext:tt,tt2,ttml
# TOML
# https://toml.io/
--type-add=toml:ext:toml
# TypeScript
# https://www.typescriptlang.org/
--type-add=ts:ext:ts,tsx
# Visual Basic
--type-add=vb:ext:bas,cls,frm,ctl,vb,resx
# Verilog
--type-add=verilog:ext:v,vh,sv
# VHDL
# http://www.eda.org/twiki/bin/view.cgi/P1076/WebHome
--type-add=vhdl:ext:vhd,vhdl
# Vim
# https://www.vim.org/
--type-add=vim:ext:vim
# XML
# https://www.w3.org/TR/REC-xml/
--type-add=xml:ext:xml,dtd,xsd,xsl,xslt,ent,wsdl
--type-add=xml:firstlinematch:/<[?]xml/
# YAML
# https://yaml.org/
--type-add=yaml:ext:yaml,yml
# TOM
--ignore-directory=is:src
--ignore-directory=is:tmp
--ignore-directory=is:fonts
--ignore-file=ext:ldtk
--ignore-file=ext:png
--ignore-file=ext:ase
--ignore-file=ext:json
--ignore-file=ext:svg

View File

@ -30,3 +30,6 @@ wasm: buildwasm zipwasm
test:
@echo $(VERSION)
nixshell:
nix-shell -p xorg.libX11 xorg.libXrandr libGL xorg.libXcursor xorg.libXinerama xorg.libXi xorg.libXxf86vm xorg_sys_opengl

40
TODO.md
View File

@ -1,22 +1,8 @@
## Levels:
- Add shaders for animation (player destruction etc)
- Start New game starts with last played level, better add a Resume
Game menu item for this and use Start New always for level 1.
- for finding caller:
pc := make([]uintptr, 10)
n := runtime.Callers(0, pc)
pc = pc[:n]
fs := runtime.CallersFrames(pc)
source, _ := fs.Next()
source, _ = fs.Next()
source, _ = fs.Next()
slog.Debug("get observer", "minmoves", observer.LevelScore,
"file", source.File, "line", source.Line)
- Turn menu button in hud_system (events in level_scene!) into ebitenui button
- Obstacle don't stop at collectibles
@ -29,11 +15,10 @@
- Add player collision animation
- Create pixel art ui elements (button, list things) and ui borders/backgrounds
Replace HUD with pixel art variant
- Modify font and font color matching the palette
- Replace HUD with pixel art variant
- On game start respond to any key (ebitengine-input has an anykey func)
- Switch to use https://github.com/quasilyte/ebitengine-input
@ -47,19 +32,10 @@
- Add save to disk feature including settings and player
accomplishements (FIXME: find the lib, asked in Discord)
- Add coded animated background like in https://github.com/tinne26/bindless/tree/main/src/misc/background
- Rework sprites (again!) to match stellar background: maybe more
technical tiles and items? Like some robotic setup?
- Fix collision snapin: sometimes, when an entity moves to the right
or south (NOT left or north!) it snaps in visibly, so a short wobble
can be seen.
- Rework animation system: since it is impossible to assign multiple
animation types to an entity via LDTK, hard code it in Tiles. Keep
implementation though. CAUTION: last time I changed this, the
collectible detonation didn't work anymore!
@ -82,3 +58,17 @@
Collider.IntersectResolve => func(newpos) {player.pos = newpos; player.vel = stop}
- in the end it must be possible to add new entities without the need
to write a collision check for them, but have collision detection anyway!
## Just in case stuff
- for finding caller:
pc := make([]uintptr, 10)
n := runtime.Callers(0, pc)
pc = pc[:n]
fs := runtime.CallersFrames(pc)
source, _ := fs.Next()
source, _ = fs.Next()
source, _ = fs.Next()
slog.Debug("get observer", "minmoves", observer.LevelScore,
"file", source.File, "line", source.Line)

View File

@ -18,6 +18,7 @@ type TileAnimation struct {
OnCollision bool // wether to animate a collision
OnDestruction bool // wether to animate destruction
OnIdle bool // wether to animate during idling
OnMoving bool // wether to animate during moving
CollisionSheet AnimationSet // an entry in assets.Animations[name]
DestructionSheet AnimationSet

View File

@ -0,0 +1,45 @@
JASC-PAL
0100
42
0 0 0
0 0 0
14 14 14
29 29 29
51 51 51
80 80 80
99 99 99
122 122 122
167 167 167
192 192 192
228 228 228
251 245 239
242 211 171
255 227 160
255 216 117
255 202 60
255 168 36
255 141 0
73 77 126
63 69 136
54 61 145
44 52 155
35 44 164
26 36 173
39 39 68
31 31 76
27 27 80
22 22 85
17 17 90
14 14 93
139 109 156
142 98 167
145 86 179
150 68 197
154 52 213
158 36 229
198 159 165
204 153 161
212 145 155
221 136 149
230 127 143
241 116 135

Binary file not shown.

Binary file not shown.

BIN
assets/space/obstacle.ase Normal file

Binary file not shown.

BIN
assets/space/oilshades.ase Normal file

Binary file not shown.

View File

@ -0,0 +1,33 @@
JASC-PAL
0100
30
242 211 171
236 199 134
230 193 96
224 176 50
205 142 32
198 159 165
186 140 144
180 129 129
172 115 115
147 87 98
139 109 156
118 93 138
97 80 118
82 68 100
62 51 75
73 77 126
61 61 106
51 48 83
37 33 56
27 21 37
39 39 68
39 39 68
39 39 68
39 39 68
39 39 68
251 245 239
223 207 198
194 178 174
171 157 155
146 134 130

BIN
assets/space/player.ase Normal file

Binary file not shown.

BIN
assets/space/robot.ase Normal file

Binary file not shown.

42
assets/space/space.pal Normal file
View File

@ -0,0 +1,42 @@
JASC-PAL
0100
39
32 0 59
63 0 83
108 0 108
146 0 124
187 0 151
236 0 178
255 19 186
255 64 190
255 113 213
255 147 231
255 186 253
245 220 255
255 255 255
0 0 0
12 12 12
26 26 26
39 39 39
54 54 54
71 71 71
86 86 86
99 99 99
118 118 118
133 133 133
153 153 153
177 177 177
205 205 205
42 2 0
52 9 0
71 24 0
94 46 0
131 71 0
165 96 0
201 106 0
243 119 0
255 165 41
255 186 82
255 239 169
255 250 211
255 255 247

BIN
assets/space/sprites.ase Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -71,6 +71,15 @@ func (position *Position) String() string {
)
}
func (position *Position) SmallString() string {
return fmt.Sprintf("%d,%d\n%d,%d",
position.X/32,
position.Y/32,
position.X,
position.Y,
)
}
func (position *Position) Move(velocity *Velocity) {
// if velocity.Direction != 0 {
// slog.Debug("moving", "velocity", velocity, "speed", speed)
@ -115,3 +124,8 @@ func (tile *Position) Intersects(moving *Position, velocity *Velocity) (bool, *P
return false, nil
}
func (tile *Position) Intersecting(position *Position, velocity *Velocity) bool {
is := tile.Rect.Bounds().Intersect(position.Rect.Bounds())
return is != image.ZR
}

View File

@ -12,9 +12,9 @@ const (
Destruction = iota
Idle
Collision
Moving
)
// virtual location, aka tile address
type Animation struct {
Active bool // animation is running
Loop bool // remove the entity if false, loop endless otherwise
@ -35,6 +35,7 @@ type Renderable struct {
DestructionAnimate Animation
IdleAnimate Animation
CollisionAnimate Animation
MovingAnimate Animation
Hidden bool
}
@ -51,6 +52,8 @@ func (render *Renderable) StartAnimation(which int) {
render.IdleAnimate.Active = true
render.IdleAnimate.Loop = true
render.IdleAnimate.Timer.Start(0)
case Moving:
render.MovingAnimate.Loop = true
}
}

View File

@ -9,6 +9,7 @@ import (
"github.com/knadh/koanf"
"github.com/knadh/koanf/providers/env"
"github.com/knadh/koanf/providers/posflag"
input "github.com/quasilyte/ebitengine-input"
flag "github.com/spf13/pflag"
)
@ -22,11 +23,26 @@ type Config struct {
Loglevel string `koanf:"loglevel"`
Debug bool `koanf:"debug"` // loglevel=debug
Startlevel int
Keymap input.Keymap `koanf:"keymap"` // we put it here so we can eventually customize it later
}
func InitConfig() (*Config, error) {
var kloader = koanf.New(".")
// Load default values using the confmap provider.
/*
if err := kloader.Load(confmap.Provider(map[string]interface{}{
"keymap": input.Keymap{
InputMoveLeft: {input.KeyGamepadLeft, input.KeyLeft, input.KeyA},
InputMoveRight: {input.KeyGamepadRight, input.KeyRight, input.KeyD},
InputMoveUp: {input.KeyGamepadUp, input.KeyUp, input.KeyW},
InputMoveDown: {input.KeyGamepadDown, input.KeyDown, input.KeyS},
},
}, "."), nil); err != nil {
return nil, fmt.Errorf("failed to load default values into koanf: %w", err)
}
*/
flagset := flag.NewFlagSet("config", flag.ContinueOnError)
flagset.BoolP("debug", "d", false, "enable debug log")
@ -72,6 +88,17 @@ func InitConfig() (*Config, error) {
conf.Startlevel = level
}
conf.Keymap = input.Keymap{
MoveLeft: {input.KeyGamepadLeft, input.KeyLeft, input.KeyA},
MoveRight: {input.KeyGamepadRight, input.KeyRight, input.KeyD},
MoveUp: {input.KeyGamepadUp, input.KeyUp, input.KeyW},
MoveDown: {input.KeyGamepadDown, input.KeyDown, input.KeyS},
SwitchPlayer: {input.KeyTab},
Abort: {input.KeyEscape},
Activate: {input.KeyEnter, input.KeyMouseLeft},
Any: {},
}
return conf, nil
}

View File

@ -3,6 +3,8 @@ package config
import (
"image/color"
"time"
input "github.com/quasilyte/ebitengine-input"
)
const (
@ -33,3 +35,14 @@ var VERSION string // maintained by -x
var FontColorFG = color.RGBA{0, 255, 128, 255} // blue
//var FontColorFG = color.RGBA{117, 167, 67, 255} // green
const (
MoveLeft input.Action = iota
MoveRight
MoveUp
MoveDown
SwitchPlayer
Abort
Activate
Any
)

View File

@ -9,6 +9,7 @@ import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/mlange-42/arche/ecs"
input "github.com/quasilyte/ebitengine-input"
)
type Game struct {
@ -20,6 +21,8 @@ type Game struct {
Observer *observers.GameObserver
Levels []*Level // fed in PlayScene.GenerateLevels()
Config *config.Config
InputSystem input.System
Input *input.Handler
}
func NewGame(width, height, cellsize int, cfg *config.Config, startscene SceneName) *Game {
@ -38,6 +41,12 @@ func NewGame(width, height, cellsize int, cfg *config.Config, startscene SceneNa
game.Observer = observers.NewGameObserver(
&world, cfg.Startlevel, width, height, cellsize)
game.InputSystem.Init(input.SystemConfig{
DevicesEnabled: input.AnyDevice,
})
game.Input = game.InputSystem.NewHandler(0, game.Config.Keymap)
game.Scenes[Welcome] = NewWelcomeScene(game)
game.Scenes[Menu] = NewMenuScene(game)
game.Scenes[About] = NewAboutScene(game)
@ -57,6 +66,9 @@ func (game *Game) GetCurrentScene() Scene {
}
func (game *Game) Update() error {
// update ebitengine-input
game.InputSystem.Update()
// handle level ends
timer := game.Observer.StopTimer

View File

@ -49,7 +49,8 @@ func NewLevel(game *Game, cellsize int, plan *ldtkgo.Level) *Level {
systemlist = append(systemlist, systems.NewPairSystem(game.World, gridcontainer))
systemlist = append(systemlist,
systems.NewPlayerSystem(game.World, gridcontainer, game.ScreenWidth, game.ScreenHeight))
systems.NewPlayerSystem(game.World, gridcontainer,
game.ScreenWidth, game.ScreenHeight, game.Input))
systemlist = append(systemlist, systems.NewAnimationSystem(game.World, game.Cellsize))

View File

@ -7,7 +7,6 @@ import (
"openquell/config"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
type PlayScene struct {
@ -73,10 +72,10 @@ func (scene *PlayScene) Update() error {
scene.Levels[scene.CurrentLevel].Update()
switch {
case inpututil.IsKeyJustPressed(ebiten.KeyEscape):
case scene.Game.Input.ActionIsJustPressed(config.Abort):
scene.SetNext(Popup)
case inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft):
case scene.Game.Input.ActionIsJustPressed(config.Activate):
// ok we're checking the menu button here, but drawing it in hud_system,
// because systems can't switch scenes
if image.Pt(ebiten.CursorPosition()).In(scene.MenuRect) {

View File

@ -11,7 +11,6 @@ import (
"github.com/ebitenui/ebitenui/widget"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
type PopupScene struct {
@ -53,7 +52,7 @@ func (scene *PopupScene) Clearscreen() bool {
func (scene *PopupScene) Update() error {
scene.Ui.Update()
if inpututil.IsKeyJustPressed(ebiten.KeyEscape) {
if scene.Game.Input.ActionIsJustPressed(config.Abort) {
scene.SetNext(Play)
}

View File

@ -14,7 +14,6 @@ import (
"github.com/ebitenui/ebitenui/widget"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
type SelectScene struct {
@ -56,7 +55,7 @@ func (scene *SelectScene) Clearscreen() bool {
func (scene *SelectScene) Update() error {
scene.Ui.Update()
if inpututil.IsKeyJustPressed(ebiten.KeyEscape) {
if scene.Game.Input.ActionIsJustPressed(config.Abort) {
scene.SetNext(Menu)
}

View File

@ -44,7 +44,7 @@ func (scene *WelcomeScene) Clearscreen() bool {
func (scene *WelcomeScene) Update() error {
switch {
case ebiten.IsKeyPressed(ebiten.KeyEnter):
case scene.Game.Input.AnyKeyJustPressed():
scene.SetNext(Menu)
}

2
go.mod
View File

@ -25,6 +25,8 @@ require (
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/quasilyte/ebitengine-input v0.9.2-0.20240402074358-f9ddd61e57f7 // indirect
github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/tidwall/gjson v1.9.3 // indirect
github.com/tidwall/match v1.1.1 // indirect

6
go.sum
View File

@ -237,6 +237,12 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/quasilyte/ebitengine-input v0.9.1 h1:sN7jNDLfGn9ZY1lurD4d3oXIOmQbZwYPVpuo4DlsiG0=
github.com/quasilyte/ebitengine-input v0.9.1/go.mod h1:xYkU+JqzWzjnVMolCIar0VFRly1TtjmEHfQWAoIan+o=
github.com/quasilyte/ebitengine-input v0.9.2-0.20240402074358-f9ddd61e57f7 h1:A33dnktcYAmgrFPSofTgLUPAos/ToFU0gN2f9KZIiug=
github.com/quasilyte/ebitengine-input v0.9.2-0.20240402074358-f9ddd61e57f7/go.mod h1:xYkU+JqzWzjnVMolCIar0VFRly1TtjmEHfQWAoIan+o=
github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7 h1:mvIS9aGirkzuYmHHNWAP6sLRoA5VFaIzMoFdPmqgPdY=
github.com/quasilyte/gmath v0.0.0-20221217210116-fba37a2e15c7/go.mod h1:EbI+KMbALSVE2s0YFOQpR4uj66zBh9ter5P4CBMSuvA=
github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=

View File

@ -3,6 +3,8 @@ package grid
import (
"openquell/components"
. "openquell/config"
"log/slog"
)
// Check a collision on the grid. We check if the entity in question
@ -24,8 +26,8 @@ func (grid *Grid) CheckGridCollision(
} else {
ok, tilepos := grid.GetSolidNeighborPosition(position, velocity, true)
if ok {
// slog.Debug("HaveSolidNeighbor", "ok", ok, "tilepos",
// tilepos.Point(), "playerpos", playerposition)
slog.Debug("(3) HaveSolidNeighbor", "tilepos",
tilepos.String(), "playpos", position.String())
intersects, newpos := tilepos.Intersects(position, velocity)
if intersects {
respond_solid(position, velocity, newpos)
@ -44,6 +46,66 @@ func (grid *Grid) GetSolidNeighborPosition(
return false, nil
}
// set to true if we are already on the last tile in the current
// direction, i.e. on the edge of the grid
edge := true
moving := position.GetMoved(velocity)
neighborpos := position.Point()
if velocity.Direction == East || velocity.Direction == South {
// FIXES snapin to down+right, but now top+left fail. Only in
// these cases we need to look at the neighbor in the future
// position where we will be when we move forward. I don't
// know why it is like this, in fact it doesn't really make
// any sense. But it works, so I'll keep it that way for the
// moment.
neighborpos = moving.Point()
}
switch velocity.Direction {
case East:
if neighborpos.X < grid.TilesX {
neighborpos.X++
edge = false
}
case West:
if neighborpos.X > 0 {
neighborpos.X--
edge = false
}
case South:
if neighborpos.Y < grid.TilesY {
neighborpos.Y++
edge = false
}
case North:
if neighborpos.Y > 0 {
neighborpos.Y--
edge = false
}
}
newpos := components.NewPosition(neighborpos, grid.Tilesize)
slog.Debug("SolidNeighbor?", "player", position.Point(),
"neighbor", neighborpos, "edge", edge, "neighbor-solid",
grid.Map[neighborpos].Solid, "newpos", newpos.Point())
if !edge && grid.Map[neighborpos].Solid {
return true, newpos
}
return false, nil
}
func (grid *Grid) OrigGetSolidNeighborPosition(
position *components.Position,
velocity *components.Velocity,
solid bool) (bool, *components.Position) {
if !solid {
return false, nil
}
// set to true if we are already on the last tile in the current
// direction, i.e. on the edge of the grid
edge := true
@ -74,9 +136,9 @@ func (grid *Grid) GetSolidNeighborPosition(
newpos := components.NewPosition(neighborpos, grid.Tilesize)
// slog.Debug("SolidNeighbor?", "player", position.Point(),
// "neighbor", neighborpos, "edge", edge, "neighbor-solid",
// grid.Map[neighborpos].Solid, "newpos", newpos.Point())
slog.Debug("SolidNeighbor?", "player", position.Point(),
"neighbor", neighborpos, "edge", edge, "neighbor-solid",
grid.Map[neighborpos].Solid, "newpos", newpos.Point())
if !edge && grid.Map[neighborpos].Solid {
return true, newpos
}

View File

@ -159,24 +159,28 @@ func NewGrid(world *ecs.World,
render.Image = tile.Sprite
render.Pos = pos
if tile.Animation.OnCollision {
switch {
case tile.Animation.OnCollision:
render.CollisionAnimate.Sprites = tile.Animation.CollisionSheet.Sprites
render.CollisionAnimate.Width = tile.Animation.CollisionSheet.Width
render.CollisionAnimate.Height = tile.Animation.CollisionSheet.Height
}
if tile.Animation.OnDestruction {
case tile.Animation.OnDestruction:
render.DestructionAnimate.Sprites = tile.Animation.DestructionSheet.Sprites
render.DestructionAnimate.Width = tile.Animation.DestructionSheet.Width
render.DestructionAnimate.Height = tile.Animation.DestructionSheet.Height
}
if tile.Animation.OnIdle {
case tile.Animation.OnIdle:
render.IdleAnimate.Sprites = tile.Animation.IdleSheet.Sprites
render.IdleAnimate.Width = tile.Animation.IdleSheet.Width
render.IdleAnimate.Height = tile.Animation.IdleSheet.Height
render.StartAnimation(components.Idle)
render.Hidden = true // we do NOT render the sprite, but the idle animation instead
case tile.Animation.OnMoving:
render.MovingAnimate.Sprites = tile.Animation.IdleSheet.Sprites
render.MovingAnimate.Width = tile.Animation.IdleSheet.Width
render.MovingAnimate.Height = tile.Animation.IdleSheet.Height
}
default:

View File

@ -1,17 +1,20 @@
package systems
import (
"fmt"
"log/slog"
"openquell/components"
. "openquell/components"
. "openquell/config"
"openquell/grid"
"openquell/observers"
"openquell/util"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/inpututil"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
"github.com/mlange-42/arche/ecs"
"github.com/mlange-42/arche/generic"
input "github.com/quasilyte/ebitengine-input"
)
type PlayerSystem struct {
@ -19,15 +22,18 @@ type PlayerSystem struct {
Selector *generic.Filter4[Position, Velocity, Player, Renderable]
GridContainer *grid.GridContainer
Width, Height int
Input *input.Handler
}
func NewPlayerSystem(world *ecs.World, gridcontainer *grid.GridContainer, width, height int) System {
func NewPlayerSystem(world *ecs.World, gridcontainer *grid.GridContainer,
width, height int, handler *input.Handler) System {
system := &PlayerSystem{
Selector: generic.NewFilter4[Position, Velocity, Player, Renderable](),
GridContainer: gridcontainer,
World: world,
Width: width,
Height: height,
Input: handler,
}
return system
@ -46,6 +52,7 @@ func PlayerBumpWallResponder(
vel *components.Velocity,
newpos *components.Position) {
slog.Debug("(2) PlayerBumpWallResponder", "old", pos.String(), "new", newpos.String())
pos.Set(newpos)
vel.Change(Stop)
}
@ -67,12 +74,16 @@ func (system PlayerSystem) Update() error {
continue
}
// check if the user alters or initiates movement
// check if the user alters or initiates movement, only
// changes player direction
system.CheckMovement(playerposition, velocity, player)
// check if player collides with walls or edges
system.GridContainer.Grid.CheckGridCollision(
playerposition, velocity, PlayerBumpEdgeResponder, PlayerBumpWallResponder)
if velocity.Moving() {
slog.Debug("(2) checking grid collision")
system.GridContainer.Grid.CheckGridCollision(
playerposition, velocity, PlayerBumpEdgeResponder, PlayerBumpWallResponder)
}
if count > 1 {
// check if player collides with another player, fuse them if any
@ -99,7 +110,11 @@ func (system PlayerSystem) Update() error {
query = system.Selector.Query(system.World)
for query.Next() {
playerposition, velocity, _, _ := query.Get()
playerposition.Move(velocity)
oldpos := playerposition.String()
if velocity.Moving() {
playerposition.Move(velocity)
slog.Debug("(4) moving player", "old", oldpos, "new", playerposition.String())
}
}
// we may have lost players, remove them here
@ -124,6 +139,10 @@ func (system *PlayerSystem) Draw(screen *ebiten.Image) {
op.GeoM.Translate(float64(pos.X), float64(pos.Y))
screen.DrawImage(sprite.Image, op)
if util.DebugEnabled() {
ebitenutil.DebugPrintAt(screen, pos.SmallString(), pos.X, pos.Y+16) // print player pos
}
}
}
@ -151,7 +170,7 @@ func (system *PlayerSystem) SwitchPlayers() {
render.Image = player.SwitchSprite()
} else {
// many players, switch when requested
if inpututil.IsKeyJustPressed(ebiten.KeyTab) {
if system.Input.ActionIsJustPressed(SwitchPlayer) {
slog.Debug("switch players")
if player.IsPrimary {
player.IsPrimary = false
@ -176,16 +195,16 @@ func (system *PlayerSystem) CheckMovement(
if !velocity.Moving() {
switch {
case inpututil.IsKeyJustPressed(ebiten.KeyRight):
case system.Input.ActionIsJustPressed(MoveRight):
velocity.Change(East)
moved = true
case inpututil.IsKeyJustPressed(ebiten.KeyLeft):
case system.Input.ActionIsJustPressed(MoveLeft):
velocity.Change(West)
moved = true
case inpututil.IsKeyJustPressed(ebiten.KeyDown):
case system.Input.ActionIsPressed(MoveDown):
velocity.Change(South)
moved = true
case inpututil.IsKeyJustPressed(ebiten.KeyUp):
case system.Input.ActionIsJustPressed(MoveUp):
velocity.Change(North)
moved = true
}
@ -196,6 +215,11 @@ func (system *PlayerSystem) CheckMovement(
player.LoopCount = 0
observer.AddMove()
}
} else {
if util.DebugEnabled() {
fmt.Println("------------------------")
}
slog.Debug("(1) player is at", "current", position.String())
}
}

View File

@ -1,7 +1,10 @@
package util
import (
"context"
. "openquell/config"
"log/slog"
)
// find an item in a list, generic variant
@ -41,3 +44,7 @@ func DirectionStr(dir int) string {
return str
}
func DebugEnabled() bool {
return slog.Default().Enabled(context.TODO(), slog.LevelDebug)
}