Change entity faces while they’re talking
What a cool effect ^w^ ^o^ ^w^
This commit is contained in:
parent
218dcdc465
commit
95442b39db
|
@ -0,0 +1,129 @@
|
|||
;;;; 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/>.
|
||||
|
||||
;;;; FLORA-SEARCH-AURORA.OVERWORLD
|
||||
;;;; All game-functions and data relating to the “overworld” (that is,
|
||||
;;;; the primary gameplay, the RPG-ish-ish bits).
|
||||
|
||||
(defpackage :flora-search-aurora.dialogue
|
||||
(:nicknames :fsa.d :dialogue)
|
||||
(:use :cl
|
||||
:flora-search-aurora.overworld :flora-search-aurora.ui :flora-search-aurora.input)
|
||||
(:export #:dialogue-state #:say))
|
||||
|
||||
(in-package :flora-search-aurora.dialogue)
|
||||
|
||||
|
||||
|
||||
;;; ———————————————————————————————————
|
||||
;;; Dialogue-generation DSL (sorta)
|
||||
;;; ———————————————————————————————————
|
||||
(defun dialogue (&rest dialogue-tree)
|
||||
(reduce (lambda (a b) (append a b))
|
||||
dialogue-tree))
|
||||
|
||||
|
||||
(defun say (speaker text)
|
||||
(list
|
||||
(list :speaker speaker :text text :face 'talking-face)
|
||||
(car (face speaker 'normal-face))))
|
||||
|
||||
|
||||
(defun mumble (speaker text)
|
||||
(list
|
||||
(list :speaker speaker :text text)))
|
||||
|
||||
|
||||
(defun face (speaker face)
|
||||
(list
|
||||
(list :speaker speaker :face face)))
|
||||
|
||||
|
||||
|
||||
;;; ———————————————————————————————————
|
||||
;;; Dialogue logic
|
||||
;;; ———————————————————————————————————
|
||||
(defun pressed-enter-p ()
|
||||
(and (listen)
|
||||
(eq (getf (normalize-char-plist (read-char-plist)) :char)
|
||||
#\return)))
|
||||
|
||||
|
||||
(defun appropriate-face (map speaker face)
|
||||
(let ((talking-face (getf-entity-data map speaker :talking-face))
|
||||
(normal-face (getf-entity-data map speaker :normal-face)))
|
||||
(cond ((and (eq face 'talking-face)
|
||||
talking-face)
|
||||
talking-face)
|
||||
((and (eq face 'normal-face)
|
||||
normal-face)
|
||||
normal-face)
|
||||
((stringp face)
|
||||
face))))
|
||||
|
||||
|
||||
(defun dialogue-state-update (dialogue-list map)
|
||||
"The logic/input-processing helper function for DIALOGUE-STATE."
|
||||
(let* ((speaker (intern (string-upcase (getf (car dialogue-list) :speaker))))
|
||||
(new-face (appropriate-face map speaker
|
||||
(getf (car dialogue-list) :face))))
|
||||
;; Replace the face, when appropriate.
|
||||
(when new-face
|
||||
(setf (getf-entity-data map speaker :face) new-face)))
|
||||
;; Progress the dialogue as appropriate.
|
||||
(let ((text (getf (car dialogue-list) :text)))
|
||||
(cond ((or (pressed-enter-p)
|
||||
(not text))
|
||||
(if (cdr dialogue-list)
|
||||
(list :dialogue (cdr dialogue-list) :map map)
|
||||
(values nil
|
||||
(list :map map))))
|
||||
((cdr dialogue-list)
|
||||
(list :dialogue dialogue-list :map map))
|
||||
('t
|
||||
(values nil
|
||||
(list :map map))))))
|
||||
|
||||
|
||||
|
||||
;;; ———————————————————————————————————
|
||||
;;; Dialogue drawing
|
||||
;;; ———————————————————————————————————
|
||||
(defun dialogue-state-draw (matrix dialogue-list)
|
||||
"Draw the dialogue where appropriate.
|
||||
Helper function for DIALOGUE-STATE."
|
||||
(let ((text (getf (car dialogue-list) :text)))
|
||||
(when text
|
||||
(render-line matrix text 0 0))))
|
||||
|
||||
|
||||
|
||||
;;; ———————————————————————————————————
|
||||
;;; Dialogue loop
|
||||
;;; ———————————————————————————————————
|
||||
(defun dialogue-state (matrix &key dialogue map)
|
||||
"Render a bit of dialogue to the screen, using :FLORA-SEARCH-AURORA.OVERWORLD
|
||||
entities as the speakers. Dialogue should be in the format:
|
||||
((:text \"Hello, papa!\"
|
||||
:speaker \"son\" ;; The entity’s ID (if applicable)
|
||||
:face \"owo\") ;; If you want their face to change
|
||||
(:face \"=w=\" :speaker 'son) ;; change their face back when done talking
|
||||
(:text \"Hello, you little gremlin! <3\"
|
||||
:speaker \"papa\"
|
||||
...))
|
||||
A state-function for use with STATE-LOOP."
|
||||
(sleep .02)
|
||||
(dialogue-state-draw matrix dialogue)
|
||||
(dialogue-state-update dialogue map))
|
|
@ -25,12 +25,14 @@
|
|||
(load "overworld.util.lisp")
|
||||
(load "overworld.tiled.lisp")
|
||||
(load "overworld.lisp")
|
||||
(load "dialogue.lisp")
|
||||
|
||||
(defpackage :flora-search-aurora
|
||||
(:export #:main)
|
||||
(:use :cl
|
||||
:flora-search-aurora.input :flora-search-aurora.display
|
||||
:flora-search-aurora.overworld :flora-search-aurora.ui))
|
||||
:flora-search-aurora.overworld :flora-search-aurora.dialogue
|
||||
:flora-search-aurora.ui))
|
||||
|
||||
(in-package :flora-search-aurora)
|
||||
|
||||
|
@ -39,9 +41,9 @@
|
|||
|
||||
(defun literary-girl-dialogue (map)
|
||||
(lambda (matrix &key (map map)
|
||||
(dialogue
|
||||
`((:speaker "literary-girl" :face "xDx" :text "daddy")
|
||||
(:speaker "player" :face "<3" :text "i love u"))))
|
||||
(dialogue (dialogue::dialogue
|
||||
(dialogue::say "literary-girl" "Oh, hello.")
|
||||
(dialogue::say "player" "What, no quip?"))))
|
||||
(overworld-state-draw matrix map)
|
||||
(dialogue-state matrix :map map :dialogue dialogue)))
|
||||
|
||||
|
@ -50,18 +52,19 @@
|
|||
(states &key (last-matrix (make-screen-matrix)) (matrix (make-screen-matrix)) (state-params nil))
|
||||
"Begin the game’s loop, which executes (henceforthly called) state-functions over and over again
|
||||
until Hell freezes over and a new king reigns supreme.
|
||||
Given a list of state-functions, states, it will execute the first function.
|
||||
Given a list of state-functions, STATES, it will execute the first function.
|
||||
Each state-function must take at least a single parameter, a matrix of characters. A state-function
|
||||
should edit this matrix in-place, replacing its elements with characters that will later be printed
|
||||
to the terminal.
|
||||
If this function returns NIL, then it will be removed from state-function list and the next
|
||||
function in the list will be run in the next iteration. If no more functions remain, the loop
|
||||
terminates.
|
||||
If this function returns a list, then the given list will be used as additional parameters to the
|
||||
state-function in the next iteration, in addition to the aforementioned matrix.
|
||||
If this function returns a function, then the returned function will go to the front of the
|
||||
state-function list, and will be ran in the next iteration onward.
|
||||
If this function returns anything else, then it’ll simply be run again in the next iteration.
|
||||
What the state-function returns is pretty important:
|
||||
* NIL — The function is removed from STATES, and so the next function in STATES will start
|
||||
getting executed instead.
|
||||
* NIL; List — The function is popped off STATES and the list is used as the new parameters for
|
||||
the next function in STATES.
|
||||
* Function — The function is pushed to the front of STATES, and so is executed instead of the
|
||||
current function.
|
||||
* List — The current function (front of STATES) continues to be executed with the given
|
||||
list as a parameters list.
|
||||
Make note to add a delay w SLEEP to your state functions, or… well, y’know. Your computer will
|
||||
overheat, or something ¯\_(ツ)_/¯"
|
||||
(when states
|
||||
|
|
|
@ -253,36 +253,3 @@ A state-function for use with STATE-LOOP."
|
|||
(sleep .02)
|
||||
(overworld-state-draw matrix map)
|
||||
(overworld-state-update map))
|
||||
|
||||
|
||||
|
||||
;;; ———————————————————————————————————
|
||||
;;; Dialogue state
|
||||
;;; ———————————————————————————————————
|
||||
(defun dialogue-state-update (dialogue-list map)
|
||||
(if dialogue-list
|
||||
(let ((speaker (intern (string-upcase (getf (car dialogue-list) :speaker))))
|
||||
(face (getf (car dialogue-list) :face)))
|
||||
(setf (getf-entity-data map speaker :face) face)))
|
||||
|
||||
(cond ((and dialogue-list
|
||||
(listen)
|
||||
(eq (getf (normalize-char-plist (read-char-plist))
|
||||
:char)
|
||||
#\return))
|
||||
(list :dialogue (cdr dialogue-list) :map map))
|
||||
(dialogue-list
|
||||
(list :dialogue dialogue-list :map map))
|
||||
('t
|
||||
(values nil
|
||||
(list :map map)))))
|
||||
|
||||
|
||||
(defun dialogue-state-draw (matrix dialogue-list)
|
||||
(render-line matrix (getf (car dialogue-list) :text) 0 0))
|
||||
|
||||
|
||||
(defun dialogue-state (matrix &key dialogue map)
|
||||
(sleep .02)
|
||||
(dialogue-state-draw matrix dialogue)
|
||||
(dialogue-state-update dialogue map))
|
||||
|
|
|
@ -82,7 +82,9 @@ alist of Tiled cell “chunks”."
|
|||
(cl-tiled:map-tile-width tiled-map)))
|
||||
:y (floor (/ (cl-tiled:object-y tiled-obj)
|
||||
(cl-tiled:map-tile-height tiled-map))))
|
||||
:face (gethash "face" properties #'string-equal)
|
||||
:face (gethash "normal_face" properties #'string-equal)
|
||||
:normal-face (gethash "normal_face" properties #'string-equal)
|
||||
:talking-face (gethash "talking_face" properties #'string-equal)
|
||||
:interact (gethash "interact" properties #'string-equal)
|
||||
:direction (if (gethash "facing_right" properties #'string-equal)
|
||||
'right
|
||||
|
|
Ŝarĝante…
Reference in New Issue