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