From 89d0d687992b41f7f0f9b0d3da19d9d587f06010 Mon Sep 17 00:00:00 2001 From: Grant Shoshin Shangreaux Date: Sun, 5 Mar 2023 11:33:08 -0600 Subject: Add: page-render-mode, model cleanup, tavern view wip --- DEV.org | 15 +++++++++++++++ endpoints.lisp | 12 +++++++++--- model.lisp | 48 +++++++++++++++++++++++++++++++++++++++--------- pages.lisp | 40 ++++++++++++++++++++++++++++++---------- 4 files changed, 93 insertions(+), 22 deletions(-) diff --git a/DEV.org b/DEV.org index f123a54..66f3c53 100644 --- a/DEV.org +++ b/DEV.org @@ -8,6 +8,21 @@ - [X] handle post (set a cookie/header) - [X] make a tavern page ** TODO Make Campaigns + - creator + - seers + - title + - rumors + +As a player, I can "petition the Emperor" to submit a request for a campaign +against the darkness. I give this campaign a title it shall be known by. +I will become the CREATOR of the campaign, and one of its SEERS. I can +appoint fellow players as additional SEERS. + +Once a campaign exists, any player may submit a RUMOR related to said +campaign. Rumors are viewable by all, but SEERS may act upon those rumors +to lay out a plan of attack, create a QUEST which is filled with HAZARDS +informed by those rumors. + ** TODO Make Monsters (in campaigns) ** TODO Attack Monsters (assign heroes / change status) diff --git a/endpoints.lisp b/endpoints.lisp index 4cd9aef..96e3876 100644 --- a/endpoints.lisp +++ b/endpoints.lisp @@ -53,6 +53,13 @@ I.e. It should be called within the scope of a request handler." "Returns T if user agent string matches on a list of known text browsers." (some (lambda (s) (search s user-agent)) '("Emacs" "Lynx" "w3m"))) +(defun page-render-mode (user-agent) + "Given the USER-AGENT string from request headers, returns a symbol which +indicates which render mode to use. For example if Emacs is the user-agent, +return :text-12mode." + (cond ((text-browser-p user-agent) :text-page) + (t :page))) + (defmacro with-session ((player &key session (redirect "/tavern-door")) &body body) (let ((session (or session (gensym "SESSION")))) `(a:if-let (,session (current-session)) @@ -129,9 +136,8 @@ functions in url parameters in endpoint definitions." (defendpoint* :get "/tavern" () () (with-session (me) - (if (text-browser-p (lzb:request-header :user-agent)) - (tavern-text me) - (tavern me)))) + (render (page-render-mode (lzb:request-header :user-agent)) + (make-instance 'tavern :player me)))) (defendpoint* :get "/godess-shrine" () () (with-session (player) diff --git a/model.lisp b/model.lisp index 0598cf7..fef01da 100644 --- a/model.lisp +++ b/model.lisp @@ -103,7 +103,7 @@ :initarg :campaign :initform nil :type campaign - :documentation "A hero may be in at mostk one campaign at a time.")) + :documentation "A hero may be in at most one campaign at a time.")) (:metaclass db:persistent-class)) @@ -123,51 +123,75 @@ :reader campaign-creator :initarg :creator :initform (error "campaigns must have a creator") - :documentation "The hero instance of the user who made this campaign.") + :type player + :documentation "The player instance of the user who made this campaign.") (seers :accessor campaign-seers :initarg :seers :initform nil + :type (or nil (cons player)) :documentation "Seers are the people who peer out into their instruments of divination that heroes may go on quests.") (title - :accessor campaign-title + :accessor title :initarg :title - :initform (error "A campaign needs a title")) + :initform (error "A campaign needs a title") + :type string) (rumors :accessor campaign-rumors - :initarg nil + :initform nil + :type (or nil (cons rumor)) :documentation "Beasts, Monsters, and Hazards rumored to be lurking about.")) (:metaclass db:persistent-class) (:documentation "A campaign is a container of quests. Campaigns are expected to be engaged with on a particular schedule, and are run by particular people.")) +(defclass rumor (db:store-object) + ((reporter + :reader rumor-reporter + :initarg :reporter + :type player + :documentation "The player who hast reported the vile rumor.") + (content + :accessor rumor-content + :initform (error "A rumor must have content") + :initarg :content + :type string + :documentation "A description of the supposed peril that awaits heroes in a particular campaign.")) + (:metaclass db:persistent-class) + (:documentation "Transcript of a rumor reported by some player related to a Campaign.")) + (defclass quest (game-object) ((campaign :reader quest-campaign :initarg :campaign :initform (error "No quest can fall outside the scope of a campaign.") + :type campaign :index-type idx:hash-index :index-reader quests-in-campaign :documentation "The campaign to which this quest belongs") (name :accessor quest-name :initarg :name + :type string :initform (format nil "~a" (gensym "QUEST"))) (horizon-of-hope :accessor horizon-of-hope :initarg :deadline + :type integer :initform nil :documentation "When all hope becomes lost.") (heroes :accessor heroes-on-quest :initarg :heroes :initform nil + :type (or nil (cons hero)) :documentation "A list of heroes in this quest. Join and flight dates are logged in the chronicle.") - (startedp - :accessor quest-startedp + (inception + :accessor quest-inception :initform nil - :documentation "Indication of whether the quest is active or not - i.e. whether heroes are on this quest.")) + :type (or nil integer) + :documentation "Time at which the quest began.")) (:metaclass db:persistent-class) - (:documentation "A collection of hazards")) + (:documentation "A collection of hazards with a deadline and start date which heroes will attack.")) (defclass hazard (game-object) ((quest @@ -175,6 +199,12 @@ :index-type idx:hash-index :index-reader hazards-in-quest :documentation "The quest to which this hazard belongs. Initially it is unbound. It becomes boudn when the hazard is added to a quest.") + (description + :accessor description + :initarg :description + :initform "" + :type string + :documentation "") (overcomep :accessor is-overcome :initform nil diff --git a/pages.lisp b/pages.lisp index 9701334..378156a 100644 --- a/pages.lisp +++ b/pages.lisp @@ -32,6 +32,14 @@ must specialize :left :milsddle :right on your desired data type." (:div :class "middle-column" (render :middle data)) (:div :class "right-column" (render :right data))))) +;;; Object-Endpoint Helpers + +(defgeneric url-path-to (obj) + (:documentation "Return a string that is the absolute url path to OBJ.")) + +(defmethod url-path-to ((obj campaign)) + (format nil "/campaign/~a" (urlify (title obj)))) + ;;; PAGES (defmacro with-page ((&key title) &body body) @@ -95,19 +103,31 @@ must specialize :left :milsddle :right on your desired data type." (with-page (:title "A Bustling Tavern") (render :three-column-layout tavern))) -(defun tavern-text (player) - (with-page (:title "A Bustling Tavern") - (render :page-text (make-instance 'tavern :player player)))) - -(defrender :page-text ((tavern tavern)) +(defrender :text-page ((tavern tavern)) (let ((player (player tavern))) (with-html (render :details player) - (:table - (:tr (:td (:h4 "Your Heroes")) - (:td (:h4 "Your Campaigns"))) - (:tr (:td (:h4 "Gossip & Gab")) - (:td (:h4 "Comrades in Arms"))))))) + (render :list (player-heroes player)) + (:a :href "/spymaster" "Report a Roguish Rumour...") + ;; (:table + ;; (:tr (:td (:h4 "Your Heroes")) + ;; (:td (:h4 "Your Campaigns"))) + ;; (:tr (:td (:h4 "Gossip & Gab")) + ;; (:td (:h4 "Comrades in Arms")))) + + ))) + +(defrender :list-item ((hero hero)) + (with-html + (with-slots ((name campaign) hero) + (:p name "the" (hero-class hero) (hero-title hero) + (when campaign + (:span "who is off in the campaign") + (:span (render :inline campaign))))))) + +(defrender :inline ((campaign campaign)) + (with-html + (:a :href (url-path-to campaign) (title campaign)))) (defrender :left ((tavern tavern)) (let ((player (player tavern))) -- cgit v1.2.3