lentejanumerica/src/darena.gmo

611 lines
12 KiB
Plaintext

# darena
an open-ended game of rocks and sand
the sandy space contains a bunch of rocks in either one of two possible colors, and yourself.
push and arrange the rocks to prototype and build your own computer, cellular automata, 1-bit art, etc.
=> ./img/screenshot_darena.png screenshot of the game. it shows a square clear space inside a textured world. inside the square there are several round rocks of two possible colors. there is a small platform, and a person.
what will you do with the sand?
# about
darena was developed as an exercise to learn about the
=> https://wiki.xxiivv.com/site/uxn.html uxn virtual machine and assembly language
as part of the {s-camino} practice.
you can play with it in the web, thanks to the webuxn port by aduros:
=> https://aduros.com/webuxn/?rom=roms/darena.rom darena via webuxn
=> https://github.com/aduros/webuxn webuxn repository
# features
* a character that moves in all four cardinal directions
* rocks that can be pushed around, unless they are colliding with other rocks or the borders of the "arena"
* a toggling station where rocks can be switched to the other color
* realistic animation: the character slows down when pushing rocks, recovering their original speed when walking
# controls
use the arrow keys for cartesian movement within the space
# possible "improvements"
* add an "official" pulling option (you can pull rocks right now, but in a hacky way)
* save rocks state (position and color) to file
* optimize the use of memory for rock colors, using one bit per rock instead of one byte
# the code
this iteration has 31 rocks and a single toggling station where one can switch their color/state.
last updated: sjm-1659d3 (changed JNZ to JCN)
you can also find the code in the
=> https://git.sr.ht/~rabbits/uxn/log/master/item/projects/demos/darena.usm uxn repo
```darena.usm
( darena.usm )
( an open-ended game of rocks and sand )
( cc0 sejo 12021 )
%DEBUG { .Console/byte DEO #0a .Console/char DEO }
%DEBUG2 { .Console/short DEO2 #0a .Console/char DEO }
( parameters )
%nrocks { #1f }
%nrocks-1 { #1e }
%nrocks_mask { #1f }
%minposx { #0f }
%minposy { #0f }
%maxposx { #f1 }
%maxposy { #f1 }
%anispeedmask_normal { #03 }
%anispeedmask_slow { #07 }
%c_color_normal { #33 }
%c_color_flipx { #73 }
%index_norock { #ff }
( output macros )
%out_screen_x { LDA #00 SWP .Screen/x DEO2 } ( ;addr )
%out_screen_y { LDA #00 SWP .Screen/y DEO2 } ( ;addr )
( helper macros )
%get_bit_n { SFT #01 AND }
%get_nibble_h { #04 SFT #0f AND }
%get_nibble_l { #0f AND }
%is_bit_n_set { get_bit_n #01 EQU }
%set_animate { #01 ;c_state LDA ORA ;c_state STA }
%rst_animate { #00 ;c_state STA }
( devices )
|00 @System [ &vector $2 &wst $1 &rst $1 &pad $4 &r $2 &g $2 &b $2 ]
|10 @Console [ &pad $8 &char $1 &byte $1 &short $2 &string $2 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &color $1 ]
|80 @Controller [ &vector $2 &button $1 &key $1 ]
( variables )
|0000
@c_pos [ &x $1 &y $1 ] ( character position )
@c_speed [ &x $1 &y $1 ] ( character speed )
@c_color [ $1 ] ( character color )
@c_sprite [ $2 ] ( character sprite addr )
@c_state [ $1 ] ( high_nibble: animation pointer, bit0: is_animated )
@f_count [ $1 ] ( frame counter )
@ani_speedmask [ $1 ] ( animation speed mask )
@r_speed_x [ $f ]
@r_speed_y [ $f ]
@tog [ &x $1 &y $1 &state $1 ] ( toggle station state )
( program )
|0100 @reset ( -> )
#f396 .System/r DEO2
#e263 .System/g DEO2
#9030 .System/b DEO2
;on_frame .Screen/vector DEO2
( init character )
#50 ;c_pos/x STA
#10 ;c_pos/y STA
#00 ;c_speed/x STA
#00 ;c_speed/y STA
c_color_normal ;c_color STA
;s_monitx_stepfront0 ;c_sprite STA2
rst_animate
anispeedmask_normal ;ani_speedmask STA
( init toggler )
#27 ;tog/x STA
#27 ;tog/y STA
#00 ;tog/state STA
( init background )
;init_bg JSR2
BRK
@on_frame ( -> )
;f_count LDA #01 ADD DUP ;f_count STA ( increase frame counter )
;ani_speedmask LDA ( mask with animation speed mask )
AND #00 EQU ,update_frame JCN ( jump to update if it's time )
BRK
@update_frame
( check keyboard )
;check_keys JSR2
( animate character sprite )
;animate_c JSR2
( clear sprites )
;clear JSR2
( update character vars )
;update_c/run JSR2
( update rocks + stand )
;update_r/run JSR2
( draw )
;draw JSR2
BRK
@clear
( clear rocks )
;s_clear .Screen/addr DEO2
nrocks #00
&rocks_loop
DUP ( get rocks_x[i] )
;rocks_x ROT #00 SWP ADD2 out_screen_x
DUP ( get rocks_y[i] )
;rocks_y ROT #00 SWP ADD2 out_screen_y
#30 .Screen/color DEO
#01 ADD
DUP2
NEQ ,&rocks_loop JCN
POP2
( clear character )
;clear_c JSR2
JMP2r
@draw
( draw toggler )
;tog/x out_screen_x
;tog/x out_screen_y
;s_stand .Screen/addr DEO2
#23 .Screen/color DEO
( draw rocks )
;s_bola .Screen/addr DEO2
nrocks #00
&rocks_loop
DUP ( get rocks_x[i] )
;rocks_x ROT #00 SWP ADD2 out_screen_x
DUP ( get rocks_y[i] )
;rocks_y ROT #00 SWP ADD2 out_screen_y
( DUP ( get color bitwise ) )
( ;r_color LDA SWP get_bit_n #31 ADD .Screen/color DEO )
DUP
;r_color ROT #00 SWP ADD2 LDA #31 ADD .Screen/color DEO
#01 ADD
DUP2
NEQ ,&rocks_loop JCN
POP2
( draw character )
;draw_c JSR2
JMP2r
@check_keys
#00 ;c_speed/x STA
#00 ;c_speed/y STA
.Controller/button DEI #07 is_bit_n_set ,&der JCN
.Controller/button DEI #06 is_bit_n_set ,&izq JCN
.Controller/button DEI #05 is_bit_n_set ,&aba JCN
.Controller/button DEI #04 is_bit_n_set ,&arr JCN
rst_animate
JMP2r
&der
#01 ;c_speed/x STA
set_animate
c_color_normal ;c_color STA
;s_monitx_stepside0 ;c_sprite STA2
JMP2r
&izq
#ff ;c_speed/x STA
set_animate
c_color_flipx ;c_color STA
;s_monitx_stepside0 ;c_sprite STA2
JMP2r
&aba
#01 ;c_speed/y STA
set_animate
c_color_normal ;c_color STA
;s_monitx_stepfront0 ;c_sprite STA2
JMP2r
&arr
#ff ;c_speed/y STA
set_animate
c_color_normal ;c_color STA
;s_monitx_stepback0 ;c_sprite STA2
JMP2r
&end
JMP2r
( sub-routines )
( in: sourcex, source y, index, rangex, rangey )
( puts in the stack the index of rock collisioned with )
@collision_rocks
&range_y $1
&range_x $1
&src_i $1
&src_x $1
&src_y $1
&rock_x $1
&rock_y $1
&run
,&range_y STR
,&range_x STR
,&src_i STR
,&src_y STR
,&src_x STR
( check collision with rocks )
( nrocks #00 )
,&src_i LDR nrocks_mask AND DUP #01 ADD nrocks_mask AND
&rocks_loop
DUP ( get rocks_x[i] )
;rocks_x ROT #00 SWP ADD2 LDA ,&rock_x STR
DUP ( get rocks_y[i] )
;rocks_y ROT #00 SWP ADD2 LDA ,&rock_y STR
,&src_x LDR ,&rock_x LDR ,&range_x LDR SUB GTH ( if sx > rx - 8 )
,&src_x LDR ,&rock_x LDR ,&range_x LDR ADD LTH ( if sx < rx + 8 )
,&src_y LDR ,&rock_y LDR ,&range_y LDR SUB GTH ( if sy > ry - 8 )
,&src_y LDR ,&rock_y LDR ,&range_y LDR ADD LTH ( if sy < ry + 8 )
ADD ADD ADD #04 EQU ,&found JCN
#01 ADD nrocks_mask AND
DUP2
NEQ ,&rocks_loop JCN
POP2
#ff
JMP2r
&found
SWP POP ( remove loop limit )
DUP ;&src_i LDA NEQ ,&end JCN ( check if result is the same as index )
POP #ff
JMP2r
&end
JMP2r
@update_c ( update character position )
&new_x $1
&new_y $1
&rock_i $1
&rock_x $1
&rock_y $1
&run
;c_speed/x LDA ;c_pos/x LDA ADD
,&new_x STR
;c_speed/y LDA ;c_pos/y LDA ADD
,&new_y STR
anispeedmask_normal ;ani_speedmask STA
&check_x
( check collision with borders )
,&new_x LDR minposx EQU ;&noup_x JCN2
,&new_x LDR maxposx EQU ;&noup_x JCN2
( check collision with rocks )
,&new_x LDR ,&new_y LDR index_norock #09 #06
;collision_rocks/run JSR2
( if it is colliding with rock, check further )
DUP #ff NEQ ,&check_x_collision JCN
POP
,&update_x JMP
&check_x_collision
( DUP DEBUG )
( slow down and save rock index )
anispeedmask_slow ;ani_speedmask STA
,&rock_i STR
( check if rock collides with others )
;rocks_x #00 ,&rock_i LDR ADD2 LDA ,&rock_x STR
;rocks_y #00 ,&rock_i LDR ADD2 LDA ,&rock_y STR
,&rock_x LDR ,&rock_y LDR ,&rock_i LDR #09 #06
;collision_rocks/run JSR2
( DUP DEBUG )
( if it is colliding, then skip adding x )
DUP #ff NEQ ,&check_y JCN
POP
( if not, check for borders )
;&rock_x LDA minposx EQU ;&noup_x JCN2
;&rock_x LDA maxposx EQU ;&noup_x JCN2
( move rock with same speed as c )
;&rock_x LDA ;c_speed/x LDA ADD
;rocks_x #00 ;&rock_i LDA ADD2
STA
&update_x
;&new_x LDA ;c_pos/x STA
,&check_y JMP
&noup_x
&check_y
( check collision with borders )
;&new_y LDA minposy EQU ;&noup_y JCN2
;&new_y LDA maxposy EQU ;&noup_y JCN2
( check collision with rocks )
;&new_x LDA ;&new_y LDA index_norock #06 #09
;collision_rocks/run JSR2
( if it is colliding with rock, check further )
DUP #ff NEQ ,&check_y_collision JCN
POP
,&update_y JMP
&check_y_collision
( DUP DEBUG )
anispeedmask_slow ;ani_speedmask STA
;&rock_i STA
( check if rock collides with others )
;rocks_x #00 ;&rock_i LDA ADD2 LDA ;&rock_x STA
;rocks_y #00 ;&rock_i LDA ADD2 LDA ;&rock_y STA
;&rock_x LDA ;&rock_y LDA ;&rock_i LDA #06 #09
;collision_rocks/run JSR2
( DUP DEBUG )
( if it is colliding, then skip adding y )
DUP #ff NEQ ,&noup_y JCN
POP
( if not, check for borders )
;&rock_y LDA minposx EQU ;&noup_y JCN2
;&rock_y LDA maxposx EQU ;&noup_y JCN2
( if not colliding, then move rock with same speed as c )
;&rock_y LDA ;c_speed/y LDA ADD
;rocks_y #00 ;&rock_i LDA ADD2
STA
&update_y
;&new_y LDA ;c_pos/y STA
JMP2r
&noup_y
JMP2r
@update_r
&rock_i $1
&run
( check collision with rocks )
;tog/x LDA ;tog/y LDA index_norock #02 #02
;collision_rocks/run JSR2
( if it is colliding with rock, check if it needs to change state )
DUP #ff NEQ ,&change_state JCN
( DUP DEBUG )
( if there's no collision, reset toggler )
POP
#00 ;tog/state STA
JMP2r
&change_state
( DUP DEBUG )
,&rock_i STR
;tog/state LDA ,&done JCN ( don't toggle if state is active )
;r_color #00 ,&rock_i LDR ADD2 DUP2 STH2
LDA #01 EOR STH2r STA
#01 ;tog/state STA
&done
JMP2r
@animate_c
( is bit0 -animate- on? )
;c_state LDA DUP #00 get_bit_n #01 NEQ ,&s_no_animate JCN
( increment and save animation pointer )
&s_animate
DUP
get_nibble_h #01 ADD #03 AND #40 SFT
SWP get_nibble_l ORA
;c_state STA
JMP2r
&s_no_animate
get_nibble_h #0f AND ;c_state STA
JMP2r
@draw_c ( draw character )
#00 ;c_state LDA get_nibble_h #08 MUL
;c_sprite LDA2 ADD2 .Screen/addr DEO2
;c_pos/x out_screen_x
;c_pos/y out_screen_y
;c_color LDA .Screen/color DEO
JMP2r
@clear_c ( clear character )
;s_clear .Screen/addr DEO2
;c_pos/x out_screen_x
;c_pos/y out_screen_y
#30 .Screen/color DEO
JMP2r
@init_bg
( init bg )
;s_border .Screen/addr DEO2
.Screen/height DEI2 #0000 STH2
&vertical0loop
DUP2
STH2r
DUP2 .Screen/y DEO2
.Screen/width DEI2 #0000 STH2
&horizontal0loop
DUP2
STH2r
DUP2 .Screen/x DEO2
#23 .Screen/color DEO
#0008 ADD2 DUP2 STH2
GTH2 ,&horizontal0loop JCN
STH2r POP2 POP2
#0008 ADD2 DUP2 STH2
GTH2 ,&vertical0loop JCN
STH2r
POP2 POP2
( arena )
;s_clear .Screen/addr DEO2
#00 maxposy #00 minposy STH2
&vertical0loop_clear
DUP2
STH2r
DUP2 .Screen/y DEO2
#00 maxposx #00 minposx STH2
&horizontal0loop_clear
DUP2
STH2r
DUP2 .Screen/x DEO2
#20 .Screen/color DEO
#0008 ADD2 DUP2 STH2
GTH2 ,&horizontal0loop_clear JCN
STH2r POP2 POP2
#0008 ADD2 DUP2 STH2 GTH2 ,&vertical0loop_clear JCN
STH2r
POP2 POP2
JMP2r
( rocks )
@rocks_x [ 25 30 42 50 67 90 98 e8 20 43 43 57 5a 7f bc a5
e5 dd a2 20 b7 9b 38 e8 33 43 63 b7 aa cf bc ]
@rocks_y [ 60 48 34 56 23 65 65 65 ba e9 24 22 72 91 22 c5
25 30 42 50 67 90 98 e8 20 43 43 57 5a 7f bc ]
@r_color [ 00 01 01 00 00 00 01 01 01 01 00 00 01 01 00 00
01 00 01 00 00 01 00 01 01 01 01 01 00 00 00 ]
( sprites )
@s_clear [ 0000 0000 0000 0000 ]
@s_border [ 3288 7e83 780d e013 ]
@s_bola [ 3c4e 9ffd f962 3c00 ]
@s_stand [ 0000 0000 0024 7eff ]
@s_stand_original [ 0000 0000 0000 3c7e ]
@s_monitx [ 3c7e 5a7f 1b3c 5a18 ]
@s_monitx_back [ 3c7e 7efe d83c 5a18 ]
@s_monitx_stepfront0 [ 3c7e 5a7f 1b3c 5a18 ]
@s_monitx_stepfront1 [ 3c7e 5a7f 1b3c 5a10 ]
@s_monitx_stepfront2 [ 3c7e 5a7f 1b3c 5a18 ]
@s_monitx_stepfront3 [ 3c7e 5a7f 1b3c 5a08 ]
@s_monitx_stepback0 [ 3c7e 7efe d83c 5a18 ]
@s_monitx_stepback1 [ 3c7e 7efe d83c 5a10 ]
@s_monitx_stepback2 [ 3c7e 7efe d83c 5a18 ]
@s_monitx_stepback3 [ 3c7e 7efe d83c 5a08 ]
@s_monitx_stepside0 [ 1c3c 7afc d81c 1818 ]
@s_monitx_stepside1 [ 1c3c 7afc d81c 1828 ]
@s_monitx_stepside2 [ 1c3c 7afc d81c 3810 ]
@s_monitx_stepside3 [ 1c3c 7afc d81c 1814 ]
```
## llega(n) aquí
=> ./s-camino.gmi {s-camino}
=> ./tiempo.gmi {tiempo}