: blah ' seremit task-emit ! ' log-emit task-echo ! ; ' seremit task-emit ! : start-repl activate blah s" .:: J O R T H ( jean forth) ::." type cr begin receive loadstring s" ok" type cr again ; task const REPL REPL start-repl 1 const ^ESC 28 const ^ENTER 29 const ^CTRL 51 const ^< 52 const ^> 56 const ^ALT 57 const ^SPACE 72 const ^UP 75 const ^LEFT 77 const ^RIGHT 80 const ^DOWN : wait-key ( k -- ) begin dup key-pressed not while suspend repeat drop ; : udelta ( u u -- u ) 2dup u> if swap -1 swap - + 1 + else swap - then ; : sleep ( count -- ) ticks swap begin over ticks udelta over u< while suspend repeat drop drop ; defer tick defer draw : defentity ( x y dir anim -- ) array , , , , ; : entity.x 3 cells + ; : entity.y 2 cells + ; : entity.dir cell + ; : entity.anim ; 0 const W 1 const E 2 const N 3 const S : dir>pos ( dir -- dx dy ) dup W = if drop -1 0 ret then dup E = if drop 1 0 ret then N = if 0 -1 else 0 1 then ; : frame ( s n e w ) b, b, b, b, ; array frames ( 0: car ) 3 1 0 2 frame ( 1: pete stand ) 5 7 7 5 frame ( 2: pete walk ) 6 8 8 6 frame : sprindex ( dir frame ) 2 << frames + + b@ ; : defstatic ( frame -- ) create b, does> b@ sprindex ; : defanim ( frame... framecount ticks-per-frame -- ) create b, dup b, 0 for b, next does> ( dir a -- ) dup dup 1 + b@ swap b@ ( dir a count tpf ) ticks swap / swap % ( dir a index ) 2 + + b@ sprindex ; 0 defstatic {car} 1 defstatic {pete-stand} 1 2 2 5 defanim {pete-walk} : ~ -1 ^ ; var player.state : f! ( b v flag -- ) r> dup @ ( b v val r: flag ) ratio ( range value -- f ) over swap clamp0 swap />ratio ; : range ( start end -- start range ) over - ; : >range r< >range r< ratio lerpr ; : lerp ( start end duration start -- i ) ticks udelta ( start end duration delta ) >ratio lerpr ; : triggered ( duration timer -- b ) dup r> @ ticks udelta ( duration delta ) 2dup <= if drop r< +! 1 else drop drop 0 then ; : now! ( timer -- ) ticks swap ! ; ( F O O T E R ) var footer-y 0 footer-y ! : draw-footer footer-y @ split-screen ; 0 const BLACK 1 const BLUE 2 const GREEN 3 const CYAN 4 const RED 5 const MAGENTA 6 const BROWN 7 const LGRAY 8 const DGRAY 9 const LBLUE 10 const LGREEN 11 const LCYAN 12 const PINK 13 const LMAGENTA 14 const YELLOW 15 const WHITE var text-color WHITE text-color ! : texty 7 swap @ >rot ticks ( from to duration start ) begin 4dup lerp r@ ! rot suspend repeat rdrop drop drop drop drop ; : show-footer 48 10 footer-y move-to ; : hide-footer 0 10 footer-y move-to ; : footer-wait show-footer ^ENTER wait-key ; ( todo: generic say" that handles newlines, gradual text display ) : say1 ( s -- ) clear text1 footer-wait ; : say2 ( s1 s2 -- ) clear text1 text0 footer-wait ; : say3 ( s1 s2 s3 -- ) clear text2 text1 text0 footer-wait ; : character ( iportrait color ) create , , does> dup @ text-color ! cell + @ draw-portrait ; 0 GREEN character pete 1 MAGENTA character mary 2 BROWN character chuck ( M O U S E ) var prevbutton : tick-debounce mousebuttons prevbutton ! ; 1 const MOUSEL : mousedown ( button -- bool ) mousebuttons & ; : clicked ( button -- bool ) dup mousedown not swap prevbutton @ & and ; ( M A P ) : +pos ( x1 y1 x2 y2 -- x y ) rot + swap ; var tileselect 8 const MAXTILE : mouseworldpos mousepos scrollpos +pos ; : world>tile 4 >> swap 4 >> swap ; : mousetile mouseworldpos world>tile ; : tile ( x y -- ptr ) mapsize drop * + map + ; 1 const WALKABLE 2 const DRIVABLE array tileflags ( grass ) WALKABLE b, ( dirt ) WALKABLE b, ( water ) 0 b, ( pavement ) WALKABLE DRIVABLE | b, ( brick ) 0 b, ( forest ) 0 b, ( roof ) 0 b, ( brick ) 0 b, ( window ) 0 b, : mapflag? ( x y flag -- b ) >rot tile b@ tileflags + b@ & ; : walkable? ( x y -- b ) WALKABLE mapflag? ; : drivable? ( x y -- b ) DRIVABLE mapflag? ; : tick-mapedit tileselect @ ^< key-pressed if 1 - then ^> key-pressed if 1 + then dup 0 < if drop MAXTILE then dup MAXTILE > if drop 0 then tileselect ! MOUSEL mousedown if tileselect @ mousetile tile b! then ; : copy-mapseg ( neww oldw y -- ) r> ( oldw neww r: y ) 2dup min >rot ( copyw neww oldw ) r@ * map + ( copyw neww src ) swap r< * map + ( copyw src dst ) swap ( newh neww oldw r: oldh ) 2dup < if 1 r< else r< 1 - 0 then ( newh neww copyw ystart ylim ) for 2dup i copy-mapseg next drop swap mapsize! ; : save-map ( filename -- ) fdeactivate swap overwrite mapsize swap fput fput mapsize * map fwrite factivate ; : load-map ( filename -- ) fdeactivate swap open fget fget 2dup * map fread mapsize! factivate ; ( J O B ) var MODE-MOVE var MODE-WAIT : listen-for-jobs activate blah begin receive MODE-WAIT @ ' tick redefine execute hide-footer MODE-MOVE @ ' tick redefine again ; task const JOB JOB listen-for-jobs ( T I C K ) : player.canmove? ( x y -- ) player.state DRIVING f@ if DRIVABLE else WALKABLE then mapflag? ; : move-player player entity.dir @ dir>pos 2dup player entity.x @ player entity.y @ world>tile +pos player.canmove? if 1 player.state MOVING f! dup if swap drop player entity.y ( d v -- ) else drop player entity.x then swap 16 * over @ + 4 r@ entity.x @ r@ entity.y @ r@ entity.dir @ r< entity.anim @ execute draw-sprite ; : full-draw player entity.x @ 152 - player entity.y @ 92 - scroll player draw-entity mouseworldpos 4 draw-sprite draw-screen draw-footer ; MODE-MOVE @ ' tick redefine ' full-draw ' draw redefine s" pete.map" load-map