This repository has been archived on 2021-04-27. You can view files and clone it, but cannot push or open issues or pull requests.
javapool/main.fs

252 lines
9.9 KiB
Forth
Raw Normal View History

2021-03-17 17:58:01 +00:00
\ JAVAPOOL - THE GAME (an incremental game based on the lore of #javapool on tilde.town)
2021-03-17 11:56:45 +00:00
\ this code likely sucks, because I'm bad at forth and figuring stuff out as I go.
2021-03-19 16:10:24 +00:00
\ written for 64-bit gforth, might be portable to other forths with some tweaking, idk.
include debug.fs
2021-03-17 11:56:45 +00:00
2021-03-17 17:58:01 +00:00
\ ANSI escape sequences. gforth already provides at-xy and page, but I also need a word for clearing the current line and displaying the cursor.
: hide-cursor esc[ ." ?25l" ;
: show-cursor esc[ ." ?25h" ;
: clear-line esc[ ." K" ;
2021-03-18 10:19:51 +00:00
: move-right ( x -- ) esc[ 0 .R ." C" ; \ 0 .R : print a number without trailing space
2021-03-17 17:58:01 +00:00
\ some of these values (member cost, device count) have two cells.
2021-03-18 20:00:59 +00:00
\ the second cell is used to store if the value has changed or not.
2021-03-18 20:07:03 +00:00
\ for flexibility, these all take an offset of the cell the flag is stored in, so multiple values can be before the flag.
2021-03-18 10:19:51 +00:00
\ this is useful as it means we only redraw the display if a relevant value has changed.
\ the word "changed" sets one of these values as being changed.
2021-03-18 20:00:59 +00:00
: changed ( addr offset -- ) cells + -1 swap ! ;
\ the word "cleared" sets one of these values as having been used, clearing the change flag.
2021-03-18 20:00:59 +00:00
: cleared ( addr offset -- ) cells + 0 swap ! ;
\ the word changed? leaves a -1 if the value has changed since last clear, 0 otherwise.
2021-03-18 20:00:59 +00:00
: changed? ( addr offset -- flag ) cells + @ ;
\ the variable "devices" holds the amount of devices currently in the pool. It has two values a change flag.
\ The first value contains the current number of devices, the second contains the number of devices in the previous tick.
\ (this is used to determine if the change flag needs to be set)
\ It begins with the change flag set, to draw the screen the first time.
2021-03-19 16:10:24 +00:00
\ this variable can contain decimal-point amounts of devices. Things that need a non-decimal amount can round it with ftrunc.
create devices 0e f, -1 ,
\ old devices is the old device count. For convenience.
: old-devices devices float + ;
2021-03-20 14:33:46 +00:00
\ d/s converts a devices per second value into devices per tick.
: d/s 1000e f/ ;
2021-03-19 16:10:24 +00:00
\ the variable "base-devices/tick" is the base amount of devices added per tick.
variable base-devices/tick
2021-03-20 14:33:46 +00:00
0.5e d/s base-devices/tick f!
2021-03-19 16:10:24 +00:00
\ the variable "membercost" is the cost of hiring a new chat member. If it is zero, hiring chat members hasn't been unlocked yet. It has a change flag and is fixed-point.
create membercost 0 , 0 ,
2021-03-17 17:58:01 +00:00
\ the variable "membercount" is how many chat members you've hired. For each chat member, device gain delay decreases.
variable membercount
2021-03-20 14:33:46 +00:00
\ the variable "constructcost" is how expensive a construct is. Follows the same rules as membercost.
create constructcost 0 , 0
\ the variable "constructcount" follows the same rules as membercount, but for constructs.
variable constructcount
2021-03-18 11:45:59 +00:00
\ count-messages-shown contains the amount of device count milestone messages that have been shown.
variable count-messages-shown
2021-03-18 10:54:27 +00:00
\ message contains a string message which is printed on the screen.
\ because forth is weird, strings can be stored in variables but it's easier to just store them someplace else and then store that address and the string's length in the variable (this is what S" " does and what "type" expects)
2021-03-18 10:54:27 +00:00
create message 0 , 0 , 0 ,
\ set-message sets the message
2021-03-18 20:00:59 +00:00
: set-message ( addr len -- ) message ! message 1 cells + ! message 2 changed ;
2021-03-18 10:54:27 +00:00
\ get-message gets the message
2021-03-18 20:00:59 +00:00
: get-message ( -- addr len ) message @ message 1 cells + @ swap ;
\ set an initial message
S" you feel like throwing some stuff into the pool." set-message
2021-03-19 16:10:24 +00:00
\ f+! is +! for floats
: f+! ( addr -- ) ( F: r -- ) dup f@ f+ f! ;
\ any-change? leaves a -1 on the stack if any value has changed, 0 otherwise
: any-change? ( -- flag ) membercost 1 changed? devices 2 changed? message 2 changed? or or ;
\ clear-all clears the change flag on all values.
: clear-all membercost 1 cleared devices 2 cleared message 2 cleared ;
2021-03-17 17:58:01 +00:00
\ the word !0= tests if something is non-zero
2021-03-19 16:10:24 +00:00
: !0= ( n -- flag ) 0= 0= ;
\ the word f!0= tests if a floating thing is non-zero
: f!0= ( r -- flag ) f0= 0= ;
2021-03-17 11:56:45 +00:00
2021-03-17 17:58:01 +00:00
\ the world unlock-members tests to see if hiring chat members can be unlocked. if so, they are unlocked.
: unlock-members
2021-03-19 16:10:24 +00:00
devices f@ ftrunc 5e f= membercost @ 0= and if
2021-03-18 10:54:27 +00:00
10 membercost !
S" A friendly townie wants to help out." set-message then ;
2021-03-17 17:58:01 +00:00
2021-03-20 14:33:46 +00:00
: unlock-constructs
devices f@ ftrunc 30e f= constructcost @ 0= and if
40 constructcost !
S" You figure out that you can probably hack these devices." set-message then ;
2021-03-17 17:58:01 +00:00
\ the word unlock tests to see if any unlock conditions are matched, and unlocks them if they are.
2021-03-20 14:33:46 +00:00
: unlock unlock-members unlock-constructs ;
2021-03-17 17:58:01 +00:00
\ the word wait-tick waits a tick. Ticks are 1ms currently.
: wait-tick 1 ms ;
2021-03-17 11:56:45 +00:00
2021-03-19 16:10:24 +00:00
\ the word debug-console is used for entering debug mode.
: debug-console
page
." Entering debug/cheat shell. use `restart` to restart the game." cr
.s cr f.s
quit ;
2021-03-17 11:56:45 +00:00
2021-03-19 16:10:24 +00:00
\ devices/tick calculates the amount of devices that are to be added per tick.
: devices/tick ( f: -- r )
base-devices/tick f@
2021-03-20 14:33:46 +00:00
membercount @ s>f 0.5e d/s f* \ each member adds 0.5 d/s
constructcount @ s>f 2e d/s f* \ each construct adds 2 d/s
f+ f+ ;
2021-03-18 11:45:59 +00:00
\ set-count-message sets the message to certain things based on the amount of devices in the pool. The count-messages-shown variable is used to track how many of these have already been shown.
: set-count-message
2021-03-19 16:10:24 +00:00
devices f@ ftrunc 50e f> count-messages-shown @ 1 < and if
2021-03-18 11:45:59 +00:00
S" The pool is starting to look a little cluttered. " set-message
1 count-messages-shown +!
then ;
\ update-devices updates the device count, setting the change flag as needed
: update-devices
devices f@ old-devices f! \ store old value of devices
devices/tick devices f+!
devices f@ ftrunc old-devices f@ ftrunc f= 0= if \ if devices has changed by an integer amount
devices 2 changed
then ;
2021-03-17 17:58:01 +00:00
\ the word game-tick runs a single tick of the game
2021-03-19 16:10:24 +00:00
: game-tick unlock
update-devices
2021-03-19 20:27:08 +00:00
set-count-message ;
2021-03-18 11:45:59 +00:00
2021-03-17 17:58:01 +00:00
\ TODO saving
\ the word exit-game exits the game.
: exit-game page show-cursor bye ;
2021-03-17 11:56:45 +00:00
2021-03-20 14:33:46 +00:00
\ the word can-afford? tests if you could afford n devices.
: can-afford? ( n -- flag ) s>f devices f@ f<= ;
\ the word pay pays an amount of devices
: pay ( n -- ) s>f devices f@ fswap f- devices f! ;
2021-03-17 17:58:01 +00:00
\ the word hire-chat-member hires a chat member.
: hire-chat-member
2021-03-17 18:04:15 +00:00
membercost @ dup !0= swap \ checks if member cost is nonzero, leaving member cost on the stack
2021-03-20 14:33:46 +00:00
can-afford? \ tests if the member cost (converted to a float) is less than the amount of devices we have
2021-03-17 17:58:01 +00:00
and if
2021-03-20 14:33:46 +00:00
membercost @ pay
2021-03-17 18:04:15 +00:00
1 membercount +! \ add a member to the count
membercount @ 2 * 5 + membercost +! \ increase the cost of buying a new member
2021-03-18 20:00:59 +00:00
membercost 1 changed
2021-03-18 10:54:27 +00:00
S" " set-message \ blank the message, because we did a thing.
2021-03-17 17:58:01 +00:00
then ;
2021-03-20 14:33:46 +00:00
\ the word build-construct builds a construct.
: build-construct
constructcost @ dup !0= swap \ checks if member cost is nonzero, leaving member cost on the stack
can-afford? \ tests if the member cost (converted to a float) is less than the amount of devices we have
and if
constructcost @ pay \ remove devices that pay for the construct
1 constructcount +! \ add a construct to the count
constructcount @ 4 * 10 + constructcost +! \ increase the cost of buying a new construct
constructcount 1 changed
S" " set-message \ blank the message, because we did a thing.
then ;
2021-03-17 17:58:01 +00:00
\ the word handle-input handles input every time around the game loop.
2021-03-17 11:56:45 +00:00
: handle-input
key? if key case
[char] q of exit-game endof
2021-03-17 17:58:01 +00:00
[char] h of hire-chat-member endof
2021-03-20 14:33:46 +00:00
[char] b of build-construct endof
2021-03-18 15:19:19 +00:00
[char] D of debug-console endof
2021-03-17 11:56:45 +00:00
endcase then ;
\ inc-y increments the y coordinate being drawn at.
: inc-y 1 + ;
2021-03-17 11:56:45 +00:00
2021-03-17 17:58:01 +00:00
\ print-rate prints the current device output rate on the screen.
: print-rate
2 set-precision
2021-03-17 18:04:15 +00:00
\ first convert to floating point, divide by 1000 to get seconds from milliseconds, then do 1/n to get the devices/second from seconds/device
2021-03-19 16:10:24 +00:00
." (" devices/tick 1000e f* f. ." devices/second)" ;
2021-03-17 17:58:01 +00:00
2021-03-18 10:54:27 +00:00
\ draw-message draws the stored message on the screen.
: draw-message ( x y -- x y )
2dup at-xy
clear-line get-message type
inc-y ;
\ draw-blank draws a blank line.
: draw-blank ( x y -- x y )
2dup at-xy
clear-line
inc-y ;
2021-03-18 10:54:27 +00:00
2021-03-17 17:58:01 +00:00
\ draw-devices draws the number of devices on the screen.
: draw-devices ( x y -- x y )
2dup at-xy
2021-03-19 16:10:24 +00:00
devices f@ f>s dup
2021-03-17 17:58:01 +00:00
clear-line
1 = if ." There is 1 device in the javapool." drop else
." There are " . ." devices in the javapool." then
space print-rate
\ increment y position
inc-y ;
2021-03-17 17:58:01 +00:00
2021-03-20 14:33:46 +00:00
\ draw-constructs draws the option for building a construct
: draw-constructs ( x y -- x y )
2dup at-xy
2021-03-20 14:33:46 +00:00
clear-line
constructcost @ dup !0= if
." (b)uild a device-throwing construct [" . ." Devices]"
space ." (" constructcount @ 0 .R ." )"
else drop then ;
\ draw-members draws the option for hiring a chat member
: draw-members ( x y -- x y )
2dup at-xy
clear-line
membercost @ dup !0= if \ if membercost is non-zero (members have been unlocked)
." (h)ire a chat member [" . ." Devices]"
2021-03-18 15:19:19 +00:00
space ." (" membercount @ 0 .R ." )"
else drop then
inc-y ;
2021-03-20 14:33:46 +00:00
2021-03-17 17:58:01 +00:00
\ draw-actions draws a list of the actions you've unlocked.
: draw-actions
2dup at-xy
draw-members cr 1 move-right
2021-03-20 14:33:46 +00:00
draw-constructs cr 1 move-right
." (q)uit (without saving)"
\ add 1 to y
1 + ;
\ clear-xy clears the x and y positions from the stack
: clear-xy ( x y -- ) drop drop ;
\ draw-screen redraws the screen, but only if there's been a change.
\ 1 1 is the starting position to draw from.
: draw-screen any-change? if 1 1 draw-message draw-devices draw-blank draw-actions clear-xy clear-all then ;
2021-03-19 16:10:24 +00:00
\ : draw-screen clear-all ;
2021-03-17 11:56:45 +00:00
\ main game loop
2021-03-19 16:10:24 +00:00
: game-loop begin wait-tick game-tick handle-input draw-screen 0 until ;
2021-03-17 11:56:45 +00:00
\ the seconds, minutes and hours words are used with skip-time in debugging.
: seconds 1000 * ;
: minutes seconds 60 * ;
: hours minutes 60 * ;
\ skip-time runs x game ticks without waiting. Used for debugging.
: skip-time ( t -- ) 0 do game-tick loop ;
2021-03-19 08:45:42 +00:00
\ for use in the debugging shell
: restart page game-loop ;
\ clear the screen and start the game
2021-03-19 16:10:24 +00:00
page hide-cursor game-loop