Rascal

by Tara McGrew

Download Source Text (ZIP)

explore.zil

"Exploration state (map reveal, room discovery)"

<GLOBAL BITMASKS <PTABLE (BYTE) 1 2 4 8 16 32 64 128>>

<CONSTANT REVEAL-BYTES </ <+ ,MAP-SIZE 7> 8>>

<GLOBAL FLOOR-REVEALED <ITABLE <* ,MAX-FLOORS ,REVEAL-BYTES> (BYTE) 0>>

;"Per-floor room discovery tracking.

FLOOR-ROOM-DISCOVERED stores 0/1 flags for each (floor, room id) pair.
FLOOR-ROOM-COUNT stores the total number of rooms on each floor.
DISCOVERED-ROOMS is the cached count for the current floor."

<GLOBAL FLOOR-ROOM-DISCOVERED <ITABLE <* ,MAX-FLOORS ,MAX-ROOMS> (BYTE) 0>>

<GLOBAL FLOOR-ROOM-COUNT <ITABLE ,MAX-FLOORS (BYTE) 0>>

<GLOBAL DISCOVERED-ROOMS 0>

;"Stair traversal helper: stash a tamed monkey object reference while ENTER-FLOOR
  rebuilds CURRENT-FLOOR-OBJ. 0 means none."

<GLOBAL PENDING-STAIR-MONKEY 0>

;"Computes the linear slot offset into the per-floor room discovery table.

Args:
  F: Floor number (1-based).
  RID: Room ID (1..MAX-ROOMS).

Returns:
  Byte offset into FLOOR-ROOM-DISCOVERED."

<ROUTINE ROOMDISC-IDX (F RID)
    <+ <* <- .F 1> ,MAX-ROOMS> <- .RID 1>>>

;"Clears the discovered-room flags for a given floor.

Args:
  F: Floor number (1-based).

Returns:
  T."

<ROUTINE CLEAR-FLOOR-ROOMDISC (F)
    <DO (RID 1 ,MAX-ROOMS)
        <PUTB ,FLOOR-ROOM-DISCOVERED <ROOMDISC-IDX .F .RID> 0>>
    <RTRUE>>

;"Counts discovered rooms for the current floor based on FLOOR-ROOM-DISCOVERED.

Args:
  F: Floor number (1-based).

Returns:
  Count of discovered rooms (0..ROOM-COUNT)."

<ROUTINE COUNT-FLOOR-ROOMDISC (F "AUX" C)
    <SET C 0>
    <DO (RID 1 ,ROOM-COUNT)
        <COND (<G? <GETB ,FLOOR-ROOM-DISCOVERED <ROOMDISC-IDX .F .RID>> 0>
               <SET C <+ .C 1>>)>>
    .C>

;"Clears the reveal bitset for a given floor.

Args:
  F: Floor number (1-based).

Returns:
  T."

<ROUTINE CLEAR-FLOOR-REVEAL (F "AUX" BASE)
    <SET BASE <FLOOR-BASE .F>>
    <DO (I 1 ,REVEAL-BYTES) <PUTB ,FLOOR-REVEALED <+ .BASE <- .I 1>> 0>>>

;"Attempts to descend stairs to the next floor (>) if standing on down stairs.

Args:
  (none)

Returns:
  T if the move happens; FALSE if blocked (wrong tile or at bottom)."

