First day

This commit is contained in:
nefrace 2023-01-02 07:58:11 +03:00
commit 845a7d3aba
10 changed files with 518 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build

32
Makefile Normal file
View File

@ -0,0 +1,32 @@
# Build dependencies
GO = tinygo
WASM_OPT = wasm-opt
# Whether to build for debugging instead of release
DEBUG = 0
# Compilation flags
GOFLAGS = -target ./target.json -panic trap
ifeq ($(DEBUG), 1)
GOFLAGS += -opt 1
else
GOFLAGS += -opt z -no-debug
endif
# wasm-opt flags
WASM_OPT_FLAGS = -Oz --zero-filled-memory --strip-producers --enable-bulk-memory
all:
@mkdir -p build
$(GO) build $(GOFLAGS) -o build/cart.wasm .
ifneq ($(DEBUG), 1)
ifeq (, $(shell command -v $(WASM_OPT)))
@echo Tip: $(WASM_OPT) was not found. Install it from binaryen for smaller builds!
else
$(WASM_OPT) $(WASM_OPT_FLAGS) build/cart.wasm -o build/cart.wasm
endif
endif
.PHONY: clean
clean:
rm -rf build

26
README.md Normal file
View File

@ -0,0 +1,26 @@
# newyearkrendelki
A game written in Go for the [WASM-4](https://wasm4.org) fantasy console.
## Building
Build the cart by running:
```shell
make
```
Then run it with:
```shell
w4 run build/cart.wasm
```
For more info about setting up WASM-4, see the [quickstart guide](https://wasm4.org/docs/getting-started/setup?code-lang=go#quickstart).
## Links
- [Documentation](https://wasm4.org/docs): Learn more about WASM-4.
- [Snake Tutorial](https://wasm4.org/docs/tutorials/snake/goal): Learn how to build a complete game
with a step-by-step tutorial.
- [GitHub](https://github.com/aduros/wasm4): Submit an issue or PR. Contributions are welcome!

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module cart
go 1.16

65
main.go Normal file
View File

@ -0,0 +1,65 @@
package main
import (
"cart/w4"
"math/rand"
)
var smiley = [8]byte{
0b11000011,
0b10000001,
0b00100100,
0b00100100,
0b00000000,
0b00100100,
0b10011001,
0b11000011,
}
var gravity = 0.2
var points []*Point = []*Point{}
var sticks []*Stick = []*Stick{}
var player *Player
var frame uint64 = 0
var lightIndex uint64 = 0
//go:export start
func start() {
rand.Seed(654654321348654)
points = []*Point{}
sticks = []*Stick{}
w4.PALETTE[0] = 0xfcdeea
w4.PALETTE[1] = 0x012824
w4.PALETTE[2] = 0x265935
w4.PALETTE[3] = 0xff4d6d
player = &Player{
Position: Vector{80, 80},
Speed: Vector{},
Gamepad: w4.GAMEPAD1,
}
for i := 0; i < 4; i++ {
p, s := CreateRope(
Vector{0, rand.Float64()*40 + float64(i*40)},
Vector{160, rand.Float64()*40 + float64(i*40)},
15,
)
points = append(points, p...)
sticks = append(sticks, s...)
}
}
//go:export update
func update() {
frame += 1
*w4.DRAW_COLORS = 2
// w4.Text("Hello from Go!", 10, 10)
Simulate(points, sticks)
for _, s := range sticks {
s.Draw()
}
for _, p := range points {
p.Draw()
}
player.Update()
player.Draw()
}

59
player.go Normal file
View File

@ -0,0 +1,59 @@
package main
import "cart/w4"
type Player struct {
Position Vector
Speed Vector
PointGrabbed *Point
GrabTimeout uint
Offset float64
Gamepad *uint8
}
func (p *Player) Update() {
p.Speed.X = 0
if p.GrabTimeout > 0 {
p.GrabTimeout--
}
if *p.Gamepad&w4.BUTTON_LEFT != 0 {
p.Speed.X -= 1
}
if *p.Gamepad&w4.BUTTON_RIGHT != 0 {
p.Speed.X += 1
}
isJumping := *p.Gamepad&w4.BUTTON_DOWN == 0
p.Speed.Y += gravity
if p.PointGrabbed != nil {
p.Speed.Y = 0
p.Position = p.PointGrabbed.Position
if *p.Gamepad&w4.BUTTON_2 != 0 {
p.GrabTimeout = 10
if isJumping {
p.GrabTimeout = 5
p.Speed.Y = -4.5
}
p.PointGrabbed = nil
}
} else {
p.Position.Move(p.Speed.X, p.Speed.Y)
if *p.Gamepad&w4.BUTTON_DOWN == 0 && p.GrabTimeout == 0 {
for _, point := range points {
diff := p.Position.Sub(point.Position)
if diff.LenSquared() < 25 {
// nearPoints = append(nearPoints, p)
*w4.DRAW_COLORS = 0x44
w4.Rect(int(point.Position.X), int(point.Position.Y), 3, 3)
p.PointGrabbed = point
point.PreviousPosition.Move(-p.Speed.X*2, -p.Speed.Y*2)
break
}
}
}
}
}
func (p *Player) Draw() {
*w4.DRAW_COLORS = 0x34
w4.Rect(int(p.Position.X)-4, int(p.Position.Y)-4, 8, 8)
}

104
ropes.go Normal file
View File

@ -0,0 +1,104 @@
package main
import (
"cart/w4"
"math"
"strconv"
)
type Point struct {
Position Vector
PreviousPosition Vector
IsLocked bool
TimeOffset uint64
}
func (p *Point) Draw() {
*w4.DRAW_COLORS = 0x31
fr := (frame + p.TimeOffset) % 60
if fr > 20 {
*w4.DRAW_COLORS = 0x32
}
if fr > 40 {
*w4.DRAW_COLORS = 0x34
}
w4.Oval(int(p.Position.X)-2, int(p.Position.Y)-2, 4, 4)
}
type Stick struct {
PointA *Point
PointB *Point
Length float64
}
func (s *Stick) Draw() {
*w4.DRAW_COLORS = 0x3
w4.Line(int(s.PointA.Position.X), int(s.PointA.Position.Y),
int(s.PointB.Position.X), int(s.PointB.Position.Y))
}
func Simulate(points []*Point, sticks []*Stick) {
for _, p := range points {
if !p.IsLocked {
positionBeforeUpdate := p.Position
diff := p.Position.Sub(p.PreviousPosition)
p.Position = p.Position.Sum(diff)
p.Position.Y += gravity
p.PreviousPosition = positionBeforeUpdate
}
}
cycles := 20
for i := 0; i < cycles; i++ {
for _, s := range sticks {
centerX := (s.PointA.Position.X + s.PointB.Position.X) / 2
centerY := (s.PointA.Position.Y + s.PointB.Position.Y) / 2
diff := s.PointA.Position.Sub(s.PointB.Position)
direction := diff.Normalized()
if !s.PointA.IsLocked {
s.PointA.Position.X = centerX + direction.X*s.Length/2
s.PointA.Position.Y = centerY + direction.Y*s.Length/2
}
if !s.PointB.IsLocked {
s.PointB.Position.X = centerX - direction.X*s.Length/2
s.PointB.Position.Y = centerY - direction.Y*s.Length/2
}
}
}
}
func CreateRope(start Vector, end Vector, divisions int) ([]*Point, []*Stick) {
var points []*Point = []*Point{}
var sticks []*Stick = []*Stick{}
for i := 0; i <= divisions; i++ {
k := float64(i) / float64(divisions)
diffX := end.X - start.X
diffY := end.Y - start.Y
posX := start.X + diffX*k
posY := start.Y + diffY*k
pos := Vector{posX, posY}
// w4.Trace("Point created at " + pos.String())
point := Point{
Position: pos,
PreviousPosition: pos,
IsLocked: (i == 0 || i == divisions),
TimeOffset: lightIndex * 153,
}
lightIndex++
if i != 0 {
lastPoint := points[len(points)-1]
diffX := pos.X - lastPoint.Position.X
diffY := pos.Y - lastPoint.Position.Y
len := math.Sqrt(diffX*diffX + diffY*diffY)
w4.Trace("Length between points is " + strconv.FormatFloat(len, 'f', 3, 64))
stick := Stick{
PointA: lastPoint,
PointB: &point,
Length: len,
}
sticks = append(sticks, &stick)
}
points = append(points, &point)
}
return points, sticks
}

24
target.json Normal file
View File

@ -0,0 +1,24 @@
{
"llvm-target": "wasm32--wasi",
"build-tags": [ "tinygo.wasm" ],
"goos": "js",
"goarch": "wasm",
"linker": "wasm-ld",
"libc": "wasi-libc",
"cflags": [
"--target=wasm32--wasi",
"--sysroot={root}/lib/wasi-libc/sysroot",
"-Oz"
],
"ldflags": [
"--allow-undefined",
"--no-demangle",
"--import-memory",
"--initial-memory=65536",
"--max-memory=65536",
"--stack-first",
"-zstack-size=14752",
"--strip-all"
],
"wasm-abi": "js"
}

63
vectors.go Normal file
View File

@ -0,0 +1,63 @@
package main
import (
"math"
"strconv"
)
type Vector struct {
X float64
Y float64
}
func (v *Vector) String() string {
return "(" + strconv.FormatFloat(v.X, 'f', 3, 64) + "; " + strconv.FormatFloat(v.Y, 'f', 3, 64) + ")"
}
func (v *Vector) LenSquared() float64 {
return v.X*v.X + v.Y*v.Y
}
func (v *Vector) Len() float64 {
return math.Sqrt(v.LenSquared())
}
func (v *Vector) Sum(v2 Vector) Vector {
return Vector{
v.X + v2.X,
v.Y + v2.Y,
}
}
func (v *Vector) Sub(v2 Vector) Vector {
return Vector{
v.X - v2.X,
v.Y - v2.Y,
}
}
func (v *Vector) MulScalar(c float64) Vector {
return Vector{
v.X * c,
v.Y * c,
}
}
func (v *Vector) DivScalar(c float64) Vector {
return Vector{
v.X * c,
v.Y * c,
}
}
func (v *Vector) Normalized() Vector {
len := math.Sqrt(v.X*v.X + v.Y*v.Y)
x := v.X / len
y := v.Y / len
return Vector{x, y}
}
func (v *Vector) Move(x float64, y float64) {
v.X += x
v.Y += y
}

141
w4/wasm4.go Normal file
View File

@ -0,0 +1,141 @@
//
// WASM-4: https://wasm4.org/docs
package w4
import "unsafe"
// ┌───────────────────────────────────────────────────────────────────────────┐
// │ │
// │ Platform Constants │
// │ │
// └───────────────────────────────────────────────────────────────────────────┘
const SCREEN_SIZE int = 160
// ┌───────────────────────────────────────────────────────────────────────────┐
// │ │
// │ Memory Addresses │
// │ │
// └───────────────────────────────────────────────────────────────────────────┘
var PALETTE = (*[4]uint32)(unsafe.Pointer(uintptr(0x04)))
var DRAW_COLORS = (*uint16)(unsafe.Pointer(uintptr(0x14)))
var GAMEPAD1 = (*uint8)(unsafe.Pointer(uintptr(0x16)))
var GAMEPAD2 = (*uint8)(unsafe.Pointer(uintptr(0x17)))
var GAMEPAD3 = (*uint8)(unsafe.Pointer(uintptr(0x18)))
var GAMEPAD4 = (*uint8)(unsafe.Pointer(uintptr(0x19)))
var MOUSE_X = (*int16)(unsafe.Pointer(uintptr(0x1a)))
var MOUSE_Y = (*int16)(unsafe.Pointer(uintptr(0x1c)))
var MOUSE_BUTTONS = (*uint8)(unsafe.Pointer(uintptr(0x1e)))
var SYSTEM_FLAGS = (*uint8)(unsafe.Pointer(uintptr(0x1f)));
var NETPLAY = (*uint8)(unsafe.Pointer(uintptr(0x20)));
var FRAMEBUFFER = (*[6400]uint8)(unsafe.Pointer(uintptr(0xa0)))
const BUTTON_1 byte = 1
const BUTTON_2 byte = 2
const BUTTON_LEFT byte = 16
const BUTTON_RIGHT byte = 32
const BUTTON_UP byte = 64
const BUTTON_DOWN byte = 128
const MOUSE_LEFT byte = 1
const MOUSE_RIGHT byte = 2
const MOUSE_MIDDLE byte = 4
const SYSTEM_PRESERVE_FRAMEBUFFER byte = 1
const SYSTEM_HIDE_GAMEPAD_OVERLAY byte = 2
// ┌───────────────────────────────────────────────────────────────────────────┐
// │ │
// │ Drawing Functions │
// │ │
// └───────────────────────────────────────────────────────────────────────────┘
/** Copies pixels to the framebuffer. */
//go:export blit
func Blit(sprite *byte, x int, y int, width uint, height uint, flags uint)
/** Copies a subregion within a larger sprite atlas to the framebuffer. */
//go:export blitSub
func BlitSub(sprite *byte, x int, y int, width uint, height uint,
srcX uint, srcY uint, stride int, flags uint)
const BLIT_2BPP = 1
const BLIT_1BPP = 0
const BLIT_FLIP_X = 2
const BLIT_FLIP_Y = 4
const BLIT_ROTATE = 8
/** Draws a line between two points. */
//go:export line
func Line(x1 int, y1 int, x2 int, y2 int)
/** Draws a horizontal line. */
//go:export hline
func HLine(x int, y int, len uint)
/** Draws a vertical line. */
//go:export vline
func VLine(x int, y int, len uint)
/** Draws an oval (or circle). */
//go:export oval
func Oval(x int, y int, width uint, height uint)
/** Draws a rectangle. */
//go:export rect
func Rect(x int, y int, width uint, height uint)
/** Draws text using the built-in system font. */
//go:export textUtf8
func Text(text string, x int, y int)
// ┌───────────────────────────────────────────────────────────────────────────┐
// │ │
// │ Sound Functions │
// │ │
// └───────────────────────────────────────────────────────────────────────────┘
/** Plays a sound tone. */
//go:export tone
func Tone(frequency uint, duration uint, volume uint, flags uint)
const TONE_PULSE1 = 0
const TONE_PULSE2 = 1
const TONE_TRIANGLE = 2
const TONE_NOISE = 3
const TONE_MODE1 = 0
const TONE_MODE2 = 4
const TONE_MODE3 = 8
const TONE_MODE4 = 12
const TONE_PAN_LEFT = 16
const TONE_PAN_RIGHT = 32
// ┌───────────────────────────────────────────────────────────────────────────┐
// │ │
// │ Storage Functions │
// │ │
// └───────────────────────────────────────────────────────────────────────────┘
/** Reads up to `size` bytes from persistent storage into the pointer `destPtr`. */
//go:export diskr
func DiskR(ptr unsafe.Pointer, count uint) uint
/** Writes up to `size` bytes from the pointer `srcPtr` into persistent storage. */
//go:export diskw
func DiskW(src unsafe.Pointer, count uint) uint
// ┌───────────────────────────────────────────────────────────────────────────┐
// │ │
// │ Other Functions │
// │ │
// └───────────────────────────────────────────────────────────────────────────┘
/** Prints a message to the debug console. */
//go:export traceUtf8
func Trace(str string)
// TinyGo requires a main function, so provide one
//go:linkname main main.main
func main() {}