2023-06-09 07:07:28 -05:00
;;;; Copyright © 2023, Jaidyn Ann <jadedctrl@posteo.at>
;;;;
;;;; This program is free software: you can redistribute it and/or
;;;; modify it under the terms of the GNU General Public License as
;;;; published by the Free Software Foundation, either version 3 of
;;;; the License, or (at your option) any later version.
;;;;
;;;; This program is distributed in the hope that it will be useful,
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;;; GNU General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License
;;;; along with this program. If not, see <https://www.gnu.org/licenses/>.
2023-07-14 08:50:02 -05:00
;;;; FLORA-SEARCH-AURORA.OVERWORLD 🌍
2023-06-09 07:07:28 -05:00
;;;; All game-functions and data relating to the “overworld” (that is,
;;;; the primary gameplay, the RPG-ish-ish bits).
( in-package :flora-search-aurora.overworld )
2023-06-24 22:17:42 -05:00
;;; ———————————————————————————————————
;;; Misc. Utils
;;; ———————————————————————————————————
( defun within-rectangle ( point top-left-corner bottom-right-corner )
" With three coordinate plists, determine whether or not POINT resides within a
rectangle as defined by its TOP-LEFT-CORNER & BOTTOM-RIGHT-CORNER. "
( and ( <= ( getf point :x ) ( getf bottom-right-corner :x ) )
( >= ( getf point :x ) ( getf top-left-corner :x ) )
( <= ( getf point :y ) ( getf bottom-right-corner :y ) )
( >= ( getf point :y ) ( getf top-left-corner :y ) ) ) )
2023-06-16 20:09:35 -05:00
;;; ———————————————————————————————————
;;; Accessors
;;; ———————————————————————————————————
( defmacro getf-entity ( map entity-id )
"Get an entity from the map-data, using its ID."
2023-06-21 23:47:54 -05:00
` ( mapcan ( lambda ( chunk ) ( assoc , entity-id ( cdr chunk ) ) )
( gethash :entities , map ) ) )
2023-06-16 20:09:35 -05:00
( defmacro getf-entity-data ( map entity-id key )
"Get a specific piece of data from the given entity's property-list."
2023-06-21 23:47:54 -05:00
` ( getf ( cdr ( mapcan ( lambda ( chunk ) ( assoc , entity-id ( cdr chunk ) ) )
( gethash :entities , map ) ) )
, key ) )
2023-06-16 20:09:35 -05:00
2023-06-26 11:12:06 -05:00
( defun removef-entity ( map entity-id )
" Remove an entity of the given ID from the map entirely. Nuke ‘ em!
Literally kill them, show no mercy, dig your sharp nails into their fleshy
stomache and PULL HARD, show NO REMORSE. RAAAAAA 🗡 🩸 "
( mapcar ( lambda ( chunk-alist )
2023-07-15 05:04:17 -05:00
( … :remove-from-alistf entity-id ( cdr chunk-alist ) ) )
2023-06-26 11:12:06 -05:00
( gethash :entities map ) ) )
2023-06-26 15:43:01 -05:00
( defmacro aget-item ( map item )
" Get an item from the MAP ’ s :ITEMS alist. That is, an item in user ’ s inventory.
Members of :ITEMS will not be persistent beween play-throughs ; the user has to
get everything again. "
` ( assoc-utils:aget ( gethash :items , map ) , item ) )
( defmacro getf-act ( map act )
" Get an ACT from the MAP ’ s :ACTS plist. That is, some marker indicating that
the user has done something. Just like :ITEMS, these are not persistent through
replays of the game. "
` ( getf ( gethash :acts , map ) , act ) )
( defmacro getf-know ( map idea )
" Get an item from the MAP ’ s :KNOWS plist. That is, some marker indicating that
the user knows something. Unlike :ITEMS and :ACTS, these _are_ persistent through
replays of the game. "
` ( getf ( gethash :knows , map ) , idea ) )
;;; ———————————————————————————————————
;;; Item searching/testing
;;; ———————————————————————————————————
2023-06-16 20:09:35 -05:00
( defun entities-near-coords ( coords radius entities &key ( x-radius radius ) ( y-radius radius ) )
"Return a list of entity-plists that are near the given coordinates within the given RADIUS."
( remove-if-not
( lambda ( test-entity )
( let ( ( test-coords ( getf ( cdr test-entity ) :coords ) ) )
( and ( < ( abs ( - ( getf coords :x )
( getf test-coords :x ) ) )
x-radius )
( < ( abs ( - ( getf coords :y )
( getf test-coords :y ) ) )
y-radius ) ) ) )
2023-06-21 23:47:54 -05:00
( cdr ( assoc ( world-coords-chunk coords ) entities ) ) ) )
2023-06-16 20:09:35 -05:00
( defun entities-near-entity ( entity entities )
2023-06-26 15:43:01 -05:00
"Return a new list of entities near the given ENTITY — that is, within touching-distance."
2023-06-16 20:09:35 -05:00
( remove-if
( lambda ( test-entity )
2023-06-22 19:08:14 -05:00
( … :plist= ( cdr entity )
2023-06-16 20:09:35 -05:00
( cdr test-entity ) ) )
( entities-near-coords ( getf ( cdr entity ) :coords )
2023-06-24 16:12:16 -05:00
( + ( length ( getf ( cdr entity ) :face ) ) 6 )
2023-06-16 20:09:35 -05:00
entities
2023-06-24 20:04:52 -05:00
:y-radius 4 ) ) )
2023-06-16 20:09:35 -05:00
2023-06-24 22:17:42 -05:00
2023-06-16 20:09:35 -05:00
( defun cell-at-world-coords-p ( map-chunks coords )
2023-06-26 15:43:01 -05:00
"Return whether or not there is a cell at the given COORDS."
2023-06-16 20:09:35 -05:00
( let ( ( chunk ( world-coords-chunk coords ) ) )
( member 't ( cdr ( assoc chunk map-chunks ) )
:test ( lambda ( ignored cell )
2023-07-07 10:33:32 -05:00
( … :plist= ( list :x ( getf cell :x ) :y ( getf cell :y ) )
coords ) ) ) ) )
2023-06-16 20:09:35 -05:00
( defun walkable-tile-p ( map x y )
2023-06-26 15:43:01 -05:00
"Return whether or not the given coordinates on the MAP are traversable for an entity."
2023-06-16 20:09:35 -05:00
( not ( cell-at-world-coords-p ( gethash :bump-map map )
( list :x x :y y ) ) ) )
2023-06-25 14:40:12 -05:00
( defun trigger-at-coords ( map world-coords )
2023-06-26 15:43:01 -05:00
"Return a “Trigger”-rectangle from MAP that’ d be triggered at the given coords."
2023-06-25 14:40:12 -05:00
( let ( ( chunk ( world-coords-chunk world-coords ) ) )
( loop for trigger in ( cdr ( assoc chunk ( gethash :triggers map ) ) )
2023-06-26 15:43:01 -05:00
do ( when ( within-rectangle world-coords
( getf trigger :coords ) ( getf trigger :bottom-coords ) )
( return trigger ) ) ) ) )
2023-06-25 14:40:12 -05:00
2023-06-26 20:25:02 -05:00
;;; ———————————————————————————————————
;;; Map conversions & manipulations
;;; ———————————————————————————————————
( defun merge-maps ( map-a map-b )
" Copy data that should be persistent between maps from map-a to map-b.
Used primarily in moving between different maps in an overworld state. "
;; Copy over important game-data from map-a.
( mapcar
( lambda ( map-key )
( setf ( gethash map-key map-b ) ( gethash map-key map-a ) ) )
' ( :acts :knows :items :seconds :day ) )
;; Copy specific bits of player data from map-a’ s :ENTITIES.
( mapcar
( lambda ( player-key )
2023-06-28 09:56:25 -05:00
( setf ( getf-entity-data map-b ' ✿ :player player-key )
( getf-entity-data map-a ' ✿ :player player-key ) ) )
2023-06-26 20:25:02 -05:00
' ( :face :normal-face :talking-face ) )
map-b )
2023-06-09 07:07:28 -05:00
;;; ———————————————————————————————————
2023-06-16 20:11:38 -05:00
;;; Overworld logic
2023-06-09 07:07:28 -05:00
;;; ———————————————————————————————————
2023-06-26 15:43:01 -05:00
( defun overworld-state-update ( map Δt )
2023-06-11 17:12:52 -05:00
" Do nothing, lol. Core part of OVERWORLD-STATE.
Returns parameters to be used in the next invocation of OVERWORLD-STATE. "
2023-07-15 05:04:17 -05:00
( let ( ( time-result ( process-overworld-time map Δt ) ) )
( if time-result
time-result
( process-overworld-input map ) ) ) )
2023-06-09 23:00:26 -05:00
2023-06-26 15:43:01 -05:00
( defun seconds->game-datetime ( seconds &key ( game-day-length 240 ) )
" Convert real-world SECONDS into a datetime plist, calculating with
GAME-DAY-LENGTH as as the seconds-per-day.
Returns a plist of properties :DAY, :HOUR, and :MINUTE, all numbers. "
( let* ( ( game-days ( floor ( / seconds game-day-length ) ) ) ;; Days passed in game-time
( seconds ( floor ( - seconds ( * game-days game-day-length ) ) ) ) ;; Keep hour below 24!
( real-day-length 1440 ) ) ;; You know what I mean <w<
( multiple-value-bind ( hour minutes-fraction )
( floor ( / ( * seconds ( / real-day-length game-day-length ) )
60 ) )
( list :day game-days :hour hour
:minute ( floor ( * 60 minutes-fraction ) ) ) ) ) )
2023-07-15 05:04:17 -05:00
( defun end-game-string ( map )
( str:concat
( if ( getf-act map :encourage-scientist )
( … :getf-lang ' ( :en "The cities of Etteburg and Bigborough live in peace. Doctor Klara Tim reached new heights in her professional career."
:eo "La urboj de Etburgo kaj Egburo apudvivas pace. Doktoro Klara Tim atingis altojn en sia kariero, plimemfide." ) )
( … :getf-lang ' ( :en "The city of Etteburg was nearly completely destroyed by the neighboring city Bigborough's police force, which claimed the city as its own." ) ) )
" "
( if ( getf-act map :perfect-friendship )
( … :getf-lang ' ( :en "Friendship with Sasha blossoms, and the two are closer than ever before. She no longer broods by the cliffside, but has reintegrated into society. What an impactful flower, huh?" ) )
( if ( getf-act map :encourage-friendship )
( … :getf-lang ' ( :en "Friendship with Sasha is better than ever before, yet still somewhat distant. Often, Sasha returns to the cliffside." ) )
( … :getf-lang ' ( :en "To this day, Sasha broods by the cliffside alone." ) ) ) ) ) )
( defun end-game ( map )
( setf flora-search-aurora:*knows* ( gethash :knows map ) )
( 🎭 :make-intermission-state
' ( :eo "LUDO FINITA" :en "GAME OVER" )
' ( :en "Where are they now?" )
( list :en ( end-game-string map ) )
2023-07-15 06:21:59 -05:00
( list :drop 3 ) ) )
2023-07-15 05:04:17 -05:00
2023-06-26 15:43:01 -05:00
( defun process-overworld-time ( map Δt )
" Do nothing, lol. Core part of OVERWORLD-STATE.
Returns parameters to be used in the next invocation of OVERWORLD-STATE. "
( let* ( ( time ( … :incf-0 ( gethash :seconds map ) Δt ) )
( game-datetime ( seconds->game-datetime time ) ) )
2023-07-15 05:04:17 -05:00
( if ( eq ( gethash :day map ) 3 )
( end-game map )
( progn
;; Go through the day-update procedures!
( when ( not ( eq ( getf game-datetime :day )
( gethash :day map ) ) )
( setf ( gethash :day map ) ( getf game-datetime :day ) ) )
nil ) ) ) )
2023-06-26 15:43:01 -05:00
2023-06-15 11:15:46 -05:00
( defun process-overworld-input ( map )
2023-06-09 23:00:26 -05:00
"Get and process any keyboard input, modifying the map or entities as necessary."
( if ( listen )
2023-06-27 11:54:52 -05:00
( let* ( ( input ( ⌨ :read-gamefied-char-plist ) ) )
( case ( getf input :semantic )
2023-06-16 14:28:41 -05:00
;; Interacting with nearby characters/entities
2023-06-27 11:54:52 -05:00
( ' ⌨ : 🆗
2023-06-28 09:56:25 -05:00
( let* ( ( player ( getf-entity map ' ✿ :player ) )
2023-06-16 14:28:41 -05:00
( interactee ( car ( entities-near-entity player ( gethash :entities map ) ) ) )
2023-06-26 11:12:06 -05:00
( interactee-id ( car interactee ) )
2023-06-16 14:28:41 -05:00
( interaction ( getf ( cdr interactee ) :interact ) ) )
( if interaction
2023-07-12 18:29:49 -05:00
( apply ( … :string->symbol interaction ) ( list map interactee-id ) )
2023-07-11 22:14:02 -05:00
( list :parameters ( list :map map ) ) ) ) )
2023-06-27 21:36:13 -05:00
( ' ⌨ : ❎
2023-07-14 08:50:02 -05:00
( list :function
( 📋 :make-menu-function
` ( ( :en "Continue" :eo "Malpaŭzigi"
2023-07-15 05:35:09 -05:00
:parameters , ( list :map map )
2023-07-14 08:50:02 -05:00
:drop 1 :selected t :selection 50 )
( :en "Backpack" :eo "Sako"
:function , ( 🎒 :make-inventory-function map )
:drop 1 )
( :en "Settings" :eo "Agordoj"
:function , ( 🔧 :make-settings-menu-function ) )
( :en "Give up" :eo "Rezigni"
:drop 3 ) ) ) ) )
2023-06-16 14:28:41 -05:00
;; Simple up-down-left-right movements
2023-06-27 11:54:52 -05:00
( ' ⌨ : →
2023-06-25 14:40:12 -05:00
( move-player map :Δx 1 ) )
2023-06-27 21:36:13 -05:00
( ' ⌨ : ←
2023-06-25 14:40:12 -05:00
( move-player map :Δx -1 ) )
2023-06-27 11:54:52 -05:00
( ' ⌨ : ↑
2023-06-25 14:40:12 -05:00
( move-player map :Δy -1 ) )
2023-06-27 11:54:52 -05:00
( ' ⌨ : ↓
2023-06-25 14:40:12 -05:00
( move-player map :Δy 1 ) )
2023-07-02 09:57:32 -05:00
( ' ⌨ : ↰
( move-player map :Δx -1 :Δy -1 ) )
( ' ⌨ : ↱
( move-player map :Δx 1 :Δy -1 ) )
( ' ⌨ : ↲
( move-player map :Δx -1 :Δy 1 ) )
( ' ⌨ : ↳
( move-player map :Δx 1 :Δy 1 ) )
2023-06-27 11:54:52 -05:00
( otherwise
2023-07-11 22:14:02 -05:00
( list :parameters ( list :map map ) ) ) ) )
( list :parameters ( list :map map ) ) ) )
2023-06-09 23:00:26 -05:00
2023-06-25 14:40:12 -05:00
( defun move-player ( map &key ( Δx 0 ) ( Δy 0 ) )
2023-07-07 10:33:32 -05:00
" Moves the play by the given changes in x & y.
Very kindly removes a list of parameters to be returned by the overworld state-function. "
2023-06-28 09:56:25 -05:00
( move-entity map ' ✿ :player :Δx Δx :Δy Δy )
( let* ( ( coords ( getf-entity-data map ' ✿ :player :coords ) )
2023-06-25 14:40:12 -05:00
( trigger ( trigger-at-coords map ( list :x ( getf coords :x ) :y ( getf coords :y ) ) ) ) )
( if ( and trigger ( getf trigger :function ) )
2023-07-12 18:29:49 -05:00
( apply ( … :string->symbol ( getf trigger :function ) )
2023-07-10 21:59:01 -05:00
( list map trigger ) )
2023-07-11 22:14:02 -05:00
( list :parameters ( list :map map ) ) ) ) )
2023-06-25 14:40:12 -05:00
( defun move-entity ( map entity-id &key ( Δx 0 ) ( Δy 0 ) )
2023-06-09 23:00:26 -05:00
"Move an entity relative to its current position."
2023-06-25 14:40:12 -05:00
( when ( < Δx 0 )
2023-06-26 20:25:02 -05:00
( setf ( getf-entity-data map entity-id :facing-right ) nil ) )
2023-06-25 14:40:12 -05:00
( when ( > Δx 0 )
2023-06-26 20:25:02 -05:00
( setf ( getf-entity-data map entity-id :facing-right ) 't ) )
2023-06-16 20:09:35 -05:00
( let ( ( coords ( getf-entity-data map entity-id :coords ) ) )
( move-entity-to map entity-id
2023-06-25 14:40:12 -05:00
:x ( + Δx ( getf coords :x ) )
:y ( + Δy ( getf coords :y ) ) ) ) )
2023-06-24 22:17:42 -05:00
2023-06-15 11:15:46 -05:00
( defun move-entity-to ( map entity &key ( x 0 ) ( y 0 ) )
2023-06-09 23:00:26 -05:00
"Move the given entity to the given coordinates."
2023-06-21 23:47:54 -05:00
( let ( ( old-chunk ( world-coords-chunk ( getf-entity-data map entity :coords ) ) )
( new-chunk ( world-coords-chunk ( list :x x :y y ) ) ) )
;; Change the entity’ s world coordinates…
2023-06-15 11:15:46 -05:00
( when ( walkable-tile-p map x y )
2023-06-21 23:47:54 -05:00
( setf ( getf ( getf-entity-data map entity :coords ) :x ) x )
2023-07-15 14:20:52 -05:00
( setf ( getf ( getf-entity-data map entity :coords ) :y ) y )
;; If the entity’ s moved into a different screen-chunk (and so into a different
;; sub-alist of MAP hash-table’ s :entities), move its list into the new chunk’ s.
( when ( not ( eq old-chunk new-chunk ) )
;; Add it to the new chunk list…
( setf ( assoc-utils:aget ( assoc-utils:aget ( gethash :entities map ) new-chunk ) entity )
( cdr ( getf-entity map entity ) ) )
;; Delete it from the old list…
( alexandria:deletef ( assoc-utils:aget ( gethash :entities map ) old-chunk ) entity
:test ( lambda ( id alist ) ( eq id ( car alist ) ) ) ) ) ) ) )
2023-06-15 11:15:46 -05:00
2023-06-09 07:07:28 -05:00
;;; ———————————————————————————————————
2023-06-16 20:09:35 -05:00
;;; Overworld-drawing: Map-rendering
2023-06-09 07:07:28 -05:00
;;; ———————————————————————————————————
2023-06-16 20:11:38 -05:00
( defun overworld-state-draw ( matrix map )
" Draw the overworld map to the given matrix.
A core part of OVERWORLD-STATE. "
2023-06-28 09:56:25 -05:00
( let* ( ( chunk ( world-coords-chunk ( getf-entity-data map ' ✿ :player :coords ) ) ) )
2023-06-24 14:13:01 -05:00
( matrix-write-tiles matrix ( gethash :tiles map ) chunk )
( matrix-write-entities matrix map chunk )
2023-06-26 15:43:01 -05:00
( when ( gethash :seconds map )
( matrix-write-datetime matrix ( seconds->game-datetime ( gethash :seconds map ) ) ) )
2023-06-24 14:13:01 -05:00
( matrix-write-tiles matrix ( gethash :top-tiles map ) chunk ) ) )
2023-06-16 20:11:38 -05:00
2023-06-26 15:43:01 -05:00
;;; ———————————————————————————————————
;;; Overworld-drawing: Map-tiles
;;; ———————————————————————————————————
2023-06-24 14:13:01 -05:00
( defun matrix-write-tiles ( matrix tiles chunk
&key ( chunk-width 72 ) ( chunk-height 20 ) )
2023-06-15 11:15:46 -05:00
"Draw a map’ s specific chunk (by its ID) to the matrix."
2023-06-16 12:41:19 -05:00
( mapcar ( lambda ( cell )
2023-06-23 12:41:47 -05:00
( if ( or ( not ( getf cell :lang ) )
( eq ( getf cell :lang ) ( … :system-language ) ) )
( matrix-write-cell matrix cell ) ) )
2023-06-24 14:13:01 -05:00
( cdr ( assoc chunk tiles ) ) ) )
2023-06-11 17:12:52 -05:00
2023-06-15 18:14:39 -05:00
( defun matrix-write-cell ( matrix cell )
2023-07-07 10:33:32 -05:00
" Set a matrice 's ( 2d array 's ) element corresponding to a “ cell ” ; that is, a
plist containing a character ( :CHAR ) and :X & :Y coordinates. "
( let ( ( coords ( world-coords->screen-coords ( list :x ( getf cell :x ) :y ( getf cell :y ) ) ) ) )
2023-06-13 18:10:15 -05:00
( setf ( aref matrix
( getf coords :y )
( getf coords :x ) )
2023-07-07 10:33:32 -05:00
( getf cell :@ ) ) ) )
2023-06-09 07:07:28 -05:00
2023-06-09 23:00:26 -05:00
;;; ———————————————————————————————————
2023-06-24 14:13:01 -05:00
;;; Overworld-drawing: Person-rendering
2023-06-09 23:00:26 -05:00
;;; ———————————————————————————————————
2023-06-21 23:47:54 -05:00
( defun matrix-write-entities ( matrix map chunk )
2023-06-09 23:00:26 -05:00
"Draw all entities from an alist of entities to the matrix."
( mapcar ( lambda ( entity-assoc )
2023-06-15 11:15:46 -05:00
( matrix-write-entity matrix ( cdr entity-assoc ) ) )
2023-06-21 23:47:54 -05:00
( cdr ( assoc chunk ( gethash :entities map ) ) ) ) )
2023-06-09 23:00:26 -05:00
( defun matrix-write-entity ( matrix entity-plist )
"Render an entity-plist to the matrix."
2023-06-25 19:59:34 -05:00
( when ( getf entity-plist :face )
( matrix-write-entity-head matrix entity-plist )
( matrix-write-entity-legs matrix entity-plist ) )
( when ( getf entity-plist :avatar )
( matrix-write-entity-avatar matrix entity-plist ) ) )
( defun matrix-write-entity-avatar ( matrix entity-plist )
"Draw an “avatar” entity; that is, not a person, but a random item."
( let* ( ( screen-coords ( world-coords->screen-coords ( getf entity-plist :coords ) ) )
( avatar ( getf entity-plist :avatar ) )
( width ( length avatar ) )
( y ( getf screen-coords :y ) )
( x ( - ( getf screen-coords :x ) ( floor ( / width 2 ) ) ) ) )
2023-06-30 13:52:50 -05:00
( ✎ :render-line matrix avatar ( list :x x :y y ) ) ) )
2023-06-14 16:36:34 -05:00
( defun matrix-write-entity-head ( matrix entity-plist )
"Draw an entity’ s head. There aren't any Mami Tomoes in this game, dang it!"
2023-06-13 18:10:15 -05:00
( let* ( ( screen-coords ( world-coords->screen-coords ( getf entity-plist :coords ) ) )
2023-06-14 16:36:34 -05:00
( face ( getf entity-plist :face ) )
( width ( + ( length face ) 2 ) ) ;; Face + |borders|
( y ( - ( getf screen-coords :y ) 1 ) )
2023-06-26 20:25:02 -05:00
( x ( if ( getf entity-plist :facing-right )
2023-06-14 16:36:34 -05:00
( - ( getf screen-coords :x ) ( floor ( / width 2 ) ) 0 )
( - ( getf screen-coords :x ) ( floor ( / width 2 ) ) 0 ) ) ) )
2023-06-30 13:52:50 -05:00
( ✎ :render-line matrix face ( list :x ( + x 1 ) :y y ) )
2023-06-14 16:36:34 -05:00
( ignore-errors ( setf ( aref matrix y x ) #\| ) )
( ignore-errors ( setf ( aref matrix y ( + width x -1 ) )
#\| ) ) ) )
2023-06-10 15:29:47 -05:00
( defun matrix-write-entity-legs ( matrix entity-plist )
2023-06-14 16:36:34 -05:00
"Draw a bipdel entity’ s legs — a surprisingly in-depth task!"
2023-06-13 18:10:15 -05:00
( let* ( ( screen-coords ( world-coords->screen-coords ( getf entity-plist :coords ) ) )
( x ( getf screen-coords :x ) )
2023-06-26 20:25:02 -05:00
( y ( getf screen-coords :y ) ) )
( cond ( ( getf entity-plist :facing-right )
2023-06-14 16:36:34 -05:00
( ignore-errors ( setf ( aref matrix y x ) #\| ) )
( ignore-errors ( setf ( aref matrix y ( - x 1 ) ) #\| ) ) )
2023-06-16 12:16:44 -05:00
( 't
2023-06-14 16:36:34 -05:00
( ignore-errors ( setf ( aref matrix y x ) #\| ) )
( ignore-errors ( setf ( aref matrix y ( + x 1 ) ) #\| ) ) ) ) ) )
2023-06-09 23:00:26 -05:00
2023-06-26 15:43:01 -05:00
;;; ———————————————————————————————————
;;; Overworld-drawing: The date
;;; ———————————————————————————————————
( defun game-datetime->string ( date-plist &optional ( year 2006 ) )
( format nil
( … :getf-lang ' ( :en "~A ~A ~A ~2,'0d:~2,'0d"
:eo "~A ~A ~Aa ~2,'0d:~2,'0d" ) )
year
( … :getf-lang ' ( :en "Jun" :eo "Jun" ) )
( + ( getf date-plist :day ) 3 )
( getf date-plist :hour )
( getf date-plist :minute ) ) )
( defun matrix-write-datetime ( matrix datetime )
( let ( ( string ( game-datetime->string datetime ) ) )
2023-06-30 13:52:50 -05:00
( ✎ :render-line matrix string ( list :x ( - 71 ( length string ) ) :y 19 ) ) ) )
2023-06-26 15:43:01 -05:00
2023-06-09 23:00:26 -05:00
;;; ———————————————————————————————————
2023-06-16 20:11:38 -05:00
;;; Overworld loop
2023-06-09 23:00:26 -05:00
;;; ———————————————————————————————————
2023-06-16 20:11:38 -05:00
( defun overworld-state
2023-06-26 15:43:01 -05:00
( matrix &key map ( Δt .02 ) )
2023-07-14 08:50:02 -05:00
" Render the given map to the MATRIX and take user-input — for one frame.
2023-06-16 20:11:38 -05:00
A state-function for use with STATE-LOOP. "
2023-06-26 15:43:01 -05:00
( sleep Δt )
2023-06-16 20:11:38 -05:00
( overworld-state-draw matrix map )
2023-06-26 15:43:01 -05:00
( overworld-state-update map Δt ) )
2023-06-23 13:29:09 -05:00
2023-07-13 22:55:16 -05:00
( defun make-overworld-function ( map )
2023-07-14 08:50:02 -05:00
"Return a state-function for a a MAP, for use with STATE-LOOP."
2023-06-26 11:12:06 -05:00
( lambda ( matrix &key ( map map ) )
2023-06-23 13:29:09 -05:00
( apply #' 🌍 :overworld-state
2023-06-26 11:12:06 -05:00
( list matrix :map map ) ) ) )
2023-07-14 08:50:02 -05:00
( defun make-overworld-state ( map )
"Return a state-plist for a a MAP, for use with STATE-LOOP."
( list :function ( make-overworld-function map ) ) )