module gamepad
var left: bool
var right: bool
var up: bool
var down: bool
var button_a: bool
var button_b: bool
var button_c: bool
var button_d: bool
end module
class tile_layer
var tilemap: tilemap
var tileset: tileset
var io_tilemap: io_tilemap
var io_tileset_addresses: int[size 1024]
var x: int, y: int
var screen_space: bool
var wrap: bool
var _tile_dimension: int
func new(io_tilemap: io_tilemap, io_tileset_addresses: int[size 1024])
io_tilemap -> .io_tilemap
io_tileset_addresses -> .io_tileset_addresses
end func
func load_tileset(tileset: tileset)
tileset -> .tileset
var io_tilemap: io_tilemap
.io_tilemap -> io_tilemap
var io_tileset_addresses: int[size 1024]
.io_tileset_addresses -> io_tileset_addresses
if tileset = null then
0 -> io_tilemap.jumbo
0 -> io_tilemap.tile_count
return
end if
var old_tile_count: pair
io_tilemap.tile_count -> old_tile_count
var tile_count: pair, p: pair
if tileset.jumbo then
1 -> io_tilemap.jumbo
16 -> ._tile_dimension
else
0 -> io_tilemap.jumbo
8 -> ._tile_dimension
end if
to_pair(tileset.tiles.size) -> tile_count
if tile_count > 1024
do 1024 -> tile_count
tile_count -> io_tilemap.tile_count
0 -> p
loop
if p >= tile_count
do drop
var tile: tile
tileset.tiles[p] -> tile
if tile <> null then
to_address(tile.pixels) + 3 -> io_tileset_addresses[p]
end if
to_pair(p + 1) -> p
end loop
loop
if p >= old_tile_count
do drop
0 -> io_tileset_addresses[p]
to_pair(p + 1) -> p
end loop
end func
func load_tilemap(tilemap: tilemap)
tilemap -> .tilemap
var io_tilemap: io_tilemap
.io_tilemap -> io_tilemap
if tilemap = null then
0 -> io_tilemap.col_count
0 -> io_tilemap.row_count
0 -> io_tilemap.tile_codes_address
0 -> io_tilemap.edge_mode
return
end if
tilemap.col_count -> io_tilemap.col_count
tilemap.row_count -> io_tilemap.row_count
to_address(tilemap.tile_codes) + 3 -> io_tilemap.tile_codes_address
end func
func _render()
var io_tilemap: io_tilemap
.io_tilemap -> io_tilemap
if .tilemap <> null then
var x: int, y: int
var x_pair: pair, y_pair: pair
.x -> x
.y -> y
if not .screen_space then
x - engine::camera_x -> x
y - engine::camera_y -> y
end if
to_pair(x) -> x_pair
to_pair(y) -> y_pair
var clip_edge: bool
not .wrap -> clip_edge
to_byte(clip_edge) -> io_tilemap.edge_mode
if clip_edge and (x_pair <> x or y_pair <> y) then
-16384 -> io_tilemap.x
else
x_pair -> io_tilemap.x
y_pair -> io_tilemap.y
end if
end if
end func
func get_tile_at_colrow(col: int, row: int): pair
var tilemap: tilemap
.tilemap -> tilemap
var tile_code: pair
var col_count: int, row_count: int
tilemap.col_count -> col_count
tilemap.row_count -> row_count
if col >= 0 and col < col_count and row >= 0 and row < row_count then
tilemap.tile_codes[row * col_count + col] -> tile_code
else
-1 -> tile_code
end if
return tile_code
end func
func set_tile_at_colrow(col: int, row: int, tile_code: pair)
var tilemap: tilemap
.tilemap -> tilemap
var col_count: int, row_count: int
tilemap.col_count -> col_count
tilemap.row_count -> row_count
if col >= 0 and col < col_count and row >= 0 and row < row_count then
tile_code -> tilemap.tile_codes[row * col_count + col]
end if
end func
func get_tile_at_xy(x: int, y: int): pair
var tile_dimension: int
._tile_dimension -> tile_dimension
var result: pair
.get_tile_at_colrow(x // tile_dimension, y // tile_dimension) -> result
return result
end func
func set_tile_at_xy(x: int, y: int, tile_code: pair)
var tile_dimension: int
._tile_dimension -> tile_dimension
.set_tile_at_colrow(x // tile_dimension, y // tile_dimension, tile_code)
end func
func get_color_at_xy(x: int, y: int): byte
var tile_code: pair, tx: int, ty: int
var tile: tile, color: byte
var tileset: tileset
.tileset -> tileset
var tile_dimension: int
._tile_dimension -> tile_dimension
.get_tile_at_colrow(x // tile_dimension, y // tile_dimension) -> tile_code
var tile_index: pair
to_pair(math::bit_and(tile_code, $03ff)) -> tile_index
if tile_code >= 0 and tile_index < tileset.tiles.size then
tileset.tiles[tile_index] -> tile
if tile <> null then
x %% tile_dimension -> tx
y %% tile_dimension -> ty
tile.pixels[ty * tile_dimension + tx] -> color
return color
end if
end if
0 -> color
return color
end func
end class
module engine
var camera_x: int, camera_y: int
var tile_layer_a: tile_layer
var tile_layer_b: tile_layer
var tile_layer_c: tile_layer
var frame_counter: pair
var _thinklist_head: actor
var _thinklist_tail: actor
var _renderlist_fence: actor
var _insertion_points: actor[size 4]
var _last_render_actor: actor
var _actor_count: int
var _used_sprites: int
var _busy: byte
func init()
0 -> io::paint_mode
io::frame_counter -> engine::frame_counter
null -> engine::_thinklist_head
null -> engine::_thinklist_tail
var insertion_points: actor[size 4]
new actor[size 4]() -> insertion_points
insertion_points -> engine::_insertion_points
var renderlist_fence: actor
new actor() -> renderlist_fence
renderlist_fence -> renderlist_fence._renderlist_prev_link
renderlist_fence -> renderlist_fence._renderlist_next_link
renderlist_fence -> insertion_points[0]
renderlist_fence -> insertion_points[1]
renderlist_fence -> insertion_points[2]
renderlist_fence -> insertion_points[3]
renderlist_fence -> engine::_last_render_actor
renderlist_fence -> engine::_renderlist_fence
0 -> engine::_actor_count
0 -> engine::_used_sprites
io::background_color <-- 13
new tile_layer(io::tilemap_a, io::tileset_a_addresses)
| -> engine::tile_layer_a
new tile_layer(io::tilemap_b, io::tileset_b_addresses)
| -> engine::tile_layer_b
new tile_layer(io::tilemap_c, io::tileset_c_addresses)
| -> engine::tile_layer_c
end func
func load_actor_spot(spot: s_actor_spot): actor
var actor: actor
if spot.actor_factory <> null then
spot.actor_factory() -> actor
else
new actor() -> actor
end if
actor.apply_spot(spot)
engine::add_actor_behind(actor)
return actor
end func
func load_actor_spots(spots: s_actor_spot[])
var i, size
spots.size -> size
0 -> i
loop
if i >= size
do drop
engine::load_actor_spot(spots[i])
i + 1 -> i
end loop
end func
func clear_actors()
var old_busy: byte
engine::_busy -> old_busy
if old_busy = 3
do return
if old_busy = 2
do kernel::fail("Operation is not allowed")
3 -> engine::_busy
var actor: actor
engine::_thinklist_head -> actor
loop
if actor = null
do drop
if actor._renderlist_prev_link <> null then
null -> actor._renderlist_prev_link
null -> actor._renderlist_next_link
engine::_actor_count - 1 -> engine::_actor_count
actor.on_removed()
end if
actor._thinklist_next_link -> actor
end loop
engine::_prune_thinklist()
var renderlist_fence: actor
engine::_renderlist_fence -> renderlist_fence
renderlist_fence -> renderlist_fence._renderlist_prev_link
renderlist_fence -> renderlist_fence._renderlist_next_link
var insertion_points: actor[size 4]
engine::_insertion_points -> insertion_points
renderlist_fence -> insertion_points[0]
renderlist_fence -> insertion_points[1]
renderlist_fence -> insertion_points[2]
renderlist_fence -> insertion_points[3]
renderlist_fence -> engine::_last_render_actor
0 -> engine::_actor_count
old_busy -> engine::_busy
end func
func add_actor(actor: actor)
engine::_add_actor(actor, false)
actor.on_added()
end func
func add_actor_behind(actor: actor)
engine::_add_actor(actor, true)
actor.on_added()
end func
func _add_actor(actor: actor, behind: bool)
if engine::_busy >= 2
do kernel::fail("Operation is not allowed")
if actor._renderlist_prev_link <> null
do kernel::fail("Actor already added")
if engine::_thinklist_tail = null then
actor -> engine::_thinklist_head
actor -> engine::_thinklist_tail
null -> actor._thinklist_next_link
elsif actor._thinklist_next_link = null
| and engine::_thinklist_tail <> actor then
actor -> engine::_thinklist_tail._thinklist_next_link
actor -> engine::_thinklist_tail
end if
var insertion_points: actor[size 4]
engine::_insertion_points -> insertion_points
var depth: byte
actor._depth -> depth
var insertion_point: actor
insertion_points[depth] -> insertion_point
if behind then
var next_insertion_point: actor
if depth < 3 then
insertion_points[depth + 1] -> next_insertion_point
else
engine::_renderlist_fence -> next_insertion_point
end if
if insertion_point = next_insertion_point then
false -> behind
else
next_insertion_point -> insertion_point
end if
end if
insertion_point -> actor._renderlist_next_link
insertion_point._renderlist_prev_link -> actor._renderlist_prev_link
actor -> actor._renderlist_prev_link._renderlist_next_link
actor -> insertion_point._renderlist_prev_link
if not behind then
actor -> insertion_points[depth]
loop
if depth = 0
do drop
to_byte(depth - 1) -> depth
if insertion_points[depth] = insertion_point
do actor -> insertion_points[depth]
end loop
end if
engine::_actor_count + 1 -> engine::_actor_count
end func
func remove_actor(actor: actor)
engine::_remove_actor(actor)
actor.on_removed()
end func
func _remove_actor(actor: actor)
if engine::_busy = 2
do kernel::fail("Operation is not allowed")
var insertion_points: actor[size 4]
var next_link: actor
engine::_insertion_points -> insertion_points
actor._renderlist_next_link -> next_link
if next_link = null or actor = engine::_renderlist_fence
do kernel::fail("Actor not added")
if insertion_points[0] = actor
do next_link -> insertion_points[0]
if insertion_points[1] = actor
do next_link -> insertion_points[1]
if insertion_points[2] = actor
do next_link -> insertion_points[2]
if insertion_points[3] = actor
do next_link -> insertion_points[3]
if engine::_last_render_actor = actor
do actor._renderlist_prev_link -> engine::_last_render_actor
actor._renderlist_prev_link -> actor._renderlist_next_link._renderlist_prev_link
actor._renderlist_next_link -> actor._renderlist_prev_link._renderlist_next_link
null -> actor._renderlist_prev_link
null -> actor._renderlist_next_link
engine::_actor_count - 1 -> engine::_actor_count
end func
func get_first_actor(): actor
var actor: actor
engine::_thinklist_head -> actor
loop
if actor = null or actor._renderlist_prev_link <> null
do drop
actor._thinklist_next_link -> actor
end loop
return actor
end func
func get_next_actor(current: actor): actor
var actor: actor
if current = null then
null -> actor
else
current._thinklist_next_link -> actor
loop
if actor = null or actor._renderlist_prev_link <> null
do drop
actor._thinklist_next_link -> actor
end loop
end if
return actor
end func
func _prune_thinklist()
var current: actor
var previous: actor
null -> previous
engine::_thinklist_head -> current
loop
if current = null then
if previous = null then
null -> engine::_thinklist_head
null -> engine::_thinklist_tail
else
previous -> engine::_thinklist_tail
null -> previous._thinklist_next_link
end if
drop
end if
var next: actor
current._thinklist_next_link -> next
if current._renderlist_prev_link = null then
null -> current._thinklist_next_link
else
if previous = null then
current -> engine::_thinklist_head
else
current -> previous._thinklist_next_link
end if
current -> previous
end if
next -> current
end loop
end func
func get_camera_cx(): int
var result
engine::camera_x + 160 -> result
return result
end func
func set_camera_cx(value: int)
value - 160 -> engine::camera_x
end func
func get_camera_cy(): int
var result
engine::camera_y + 112 -> result
return result
end func
func set_camera_cy(value: int)
value - 112 -> engine::camera_y
end func
func render()
if engine::_busy <> 0
do kernel::fail("Operation is not allowed")
2 -> engine::_busy
engine::tile_layer_a._render()
engine::tile_layer_b._render()
engine::tile_layer_c._render()
var frame_delta: pair
engine::frame_counter -> frame_delta
io::frame_counter -> engine::frame_counter
to_pair(engine::frame_counter - frame_delta) -> frame_delta
if frame_delta < 0
do 1 -> frame_delta
var renderlist_fence: actor
var end_actor: actor
engine::_renderlist_fence -> renderlist_fence
engine::_last_render_actor -> end_actor
var sprite_index: int
0 -> sprite_index
if end_actor = renderlist_fence then
renderlist_fence._renderlist_prev_link -> end_actor
end if
if end_actor <> renderlist_fence then
var sprites_left: byte
64 -> sprites_left
var actor: actor
end_actor -> actor
loop
actor._renderlist_next_link -> actor
if actor = renderlist_fence
do drop
if actor.visible then
to_byte(sprites_left - 1) -> sprites_left
if sprites_left = 0
do drop
end if
end loop
if sprites_left = 0 then
actor -> engine::_last_render_actor
else
loop
actor._renderlist_next_link -> actor
if actor.visible then
actor.render(sprite_index, frame_delta)
sprite_index + 1 -> sprite_index
to_byte(sprites_left - 1) -> sprites_left
if sprites_left = 0
do drop
end if
if actor = end_actor
do drop
end loop
actor -> engine::_last_render_actor
end if
64 -> sprites_left
end_actor -> actor
loop
actor._renderlist_next_link -> actor
if actor = renderlist_fence
do drop
if actor.visible then
actor.render(sprite_index, frame_delta)
sprite_index + 1 -> sprite_index
to_byte(sprites_left - 1) -> sprites_left
if sprites_left = 0
do drop
end if
end loop
end if
var i: int
sprite_index -> i
loop
if i >= engine::_used_sprites
do drop
if i >= 64
do drop
0 -> io::sprites[i].pixels_address
i + 1 -> i
end loop
sprite_index -> engine::_used_sprites
0 -> engine::_busy
end func
func wait_for_paint()
1 -> io::irq_pending
3 -> io::paint_mode
1 -> io::irq_wake_mask
kernel::sleep()
end func
func think()
if engine::_busy <> 0
do kernel::fail("Operation is not allowed")
1 -> engine::_busy
var buttons: byte
io::gamepad_0.buttons --> buttons
math::bit_and(buttons, 1) <> 0 -> gamepad::button_a
math::bit_and(buttons, 2) <> 0 -> gamepad::button_b
math::bit_and(buttons, 4) <> 0 -> gamepad::button_c
math::bit_and(buttons, 8) <> 0 -> gamepad::button_d
false -> gamepad::left
false -> gamepad::right
false -> gamepad::up
false -> gamepad::down
var p: pair
io::gamepad_0.x -> p
if p >= 512 then
true -> gamepad::right
elsif p <= -512 then
true -> gamepad::left
end if
io::gamepad_0.y -> p
if p >= 512 then
true -> gamepad::down
elsif p <= -512 then
true -> gamepad::up
end if
var actor: actor
engine::get_first_actor() -> actor
loop
if actor = null
do drop
if actor._renderlist_prev_link <> null
do actor.think()
engine::get_next_actor(actor) -> actor
end loop
engine::_prune_thinklist()
0 -> engine::_busy
end func
end module