<ROUTINE GO-DOWN ("AUX" MONKEY)
    <COND (<G=? ,CURRENT-FLOOR ,MAX-FLOORS> <RFALSE>)>
    <COND (<N==? <TILE-AT ,PLAYER-X ,PLAYER-Y> ,TILE-STAIR-DOWN> <RFALSE>)>
    <SET MONKEY <ENEMY-OBJ-OF-TYPE ,CURRENT-FLOOR-OBJ ,ETYPE-MONKEY>>
    <COND (<AND <G? .MONKEY 0> <FSET? .MONKEY ,TAMEBIT>>
           <SETG PENDING-STAIR-MONKEY .MONKEY>)
          (ELSE <SETG PENDING-STAIR-MONKEY 0>)>
    <SETG STATS-STAIRS-DOWN <+ ,STATS-STAIRS-DOWN 1>>
    <LOG "You descend to floor " N <+ ,CURRENT-FLOOR 1> "." CR>
    <ENTER-FLOOR <+ ,CURRENT-FLOOR 1> ,PLAYER-X ,PLAYER-Y T>
    <COND (<G? ,PENDING-STAIR-MONKEY 0>
           <MOVE ,PENDING-STAIR-MONKEY ,CURRENT-FLOOR-OBJ>
           <DO (D 1 8)
               <COND (<TRY-ENEMY-MOVE ,PENDING-STAIR-MONKEY
                                      <+ ,PLAYER-X <DIR8-DX .D>>
                                      <+ ,PLAYER-Y <DIR8-DY .D>>>
                      <RETURN>)>>
           <SETG PENDING-STAIR-MONKEY 0>)>
    <RTRUE>>

;"Attempts to ascend stairs to the previous floor (<) if standing on up stairs.

Args:
  (none)

Returns:
  T if the move happens; FALSE if blocked (wrong tile or at top)."

<ROUTINE GO-UP ("AUX" MONKEY)
    <COND (<N==? <TILE-AT ,PLAYER-X ,PLAYER-Y> ,TILE-STAIR-UP> <RFALSE>)>

    <SET MONKEY <ENEMY-OBJ-OF-TYPE ,CURRENT-FLOOR-OBJ ,ETYPE-MONKEY>>
    <COND (<AND <G? .MONKEY 0> <FSET? .MONKEY ,TAMEBIT>>
           <SETG PENDING-STAIR-MONKEY .MONKEY>)
          (ELSE <SETG PENDING-STAIR-MONKEY 0>)>

    ;"With the trophy, ascending from floor 1 is a win condition."
    <COND (<AND <==? ,CURRENT-FLOOR 1> <PLAYER-HAS-TROPHY?>>
           <SETG YOU-WIN? T>
           <SETG GAME-OVER? T>
           <RTRUE>)>

    <COND (<L=? ,CURRENT-FLOOR 1>
           <LOG "An unseen force stops you from ascending." CR>
           <RFALSE>)>
    <SETG STATS-STAIRS-UP <+ ,STATS-STAIRS-UP 1>>
    <LOG "You ascend to floor " N <- ,CURRENT-FLOOR 1> "." CR>

    ;"Carrying the trophy repopulates monsters on each upward move."
    <COND (<PLAYER-HAS-TROPHY?> <CLEAR-FLOOR-ENEMIES <- ,CURRENT-FLOOR 1>>)>
    <ENTER-FLOOR <- ,CURRENT-FLOOR 1> ,PLAYER-X ,PLAYER-Y <>>
    <COND (<G? ,PENDING-STAIR-MONKEY 0>
           <MOVE ,PENDING-STAIR-MONKEY ,CURRENT-FLOOR-OBJ>
           <DO (D 1 8)
               <COND (<TRY-ENEMY-MOVE ,PENDING-STAIR-MONKEY
                                      <+ ,PLAYER-X <DIR8-DX .D>>
                                      <+ ,PLAYER-Y <DIR8-DY .D>>>
                      <RETURN>)>>
           <SETG PENDING-STAIR-MONKEY 0>)>
    <RTRUE>>

;"Computes the byte offset of the reveal bitset for a given floor.

Args:
  F: Floor number (1-based).

Returns:
  Base offset into FLOOR-REVEALED for that floor."

<ROUTINE FLOOR-BASE (F)
    <* <- .F 1> ,REVEAL-BYTES>>

;"Checks whether (X, Y) has been revealed on the current floor.
	Reveal state is stored as a bitset per floor in FLOOR-REVEALED.

Args:
	X, Y: Map coordinates.

Returns:
	T if revealed; FALSE otherwise."

