# HG changeset patch # User Steve Losh # Date 1483822214 0 # Node ID dd9a7ef86a218133370dc89b45d1b6c22124dce7 # Parent 01ab77c9d46a917abb1f632022489415a097b8a4 Add ruins diff -r 01ab77c9d46a -r dd9a7ef86a21 antipodes.asd --- a/antipodes.asd Sat Jan 07 19:17:56 2017 +0000 +++ b/antipodes.asd Sat Jan 07 20:50:14 2017 +0000 @@ -26,9 +26,11 @@ (:module "aspects" :serial t :components ((:file "coordinates") (:file "holdable") + (:file "trigger") (:file "visible"))) (:module "entities" :serial t :components ((:file "food") + (:file "ruin") (:file "player"))) (:file "flavor") (:file "main"))))) diff -r 01ab77c9d46a -r dd9a7ef86a21 data/vegetables.lisp --- a/data/vegetables.lisp Sat Jan 07 19:17:56 2017 +0000 +++ b/data/vegetables.lisp Sat Jan 07 20:50:14 2017 +0000 @@ -38,7 +38,7 @@ "hubbard squash" "pickled jalapenos" "kale" - "kidney bean" + "kidney beans" "kohlrabi" "lavender" "leeks" diff -r 01ab77c9d46a -r dd9a7ef86a21 data/venues.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/venues.lisp Sat Jan 07 20:50:14 2017 +0000 @@ -0,0 +1,363 @@ +#("an Acupuncturist" + "an Adult Boutique" + "an Advertising Agency" + "an Afghan Restaurant" + "an African Restaurant" + "an American Restaurant" + "an Animal Shelter" + "an Antique Shop" + "an Aquarium" + "an Arcade" + "an Arepa Restaurant" + "an Argentinian Restaurant" + "an Art Gallery" + "an Art Museum" + "an Arts & Crafts Store" + "an Asian Restaurant" + "an Auditorium" + "an Australian Restaurant" + "an Austrian Restaurant" + "an Auto Garage" + "an Automotive Shop" + "a Baby Store" + "a Badminton Court" + "a Bagel Shop" + "a Bakery" + "a Bank" + "a Bar" + "a Baseball Field" + "a Baseball Stadium" + "a Basketball Court" + "a Basketball Stadium" + "a Bath House" + "a BBQ Joint" + "a Beer Garden" + "a Beer Store" + "a Belarusian Restaurant" + "a Belgian Restaurant" + "a Bike Shop" + "a Bistro" + "a Board Shop" + "a Bookstore" + "a Botanical Garden" + "a Bowling Alley" + "a Bowling Green" + "a Boxing Gym" + "a Brazilian Restaurant" + "a Brewery" + "a Bridal Shop" + "a Bridge" + "a Bubble Tea Shop" + "a Buddhist Temple" + "a Building" + "a Burger Joint" + "a Burrito Place" + "a Business Service" + "a Butcher" + "a Cafeteria" + "a Café" + "a Cajun Restaurant" + "a Cambodian Restaurant" + "a Camera Store" + "a Campaign Office" + "a Campground" + "a Candy Store" + "a Car Dealership" + "a Car Wash" + "a Caribbean Restaurant" + "a Carpet Store" + "a Casino" + "a Castle" + "a Caucasian Restaurant" + "a Cemetery" + "a Champagne Bar" + "a Check Cashing Service" + "a Cheese Shop" + "a Chinese Restaurant" + "a Chiropractor" + "a Chocolate Shop" + "a Christmas Market" + "a Church" + "a Circus" + "a City Hall" + "a Climbing Gym" + "a Clothing Store" + "a Club House" + "a Cocktail Bar" + "a Coffee Shop" + "a College Bookstore" + "a College Cafeteria" + "a College Classroom" + "a College Gym" + "a College Lab" + "a College Library" + "a College Residence Hall" + "a Comedy Club" + "a Comic Shop" + "a Community Center" + "a Concert Hall" + "a Conference Room" + "a Convenience Store" + "a Convention Center" + "a Cosmetics Shop" + "a Costume Shop" + "a Country Dance Club" + "a Courthouse" + "a Coworking Space" + "a Credit Union" + "a Creperie" + "a Cricket Ground" + "a Cuban Restaurant" + "a Cultural Center" + "a Cupcake Shop" + "a Czech Restaurant" + "a Dance Studio" + "a Daycare" + "a Deli" + "a Dentist's Office" + "a Department Store" + "a Design Studio" + "a Dessert Shop" + "a Dim Sum Restaurant" + "a Diner" + "a Disc Golf" + "a Discount Store" + "a Distillery" + "a Dive Bar" + "a Doctor's Office" + "a Donut Shop" + "a Driving School" + "a Drugstore" + "a Dry Cleaner" + "a Dumpling Restaurant" + "a Eastern European Restaurant" + "an Electronics Store" + "an Elementary School" + "an Embassy" + "an Emergency Room" + "an English Restaurant" + "an Ethiopian Restaurant" + "an Eye Doctor" + "a Fabric Shop" + "a Factory" + "a Falafel Restaurant" + "a Farm" + "a Farmers Market" + "a Fast Food Restaurant" + "a Filipino Restaurant" + "a Fire Station" + "a Fireworks Store" + "a Fish & Chips Shop" + "a Fish Market" + "a Fishing Store" + "a Flea Market" + "a Flower Shop" + "a Fondue Restaurant" + "a Food Court" + "a Food Truck" + "a Football Stadium" + "a Fraternity House" + "a French Restaurant" + "a Fried Chicken Joint" + "a Funeral Home" + "a Gas Station" + "a Gastropub" + "a Gay Bar" + "a German Restaurant" + "a Gift Shop" + "a Gluten-free Restaurant" + "a Go Kart Track" + "a Golf Course" + "a Greek Restaurant" + "a Grocery Store" + "a Gun Range" + "a Gun Shop" + "a Gym" + "a Hardware Store" + "a Hawaiian Restaurant" + "a Health Food Store" + "a High School" + "a Himalayan Restaurant" + "a Hindu Temple" + "a History Museum" + "a Hobby Shop" + "a Hockey Arena" + "a Hockey Field" + "a Hookah Bar" + "a Hospital" + "a Hot Dog Joint" + "a Hotel Bar" + "a Hotpot Restaurant" + "a Hungarian Restaurant" + "an Ice Cream Shop" + "an Indian Restaurant" + "an Indie Movie Theater" + "an Indie Theater" + "an Indonesian Restaurant" + "an Internet Cafe" + "an Irish Pub" + "an Italian Restaurant" + "a Japanese Restaurant" + "a Jazz Club" + "a Jewelry Store" + "a Juice Bar" + "a Karaoke Bar" + "a Knitting Store" + "a Korean Restaurant" + "a Kosher Restaurant" + "a Laboratory" + "a Laundromat" + "a Library" + "a Lighthouse" + "a Lingerie Store" + "a Liquor Store" + "a Locksmith" + "a Lounge" + "a Mac & Cheese Joint" + "a Malaysian Restaurant" + "a Marijuana Dispensary" + "a Martial Arts Dojo" + "a Massage Studio" + "a Mattress Store" + "a Medical Center" + "a Mediterranean Restaurant" + "a Mental Health Office" + "a Mexican Restaurant" + "a Middle Eastern Restaurant" + "a Middle School" + "a Mini Golf Course" + "a Mobile Phone Shop" + "a Modern European Restaurant" + "a Molecular Gastronomy Restaurant" + "a Monastery" + "a Mongolian Restaurant" + "a Moroccan Restaurant" + "a Mosque" + "a Motorcycle Shop" + "a Movie Theater" + "a Multiplex" + "a Museum" + "a Music School" + "a Music Store" + "a Nail Salon" + "a New American Restaurant" + "a Newsstand" + "a Nightclub" + "a Noodle House" + "a Nursery School" + "an Opera House" + "an Optical Shop" + "an Organic Grocery" + "an Outlet Store" + "a Paintball Field" + "a Pakistani Restaurant" + "a Pawn Shop" + "a Perfume Shop" + "a Persian Restaurant" + "a Peruvian Restaurant" + "a Pet Store" + "a Photography Lab" + "a Piano Bar" + "a Pie Shop" + "a Piercing Parlor" + "a Pizza Place" + "a Planetarium" + "a Police Station" + "a Polish Restaurant" + "a Pool Hall" + "a Pool" + "a Portuguese Restaurant" + "a Post Office" + "a Prayer Room" + "a Preschool" + "a Private School" + "a Pub" + "a Public Art" + "a Racetrack" + "a Radio Station" + "a Record Shop" + "a Recording Studio" + "a Recreation Center" + "a Recruiting Agency" + "a Religious School" + "a Rock Club" + "a Roller Rink" + "a Romanian Restaurant" + "a Rugby Pitch" + "a Russian Restaurant" + "a Sake Bar" + "a Salon" + "a Salsa Club" + "a Sandwich Place" + "a Scandinavian Restaurant" + "a Science Museum" + "a Sculpture Garden" + "a Seafood Restaurant" + "a Shoe Repair" + "a Shoe Store" + "a Shrine" + "a Skate Park" + "a Skating Rink" + "a Snack Place" + "a Soccer Field" + "a Soccer Stadium" + "a Sorority House" + "a Soup Place" + "a South American Restaurant" + "a Southern Restaurant" + "a Souvenir Shop" + "a Souvlaki Shop" + "a Spa" + "a Spanish Restaurant" + "a Speakeasy" + "a Sporting Goods Shop" + "a Sports Bar" + "a Sports Club" + "a Squash Court" + "a Sri Lankan Restaurant" + "a Stadium" + "a Stationery Store" + "a Steakhouse" + "a Storage Facility" + "a Supermarket" + "a Sushi Restaurant" + "a Swiss Restaurant" + "a Synagogue" + "a Taco Place" + "a Tanning Salon" + "a Tapas Restaurant" + "a Tatar Restaurant" + "a Tattoo Parlor" + "a Tea Room" + "a Temple" + "a Tennis Court" + "a Thai Restaurant" + "a Theater" + "a Theme Park" + "a Thrift Store" + "a Tibetan Restaurant" + "a Town Hall" + "a Toy Store" + "a Track Stadium" + "a Travel Agency" + "a Turkish Restaurant" + "a TV Station" + "a Ukrainian Restaurant" + "a Used Bookstore" + "a Vegan Restaurant" + "a Veterinarian's Office" + "a Video Game Store" + "a Video Store" + "a Vietnamese Restaurant" + "a Volleyball Court" + "a Warehouse Store" + "a Warehouse" + "a Watch Repair Shop" + "a Water Park" + "a Whisky Bar" + "a Wine Bar" + "a Wine Shop" + "a Winery" + "a Wings Joint" + "a Yoga Studio" + "a Zoo") diff -r 01ab77c9d46a -r dd9a7ef86a21 package.lisp --- a/package.lisp Sat Jan 07 19:17:56 2017 +0000 +++ b/package.lisp Sat Jan 07 20:50:14 2017 +0000 @@ -57,11 +57,19 @@ :make-food :food/energy + :trigger + :trigger? + :trigger/text + + :ruin + :make-ruin + :coords :coords/x :coords/y :coords? :coords-lookup + :coords-nearby :coords-move-entity :holdable diff -r 01ab77c9d46a -r dd9a7ef86a21 src/aspects/coordinates.lisp --- a/src/aspects/coordinates.lisp Sat Jan 07 19:17:56 2017 +0000 +++ b/src/aspects/coordinates.lisp Sat Jan 07 20:50:14 2017 +0000 @@ -28,7 +28,7 @@ (when (within-bounds-p x y) (aref *world-contents* x y))) -(defun nearby (entity &optional (radius 1)) +(defun coords-nearby (entity &optional (radius 1)) (remove entity (iterate (with x = (coords/x entity)) (with y = (coords/y entity)) diff -r 01ab77c9d46a -r dd9a7ef86a21 src/aspects/trigger.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aspects/trigger.lisp Sat Jan 07 20:50:14 2017 +0000 @@ -0,0 +1,3 @@ +(in-package :ap.entities) + +(define-aspect trigger text) diff -r 01ab77c9d46a -r dd9a7ef86a21 src/aspects/visible.lisp --- a/src/aspects/visible.lisp Sat Jan 07 19:17:56 2017 +0000 +++ b/src/aspects/visible.lisp Sat Jan 07 20:50:14 2017 +0000 @@ -1,4 +1,3 @@ (in-package :ap.entities) - (define-aspect visible glyph color) diff -r 01ab77c9d46a -r dd9a7ef86a21 src/config.lisp --- a/src/config.lisp Sat Jan 07 19:17:56 2017 +0000 +++ b/src/config.lisp Sat Jan 07 20:50:14 2017 +0000 @@ -6,3 +6,8 @@ (defparameter *noise-scale* 0.03) (defparameter *noise-seed-x* (random 1000.0)) (defparameter *noise-seed-y* (random 1000.0)) + +(defparameter *ruin-density* 1/1000) +(defparameter *ruin-size-mean* 10.0) +(defparameter *ruin-size-dev* 2.0) +(defparameter *graffiti-chance* 1/10) diff -r 01ab77c9d46a -r dd9a7ef86a21 src/entities/ruin.lisp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/entities/ruin.lisp Sat Jan 07 20:50:14 2017 +0000 @@ -0,0 +1,39 @@ +(in-package :ap.entities) + +(defparameter *venues* + (read-file-into-form "data/venues.lisp")) + +(define-entity ruin (coords trigger)) + +(defun random-ruin-text () + (format nil "You see the ruins of ~A.~2%~A" + (random-elt *venues*) + (if (randomp ap::*graffiti-chance*) + (format nil "Someone has graffitied \"~A\" on the wall." + (string-upcase (random-elt #("give up" + "why me?" + "tom <3 alice" + "dave was here" + "420" + "goodbye" + "5823" + "jesus saves" + "hail satan" + "run" + "head north" + "head south" + "were damned" + "hope is lost" + "turn back")))) + (random-elt #("Maybe there's something useful left." + "Perhaps you should scavenge?" + "It brings back fond memories." + "Your parents used to live near one of these." + "The world has suddenly gotten quiet." + "A remnant of a happier time."))))) + +(defun make-ruin (x y) + (create-entity 'ruin + :coords/x x + :coords/y y + :trigger/text (random-ruin-text))) diff -r 01ab77c9d46a -r dd9a7ef86a21 src/main.lisp --- a/src/main.lisp Sat Jan 07 19:17:56 2017 +0000 +++ b/src/main.lisp Sat Jan 07 20:50:14 2017 +0000 @@ -17,6 +17,7 @@ (defparameter *height* nil) (defparameter *terrain* nil) +(defparameter *structures* nil) (defparameter *view-x* nil) (defparameter *view-y* nil) @@ -24,6 +25,19 @@ (defparameter *player* nil) +;;;; Colors ------------------------------------------------------------------- +(defcolors + (+white-black+ charms/ll:COLOR_WHITE charms/ll:COLOR_BLACK) + (+blue-black+ charms/ll:COLOR_BLUE charms/ll:COLOR_BLACK) + (+cyan-black+ charms/ll:COLOR_CYAN charms/ll:COLOR_BLACK) + (+yellow-black+ charms/ll:COLOR_YELLOW charms/ll:COLOR_BLACK) + (+green-black+ charms/ll:COLOR_GREEN charms/ll:COLOR_BLACK) + (+pink-black+ charms/ll:COLOR_MAGENTA charms/ll:COLOR_BLACK) + + (+black-white+ charms/ll:COLOR_BLACK charms/ll:COLOR_WHITE) + ) + + ;;;; Heightmap ---------------------------------------------------------------- ;;; TODO: Switch to something less samey @@ -49,18 +63,79 @@ (noise-heightmap heightmap) heightmap)) +(defun random-coord () + (random *map-size*)) -;;;; Colors ------------------------------------------------------------------- -(defcolors - (+white-black+ charms/ll:COLOR_WHITE charms/ll:COLOR_BLACK) - (+blue-black+ charms/ll:COLOR_BLUE charms/ll:COLOR_BLACK) - (+cyan-black+ charms/ll:COLOR_CYAN charms/ll:COLOR_BLACK) - (+yellow-black+ charms/ll:COLOR_YELLOW charms/ll:COLOR_BLACK) - (+green-black+ charms/ll:COLOR_GREEN charms/ll:COLOR_BLACK) - (+pink-black+ charms/ll:COLOR_MAGENTA charms/ll:COLOR_BLACK) +(defun underwaterp (height) + (< height -0.05)) + +(defun deepwaterp (height) + (< height -0.20)) + + +;;;; Ruins -------------------------------------------------------------------- +(defun make-empty-structures () + (make-array (list *map-size* *map-size*))) + +(defun passablep (structure) + (if (member structure '(:wall)) + nil + t)) + +(defun add-intact-ruin (width height start-x start-y) + (iterate (for-nested ((x :from start-x :below (+ start-x width)) + (y :from start-y :below (+ start-y height)))) + (setf (aref *structures* x y) :floor)) + (iterate (repeat width) + (for x :from start-x) + (setf (aref *structures* x start-y) :wall + (aref *structures* x (+ start-y height -1)) :wall)) + (iterate (repeat height) + (for y :from start-y) + (setf (aref *structures* start-x y) :wall + (aref *structures* (+ start-x width) y) :wall))) - (+black-white+ charms/ll:COLOR_BLACK charms/ll:COLOR_WHITE) - ) +(defun add-ruin-door (width height start-x start-y) + (setf (aref *structures* (+ start-x (random width)) + (if (randomp) + start-y + (+ start-y height -1))) + nil)) + +(defun decay-ruin (width height start-x start-y condition) + (iterate (for-nested ((x :from start-x :to (+ start-x width)) + (y :from start-y :below (+ start-y height)))) + (when (or (randomp condition) + (and (deepwaterp (aref *terrain* x y)) + (not (eq :wall (aref *structures* x y))))) + (setf (aref *structures* x y) nil)))) + +(defun place-ruin-food (width height start-x start-y) + (iterate (repeat (random 4)) + (make-food + (random-range (1+ start-x) (+ start-x width)) + (random-range (1+ start-y) (+ start-y height))))) + +(defun add-ruin-trigger (width height start-x start-y) + (make-ruin (+ start-x (truncate width 2)) + (+ start-y (truncate height 2)))) + +(defun add-ruin () + (let ((x (clamp 0 (- *map-size* 50) (random-coord))) + (y (clamp 0 (- *map-size* 50) (random-coord))) + (width (max 5 (truncate (random-gaussian *ruin-size-mean* *ruin-size-dev*)))) + (height (max 5 (truncate (random-gaussian *ruin-size-mean* *ruin-size-dev*)))) + (condition (random-range 0.1 0.8))) + (add-intact-ruin width height x y) + (add-ruin-door width height x y) + (decay-ruin width height x y condition) + (place-ruin-food width height x y) + (add-ruin-trigger width height x y))) + +(defun fill-ruins () + (iterate + (repeat (round (* *ruin-density* *map-size* *map-size*))) + (add-ruin))) ;;;; Intro -------------------------------------------------------------------- @@ -124,9 +199,6 @@ ;;;; World Generation --------------------------------------------------------- -(defun underwaterp (height) - (< height 0.05)) - (defun generate-terrain () (setf *terrain* (generate-heightmap) *view-x* 0 *view-y* 0)) @@ -140,15 +212,19 @@ *map-size* *map-size*))) (until (zerop remaining)) - (for x = (random *map-size*)) - (for y = (random *map-size*)) + (for x = (random-coord)) + (for y = (random-coord)) (when (not (underwaterp (aref *terrain* x y))) (make-food x y) (decf remaining)))) +(defun generate-structures () + (setf *structures* (make-empty-structures)) + (fill-ruins)) + (defun generate-world () (clear-entities) - (with-dims (30 (+ 2 3)) + (with-dims (30 (+ 2 4)) (with-panel-and-window (pan win *width* *height* (center *width* *screen-width*) @@ -157,10 +233,13 @@ (progn (write-string-left win "Generating terrain..." 1 1) (redraw) (generate-terrain)) - (progn (write-string-left win "Placing food..." 1 2) + (progn (write-string-left win "Generating structures..." 1 2) + (redraw) + (generate-structures)) + (progn (write-string-left win "Placing food..." 1 3) (redraw) (place-food)) - (progn (write-string-left win "Spawning player..." 1 3) + (progn (write-string-left win "Spawning player..." 1 4) (redraw) (spawn-player)))) (world-map)) @@ -190,7 +269,13 @@ ((< height 0.05) (values #\` +yellow-black+)) ; sand ((< height 0.40) (values #\. +white-black+)) ; dirt ((< height 0.55) (values #\^ +white-black+)) ; hills - (t (values #\# +white-black+)))) ; mountains + (t (values #\* +white-black+)))) + +(defun structure-char (contents) + (case contents + (:wall #\#) + (:floor #\_))) + (defun clamp-view (coord size) (clamp 0 (- *map-size* size 1) coord)) @@ -204,18 +289,27 @@ (coords/x *player*) (coords/y *player*))) + (defun render-items (window) - (let ((items (-<> (coords-lookup (coords/x *player*) - (coords/y *player*)) - (remove-if-not #'holdable? <>)))) + (let* ((x (coords/x *player*)) + (y (coords/y *player*)) + (items (-<> (coords-lookup x y) + (remove-if-not #'holdable? <>))) + (here-string (if (underwaterp (aref *terrain* x y)) + "floating here" + "here"))) (when items (if (= (length items) 1) (write-string-left window - (format nil "You see ~A here" (holdable/description (first items))) + (format nil "You see ~A ~A" + (holdable/description (first items)) + here-string) 0 0) (progn - (write-string-left window "The following things are here:" 0 0) + (write-string-left window (format nil "The following things are ~A:" + here-string) + 0 0) (iterate (for item :in items) (for y :from 1) @@ -226,15 +320,22 @@ (defun render-map (window) (iterate (with terrain = *terrain*) + (with structures = *structures*) (with vx = *view-x*) (with vy = *view-y*) - (for-nested ((sx :from 0 :below *width*) - (sy :from 0 :below *height*))) + (for-nested ((sx :from 0 :below (1- *width*)) + (sy :from 0 :below (1- *height*)))) (for x = (+ sx vx)) (for y = (+ sy vy)) - (for (values glyph color) = (terrain-char (aref terrain x y))) - (with-color (window color) - (charms:write-char-at-point window glyph sx sy)) + + (for (values terrain-glyph terrain-color) = (terrain-char (aref terrain x y))) + (with-color (window terrain-color) + (charms:write-char-at-point window terrain-glyph sx sy)) + + (for structure-glyph = (structure-char (aref structures x y))) + (when structure-glyph + (charms:write-char-at-point window structure-glyph sx sy)) + (for entities = (coords-lookup x y)) (for entity = (if (member *player* entities) *player* @@ -268,10 +369,11 @@ (defun move-player (dx dy) - (let ((player *player*)) - (coords-move-entity player - (+ (coords/x player) dx) - (+ (coords/y player) dy)))) + (let* ((player *player*) + (dest-x (+ (coords/x player) dx)) + (dest-y (+ (coords/y player) dy))) + (when (passablep (aref *structures* dest-x dest-y)) + (coords-move-entity player dest-x dest-y)))) (defun world-map-input (window) (case (charms:get-char window) @@ -283,6 +385,13 @@ (:down (move-player 0 1) :tick))) +(defun check-triggers () + (iterate (for trigger :in (-<> *player* + (coords-nearby <> 10) + (remove-if-not #'trigger? <>))) + (popup (trigger/text trigger)) + (destroy-entity trigger))) + (defun world-map () (with-dims ((- *screen-width* 2) (- *screen-height* 1)) (with-panels-and-windows @@ -301,7 +410,8 @@ (if (ap.flavor:flavorp) (popup (ap.flavor:random-flavor)) (case (world-map-input bar-win) - (:tick (tick-player *player*)) + (:tick (tick-player *player*) + (check-triggers)) (:quit (return)) (:help (popup *help*)))))))) nil)