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

172 lines
7.2 KiB
Forth

\ JAVAPOOL - THE GAME (an incremental game based on the lore of #javapool on tilde.town)
\ this code likely sucks, because I'm bad at forth and figuring stuff out as I go.
\ 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" ;
: move-right ( x -- ) esc[ 0 .R ." C" ; \ 0 .R : print a number without trailing space
\ some of these values (member cost, device count) have two cells.
\ the first cell stores the value, the second cell stores if the value has changed.
\ 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.
: changed ( addr -- ) 1 cells + -1 swap ! ;
\ the word "cleared" sets one of these values as having been used, clearing the change flag.
: cleared ( addr -- ) 1 cells + 0 swap ! ;
\ the word changed? leaves a -1 if the value has changed since last clear, 0 otherwise.
: changed? ( addr -- flag ) 1 cells + @ ;
\ initialise some variables that encode game state.
\ the variable "counter" holds the count of the current tick. It always starts at 0.
variable counter
\ the variable "devices" holds the amount of devices currently in the pool. It has a change flag. It begins with the change flag set, to draw the screen the first time.
create devices 0 , -1 ,
\ the variable "devicerate" is the amount of ticks between devices getting added.
variable devicerate
2000 devicerate !
\ 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.
create membercost 0 , 0 ,
\ the variable "membercount" is how many chat members you've hired. For each chat member, device gain delay decreases.
variable membercount
\ count-messages-shown contains the amount of device count milestone messages that have been shown.
variable count-messages-shown
\ 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)
\ as I'd like changed? etc to work on this, it needs to have three cells, arranged like this:
\ [length change-flag address]
\ TODO this is a terrible idea. Potential better ideas:
\ * have the change flag be the first value, so then the subsequent values can be any length
\ * learn how strings work in this language, store the message inside message
create message 0 , 0 , 0 ,
\ set-message sets the message
: set-message ( addr len -- ) message ! message 2 cells + ! message changed ;
\ get-message gets the message
: get-message ( -- addr len ) message @ message 2 cells + @ swap ;
\ set an initial message
S" you feel like throwing some stuff into the pool." set-message
\ FOR DEBUGGING PURPOSES - some sets that skip into the future
\ 40 devices !
\ 10 membercost !
\ any-change? leaves a -1 on the stack if any value has changed, 0 otherwise
: any-change? ( -- flag ) membercost changed? devices changed? message changed? or or ;
\ clear-all clears the change flag on all values.
: clear-all membercost cleared devices cleared message cleared ;
\ the word !0= tests if something is non-zero
: !0= 0= 0= ;
\ the world unlock-members tests to see if hiring chat members can be unlocked. if so, they are unlocked.
: unlock-members
devices @ 5 = membercost @ 0= and if
10 membercost !
S" A friendly townie wants to help out." set-message then ;
\ the word unlock tests to see if any unlock conditions are matched, and unlocks them if they are.
: unlock unlock-members ;
\ the word tick waits a tick. Ticks are 1ms currently.
: tick 1 ms ;
\ the word tick-up ticks the counter up
: tick-up tick 1 counter +! ;
\ 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
devices @ 50 > count-messages-shown @ 1 < and if
S" The pool is starting to look a little cluttered. " set-message
1 count-messages-shown +!
then ;
\ the word game-tick runs a single tick of the game
: game-tick tick-up unlock
counter @ devicerate @ mod 0= if
1 devices +!
set-count-message
devices changed then ;
\ TODO saving
\ the word exit-game exits the game.
: exit-game page show-cursor bye ;
\ the word hire-chat-member hires a chat member.
\ TODO something funky is going on with the rate past a certain point. find out where and why.
\ goes negative at 12 members, seems to be doing weird stuff before that with speeds.
: hire-chat-member
membercost @ dup !0= swap \ checks if member cost is nonzero, leaving member cost on the stack
devices @ <= \ tests if the member cost is less than the amount of devices we have
and if
devices @ membercost @ - devices ! \ remove devices that "pay" for the member
membercount @ dup !0= if \ membercount is nonzero
devicerate @ swap 1 + 500 swap / - devicerate !
else
devicerate @ 500 - devicerate ! drop
then
1 membercount +! \ add a member to the count
membercount @ 2 * 5 + membercost +! \ increase the cost of buying a new member
membercost changed
S" " set-message \ blank the message, because we did a thing.
then ;
\ the word debug-console is used for entering debug mode.
\ TODO write debug mode. Ideally this could just be a forth console.
: debug-console
page
." Entering debug/cheat mode!"
page ;
\ the word handle-input handles input every time around the game loop.
: handle-input
key? if key case
[char] q of exit-game endof
[char] h of hire-chat-member endof
[char] D of debug-console endof
endcase then ;
\ print-rate prints the current device output rate on the screen.
: print-rate
2 set-precision
\ 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
." (" devicerate @ s>f 1000e f/ 1e fswap f/ f. ." devices/second)";
\ draw-message draws the stored message on the screen.
: draw-message
1 2 at-xy
clear-line get-message type ;
\ draw-devices draws the number of devices on the screen.
: draw-devices
devices @ dup
1 1 at-xy
clear-line
1 = if ." There is 1 device in the javapool." drop else
." There are " . ." devices in the javapool." then
space print-rate ;
\ draw-members draws the option for hiring a chat member
: draw-members
clear-line
membercost @ dup !0= if \ if membercost is non-zero (members have been unlocked)
." (h)ire a chat member [" . ." Devices]"
space ." (" membercount @ 0 .R ." )"
else drop then ;
\ draw-actions draws a list of the actions you've unlocked.
: draw-actions
1 4 at-xy
draw-members cr 1 move-right
." (q)uit (without saving)" ;
\ draw-screen redraws the screen, but only if there's been a change.
: draw-screen any-change? if draw-devices draw-actions draw-message clear-all then ;
\ main game loop
: game-loop begin game-tick handle-input draw-screen 0 until ;
\ clear the screen and start the game
page hide-cursor game-loop