<ROUTINE REVEALED? (X Y "AUX" IDX Q BIT BASE BYTE MASK)
    <SET IDX <MAP-INDEX .X .Y>>
    <SET Q </ .IDX 8>>
    <SET BIT <- .IDX <* .Q 8>>>
    <SET BASE <FLOOR-BASE ,CURRENT-FLOOR>>
    <SET BYTE <GETB ,FLOOR-REVEALED <+ .BASE .Q>>>
    <SET MASK <GETB ,BITMASKS .BIT>>
    <G? <BAND .BYTE .MASK> 0>>

;"Marks a linear map index as revealed for the current floor.

Args:
  IDX: Linear index from MAP-INDEX.

Returns:
  T."

<ROUTINE REVEAL-IDX (IDX "AUX" Q BIT BASE AT MASK OLD)
    <SET Q </ .IDX 8>>
    <SET BIT <- .IDX <* .Q 8>>>
    <SET BASE <FLOOR-BASE ,CURRENT-FLOOR>>
    <SET AT <+ .BASE .Q>>
    <SET MASK <GETB ,BITMASKS .BIT>>
    <SET OLD <GETB ,FLOOR-REVEALED .AT>>
    <PUTB ,FLOOR-REVEALED .AT <BOR .OLD .MASK>>
    <RTRUE>>

;"Reveals a single tile (if in bounds).

Args:
  X, Y: Map coordinates.

Returns:
  T if in bounds; FALSE otherwise."

<ROUTINE REVEAL-TILE (X Y)
    <COND (<NOT <IN-BOUNDS? .X .Y>> <RFALSE>)>
  <COND (<REVEALED? .X .Y> <RTRUE>)>
  <REVEAL-IDX <MAP-INDEX .X .Y>>
  <MARK-DIRTY .X .Y>
    <RTRUE>>

;"Reveals a 3x3 square centered on (X, Y).

Args:
  X, Y: Map coordinates.

Returns:
  (none)"

<ROUTINE REVEAL-AROUND (X Y)
    <DO (DY -1 1) <DO (DX -1 1) <REVEAL-TILE <+ .X .DX> <+ .Y .DY>>>>>

;"Reveals any adjacent stair tiles around (X, Y).

This is used when revealing an entire room: stairs can be forced passable and
have ROOMIDS=0, so they might not be revealed by REVEAL-ROOM alone.

Args:
  X, Y: Map coordinates.

Returns:
  T."

<ROUTINE REVEAL-STAIRS-NEAR (X Y "AUX" NX NY)
    <DO (DY -1 1)
        <DO (DX -1 1)
            <SET NX <+ .X .DX>>
            <SET NY <+ .Y .DY>>
            <COND (<AND <IN-BOUNDS? .NX .NY>
                        <OR <==? <TILE-AT .NX .NY> ,TILE-STAIR-UP>
                            <==? <TILE-AT .NX .NY> ,TILE-STAIR-DOWN>>>
                   <REVEAL-TILE .NX .NY>)>>>
    <RTRUE>>

;"Reveals all tiles belonging to a given room ID.

Args:
  RID: Room ID.

Returns:
  (none)"

