about summary refs log tree commit diff stats
path: root/retro/dungeon.retro
blob: 603241487029c72749bfa0e29d937417a42129bc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# Game! Dungeon crawling?

## Utilities 

By using the inbuilt word `n:random` we are able to write a word to get a random number from within a range. 

~~~
:n:ranged-random (lower,upper-random) over - n:inc n:random swap mod + ;
~~~

## Encounter

A word to determine what sort of encounter a player will have at a given location. Currently each type of encounter is evenly weighted, so, just as likely as any other -- in the future I'd like to weight more heavily to some than others. A naive approach to do this would be to duplicate certain kinds of encounter in the look up table. Encounters are generated on a per-turn basis and aren't currently linked to player position in anyway -- in the future I'd like to have certain types of encounter linked to certain positions within the play space.

See http://retroforth.org/examples/magic-8th-ball.retro.html

~~~
:null-encounter 'null_encounter s:put nl ; 
:romantic-encounter 'romantic_encounter s:put nl ; 
:vendor-encounter 'vendor_encounter s:put nl ; 
:aggressive-encounter 'aggressive_encounter s:put nl ; 
:wildlife-encounter 'wildlife_encounter s:put nl ;
:environmental-encounter 'environmental_encounter s:put nl ;

{ [ ] (empty,_fill_offset_zero)
  &null-encounter
  &romantic-encounter
  &vendor-encounter
  &aggressive-encounter
  &wildlife-encounter
  &environmental-encounter } 'encounters const

:build-encounter
    &encounters #1 #6 n:ranged-random a:fetch call ;
~~~


## Global state

Bootstrapping some state before taking in any player input.

~~~
{ 'hp 'x 'y } [ 'Player.%s s:format var ] a:for-each
{ 'turn } [ 'Global.%s s:format var ] a:for-each

:state-init
    #0 !Global.turn
    #50 !Player.x
    #50 !Player.y
    #50 #100 n:ranged-random !Player.hp ;

:state-display
    @Global.turn
    @Player.x
    @Player.y
    @Player.hp
    'POWER:_%n\nPOS_Y:_%n\nPOS_X:_%n\n============\nTurn_Count:_%n\n s:format s:put ;
~~~

## Process player input

Here I define a very basic word to process player input and to display game ui as well as some words that can be triggered by player input. The last word is the core ui, it displays some hint text about the game's controls.

~~~
:go-north
    &Player.y v:inc build-encounter ;

:go-south
    &Player.y v:dec build-encounter ;

:go-east
    &Player.x v:inc build-encounter ;

:go-west
    &Player.x v:dec build-encounter ;

:go-center
    #50 !Player.x
    #50 !Player.y
    build-encounter ;

:unknown-input
    nl '_is_not_a_known_input s:append s:put nl ;

:process-input
    'w [ go-north ] s:case
    's [ go-south ] s:case
    'a [ go-west ] s:case
    'd [ go-east ] s:case
    'c [ go-center ] s:case
    'q [ bye ] s:case
    unknown-input ;

:hint-display
    nl state-display nl '(w)_-_north\n(s)_-_south\n(a)_-_west\n(d)_-_east\n============\n(q)_-_quit s:format s:put nl nl ;
~~~

## Game loop

The core game loop, like most game loops, is a while loop that'll run forever as long as some value is TRUE. I never actually bother to set that value to FALSE. To quit, the player inputs "q" which triggers the word `bye` to exit the loop and halt the process, returning to the host system.

Of note here is the word `turn` -- this is triggered at the start of each loop after collecting the player's input. Turn isn't much more than a wrapper around `process-input`, the trick it pulls is to lead with an `s:get` (akin to Lua's io.read) to read in whatever is input to stdin by the player. Once input is read in a new "turn" starts (increasing the turn count in Global.turn) and then clearing the display. Clearing the display ensures that the display is only ever showing state for the currently active turn.

`pos-check` limits the size of the game surface to 100 x by 100 y. 

~~~
:pos-check
    @Player.x #100 gt? [ #0 !Player.x ] if
    @Player.x #0 lt? [ #100 !Player.x ] if 
    @Player.y #100 gt? [ #0 !Player.y ] if 
    @Player.y #0 lt? [ #100 !Player.y ] if ;

:turn
    &Global.turn v:inc clear process-input pos-check hint-display ;

:welcome-message
    nl 'Welcome!_╰(˙ᗜ˙)੭━☆゚.*・。゚ s:put nl ;

state-init welcome-message hint-display
[ s:get turn TRUE ] while
~~~