<ROUTINE REVEAL-ROOM (RID "AUX" X Y IDX)
    ;"Track discovered rooms per floor so the UI can show discovered/total."
    <COND (<AND <G? .RID 0>
                <L=? .RID ,MAX-ROOMS>
                <L=? ,CURRENT-FLOOR ,MAX-FLOORS>
                <L=? <GETB ,FLOOR-ROOM-DISCOVERED
                           <ROOMDISC-IDX ,CURRENT-FLOOR .RID>>
                     0>>
           <PUTB ,FLOOR-ROOM-DISCOVERED <ROOMDISC-IDX ,CURRENT-FLOOR .RID> 1>
           <SETG DISCOVERED-ROOMS <+ ,DISCOVERED-ROOMS 1>>
           <SETG STATS-ROOMS-DISCOVERED-TOTAL
               <+ ,STATS-ROOMS-DISCOVERED-TOTAL 1>>)>
    <SET Y 1>
    <REPEAT ()
        <COND (<G? .Y ,MAP-H> <RETURN>)>
        <SET X 1>
        <REPEAT ()
            <COND (<G? .X ,MAP-W> <SET Y <+ .Y 1>> <RETURN>)>
            <SET IDX <MAP-INDEX .X .Y>>
                 <COND (<AND <==? <GETB ,ROOMIDS .IDX> .RID>
                   <NOT <REVEALED? .X .Y>>>
                   <REVEAL-IDX .IDX>
                   <MARK-DIRTY .X .Y>
                   ;"If the room contains stairs whose ROOMIDS=0 (e.g. forced
          		         passable), they should still become visible when the room is
          		         revealed. Reveal any adjacent stair tiles."
                   <REVEAL-STAIRS-NEAR .X .Y>)>
            <SET X <+ .X 1>>>>>

"Player movement, stairs, and turn input"

;"Teleports the player to a random safe passable tile on the current floor.

The destination is restricted to ordinary passable tiles (floor/corridor/door)
and avoids stairs, the trader, and enemy-occupied tiles.

Args:
  (none)

Returns:
  T."
<ROUTINE TELEPORT-PLAYER ("AUX" TRIES X Y OLDX OLDY)
    <SET TRIES 0>
    <REPEAT ()
        <SET TRIES <+ .TRIES 1>>
        <COND (<G? .TRIES ,TELEPORT-TRY-LIMIT>
               <LOG "Nothing happens." CR>
               <RETURN T>)>
        <SET X <+ 1 <RNG ,MAP-W>>>
        <SET Y <+ 1 <RNG ,MAP-H>>>
        <COND (<NOT <IN-BOUNDS? .X .Y>> <AGAIN>)>
        <COND (<NOT <OR <==? <TILE-AT .X .Y> ,TILE-FLOOR>
                        <==? <TILE-AT .X .Y> ,TILE-CORRIDOR>
                        <==? <TILE-AT .X .Y> ,TILE-DOOR>>>
               <AGAIN>)>
        <COND (<OR <==? <TILE-AT .X .Y> ,TILE-STAIR-UP>
                   <==? <TILE-AT .X .Y> ,TILE-STAIR-DOWN>>
               <AGAIN>)>
        <COND (<TRADER-AT? .X .Y> <AGAIN>)>
        <COND (<G? <ENEMY-AT .X .Y> 0> <AGAIN>)>
        <SET OLDX ,PLAYER-X>
        <SET OLDY ,PLAYER-Y>
        <SETG PLAYER-X .X>
        <SETG PLAYER-Y .Y>
        <MARK-PLAYER-MOVE .OLDX .OLDY ,PLAYER-X ,PLAYER-Y>
        <AFTER-PLAYER-RELOCATE>
        <RETURN T>>>

<IF-DEBUG
    ;"Debug helper: teleport the player to the up/down stairs on the current floor.

    Args:
    DOWN?: True to target down stairs (>); false to target up stairs (<).

    Returns:
    T."
    <ROUTINE STAIR-FINDER-TELEPORT (DOWN? "AUX" X Y TILE RID OLDX OLDY)
        <SETG DEBUG-USED? T>
        <COND (.DOWN?
        <SET X <FLOOR-DOWN-X ,CURRENT-FLOOR>>
        <SET Y <FLOOR-DOWN-Y ,CURRENT-FLOOR>>
            <SET TILE ,TILE-STAIR-DOWN>)
            (ELSE
        <SET X <FLOOR-UP-X ,CURRENT-FLOOR>>
        <SET Y <FLOOR-UP-Y ,CURRENT-FLOOR>>
            <SET TILE ,TILE-STAIR-UP>)>
        <COND (<OR <==? .X 0> <==? .Y 0>>
            <LOG "Stair finder: stairs not recorded for this floor." CR>
            <RTRUE>)>
        <COND (<NOT <IN-BOUNDS? .X .Y>>
            <LOG "Stair finder: stair coords invalid: (" N .X "," N .Y ")." CR>
            <RTRUE>)>
        <LOG "Stair finder: target (" N .X "," N .Y "), tile=" N <TILE-AT .X .Y>>
        <SET RID <ROOMID-AT .X .Y>>
        <LOG ", roomid=" N .RID ", ROOM-COUNT=" N ,ROOM-COUNT CR>
        <COND (<AND <G? .RID 0> <L=? .RID ,ROOM-COUNT>>
            <LOG "  Room " N .RID " bounds: L=" N <ROOM-GET .RID ,ROOM-L>
                    " T=" N <ROOM-GET .RID ,ROOM-T>
                    " R=" N <ROOM-GET .RID ,ROOM-R>
                    " B=" N <ROOM-GET .RID ,ROOM-B> CR>)>
        <LOG "  Neighbors: N=" N <TILE-AT .X <- .Y 1>>
            " S=" N <TILE-AT .X <+ .Y 1>>
            " W=" N <TILE-AT <- .X 1> .Y>
            " E=" N <TILE-AT <+ .X 1> .Y> CR>
        <COND (<N==? <TILE-AT .X .Y> .TILE>
            <LOG "Stair finder: WARNING: expected stair tile at (" N .X "," N .Y ") but map has tile " N <TILE-AT .X .Y> "." CR>)>
        <SET OLDX ,PLAYER-X>
        <SET OLDY ,PLAYER-Y>
        <SETG PLAYER-X .X>
        <SETG PLAYER-Y .Y>
        <MARK-PLAYER-MOVE .OLDX .OLDY ,PLAYER-X ,PLAYER-Y>
        <AFTER-PLAYER-RELOCATE>
        <LOG "Stair finder: teleported to (" N .X "," N .Y ")." CR>
        <RTRUE>>>

;"Common post-move side effects after PLAYER-X/PLAYER-Y changes.

Reveals, picks up any items on the tile, and updates room discovery."

<ROUTINE AFTER-PLAYER-RELOCATE ("AUX" ID)
    <SET ID <POTION-OBJ-AT ,PLAYER-X ,PLAYER-Y>>
    <COND (<AND <G? .ID 0> <NOT <FSET? .ID ,SEENBIT>>>
           <FSET .ID ,SEENBIT>
           <SETG STATS-POTIONS-FOUND <+ ,STATS-POTIONS-FOUND 1>>)> 
    <REVEAL-AROUND ,PLAYER-X ,PLAYER-Y>
    <TRY-PICKUP-KEY>
    <TRY-PICKUP-POTION>
    <TRY-PICKUP-WEAPON>
    <TRY-PICKUP-TREASURE>
    <TRY-PICKUP-FOOD>
    <TRY-PICKUP-GOLD>
    <TRY-ACTIVATE-SHRINE>
    <COND (<AND <G? <ROOMID-AT ,PLAYER-X ,PLAYER-Y> 0>
                <N==? <ROOMID-AT ,PLAYER-X ,PLAYER-Y> ,CURRENT-ROOM>>
           <SETG CURRENT-ROOM <ROOMID-AT ,PLAYER-X ,PLAYER-Y>>
           <COND (<L=? ,PLAYER-SHADOW-TURNS 0> <REVEAL-ROOM ,CURRENT-ROOM>)>)>
    <SETG INTERIOR-LAUNCHED? <>>
    <COND (<==? <TILE-AT ,PLAYER-X ,PLAYER-Y> ,TILE-INTERIOR>
           <SET ID <INTERIOR-ID-AT ,CURRENT-FLOOR ,PLAYER-X ,PLAYER-Y>>
           <COND (<G? .ID 0>
                  <SETG INTERIOR-LAUNCHED? <LAUNCH-INTERIOR-ID .ID>>)>)>
    <RTRUE>>