luv

Workshop wiki

party.lisp

luvcraft/birthday/party.lisp

system luvcraft/birthday · 13 definitions · on GitHub

The party: how the aspects compose into one scene.

celebrate-birthday is the only place that knows the whole arrangement: where the gazebo stands on the meadow, how the balloons tie to its posts, where the gnomes dance, where the funny balls scatter, and when the sky settles into the pinned dusk the fireworks want. Each aspect stays ignorant of the others; changing the composition means editing only this file.

The world lives under its own save file beside the ordinary one, so the everyday game is untouched and the party can be reopened any time.

in-package#:luvcraft.birthday
defunbirthday-world-pathname
name

The party world's save, beside the ordinary world under name's name.

merge-pathnames
formatnil"~(~a~)-birthday.sexp"name
defunbirthday-age
birth-yearbirth-monthbirth-day

How old someone born on that date is today, by the wall calendar.

multiple-value-bind
secondminutehourdaymonthyear
decode-universal-time
get-universal-time
declare
-yearbirth-year
if
or
<monthbirth-month
and
=monthbirth-month
<daybirth-day
10
defunhonoree-for-host
host

(values name age) for the machine named host.

The party follows the computer: on wigwam it is Daniel's, born the fifteenth of August 1985 -- belatedly, most years -- with his age read off the calendar rather than written down. Everywhere else it is Alex's, who is four.

let*
short
subseqhost0
if
string-equalshort"wigwam"
values"DANIEL"
values"ALEX"4
defunparty-honoree
honoree-for-host
machine-instance
defparameter*party-sky-hour*18.0

Pinned time of day: past sunset, before deep night, so a warm band still hangs on the horizon while the stars and fireworks read against the indigo above it.

defunbirthday-surface-height
worldxz

The y of the highest solid block in the column, probing downward.

loopforyfrom30downto0forblock=whenblockreturnyfinally
defunballoon-plan
anchors

Balloon plists from the gazebo's anchors: one balloon floating above each post, and a bouquet crowding the roof finial.

let
balloons'
hue0.0
loopfor
xyz
in
getfanchors:post-tops
do
push
list:x
+x0.0
:y
+y3.4
:z
+z0.0
:radius0.5:huehue:phase
*hue7.0
balloons
incfhue0.13
destructuring-bind
xyz
getfanchors:roof-apex
loopfor
dxdydz
in'
0.01.80.0
-0.92.30.4
0.82.6-0.3
0.33.00.8
-0.52.0-0.8
do
push
list:x
+xdx
:y
+ydy
:z
+zdz
:radius0.55:huehue:phase
*hue9.0
balloons
incfhue0.17
balloons
defungnome-ring
worldcenter-xcenter-z&key
count6
radius10.5

Gnome plists standing in a ring on the lawn around the gazebo.

loopforindexbelowcountforangle=
/
*2piindex
count
forx=
+center-x
*radius
forz=
+center-z
*radius
forground=
birthday-surface-heightworld
roundx
roundz
collect
list:x:y
float
1+ground
:z:scale
+0.85
*0.3
/indexcount
:phase
float
/indexcount
:hue
float
/indexcount
defuncelebrate-birthday
&keynameagefullscreen-p
visible-pt
widthheight
frames-per-second60
register-pt

Throw the party: open the birthday meadow and decorate it.

Loads or creates the party world under its own save file, raises the gazebo, ties on the balloons, calls in the gnomes, scatters the funny balls, hangs the greeting in the air, pins the sky at dusk, and starts the fireworks. Returns the session; luvcraft:stop-playing ends the party and saves it.

name and age default by machine: see honoree-for-host. A film or capture passes :visible-p NIL, its exact :width and :height, :frames-per-second NIL, and :register-p NIL so the hidden party neither displaces nor becomes the interactive *SESSION*.

multiple-value-bind
host-namehost-age
unlessname
setfnamehost-name
unlessage
setfagehost-age
let
multiple-value-bind
worldresume-description
if
probe-filepathname

The lawn must be resident before the gazebo's edits can land. Rebuilding over an existing gazebo rewrites the same cells with the same blocks, so the build is idempotent across reopenings.

luvcraft:center-little-world-residency
luvcraft::block-world-sourceworld
world00:radius3

The meadow promises a flat lawn at *CLEARING-HEIGHT*, so the gazebo is told its ground rather than probing for it: the probe scans the column from above the top chunk layer, which a restored world's space rejects rather than reading as absent.

let
anchors
build-birthday-gazeboworld:center-x0:center-z0:ground-y
multiple-value-bind
cameraplayerselected-blockcarried
declare
ignoreselected-blockcarried

A fresh party spawns at the vantage that frames gazebo and greeting together; a resumed one keeps its player where Alex last stood.

unlessresume-description
setfcamera
make-instance'luvcraft:fly-camera:position
luvcraft::make-vec30d010.5d0-21d0
:yaw0.0d0:pitch0.08d0
player
let
unwind-protect
setfsession
luvcraft:start-luvcraft:providerprovider:title
formatnil"luvcraft — ~:(~a~)'s birthday!"name
:worldworld:cameracamera:playerplayer:fullscreen-pfullscreen-p:visible-pvisible-p:widthwidth:heightheight:frames-per-secondframes-per-second:checkpoint-writerwriter
let
clock
luvcraft:luvcraft-session-sky-clocksession
setfsky-hour
luvcraft:sky-clock-paused-pclock
t

The lobby's corner instrument has no place at a party.

dolist
overlay
luvcraft::luvcraft-session-overlayssession

The greeting is a marquee wall over the entrance: the name and the age in scrolling lights, mounted above the eave.

add-birthday-marqueesession:text
formatnil"~:@(~a~) ~d"nameage
luvcraft::scatter-party-ballssession:center'
38-13
:count24

Shells rise from high behind the roof: from the lawn the launch point hides behind the gazebo, rockets crest the ridge, and every burst opens in the clear sky above the finial's balloon bouquet rather than behind the cap.

destructuring-bind
xyz
getfanchors:roof-apex
declare
ignorey
add-birthday-fireworkssession:origin
list
+x3.0
24.0
+z20.0
session
defunfilm-birthday-party
pathname&keynameage
seconds12
frame-rate30
width1280
sweep0.9
lift2.5

Film the birthday party into an MP4 at pathname and return it.

A hidden party session plays for seconds of real time while the camera swings sweep radians around the gazebo from the spawn vantage, rising lift cells and tilting up toward the bursts -- the fireworks open in the sky above the marquee, just over the spawn framing's top edge -- so the marquee, balloons, gnomes, and fireworks all pass through frame at their true animation speed.

let
session
celebrate-birthday:namename:ageage:providerprovider:visible-pnil:widthwidth:heightheight:frames-per-secondnil:register-pnil
unwind-protect
let*
camera
luvcraft:luvcraft-session-camerasession
radius23.0d0
base-y
frame-count
max1
round
*secondsframe-rate
luvcraft:film-luvcraft-sessionsessionpathname:secondsseconds:frame-rateframe-rate:before-frame
lambda
let*
progress
/frame-count
angle
*sweep
-progress0.5d0
setf
+base-y
*liftprogress
angle
+0.08d0
*0.22d0progress
defunadd-afterparty-turtles
session

Pose turtles on the lawn: guests who stayed for the morning.

let
population
luvcraft::luvcraft-session-critterssession
loopfor
xzyawgaitresting-pseed
in'
12.3d01.8d02.0d01.2d0nil21
11.6d0-2.2d05.1d00d0t22
3.5d04.5d00.8d00d0t23
-2.0d06.5d02.5d02.6d0nil24
-6.0d0-3.0d04.2d00d0t25
do
luvcraft::add-critterpopulation
luvcraft::make-gazetteer-turtlex7.0d0zyawgait:resting-presting-p:seedseed
defunfilm-birthday-cutscenes
pathname&keynameage
frame-rate20
width1280
evening-hour18.0
morning-hour7.2

Film the party as two acts cut together in one MP4.

The evening act, under evening-hour dusk where the marquee burns brightest: an establishing sweep of the gazebo and a fireworks crescendo. Then the morning-hour afterparty: telephoto gnome portraits with the focus-plane blur engaged -- the centre ray rests on the lawn at the gnome's distance, so the gnome stays sharp while the gazebo melts behind it -- and a golden wide over the lawn where the turtles stayed.

let
session
celebrate-birthday:namename:ageage:providerprovider:visible-pnil:widthwidth:heightheight:frames-per-secondnil:register-pnil:sky-hourevening-hour

A cutscene is cinema, not play: no hotbar, no inventory, no crosshair.

dolist
overlay
luvcraft::luvcraft-session-overlayssession
unwind-protect
let*
camera
luvcraft:luvcraft-session-camerasession
clock
luvcraft:luvcraft-session-sky-clocksession
shots

The morning sun stands due east (+x); the afterparty wide runs the west side looking sunward for gold and shafts, the first gnome is front-lit with the sun at the camera's back, and the second is backlit into the glow.

flet
ease
s
*ss
-3d0
*2d0s
list

Evening: the marquee in lights over the lawn.

list9d0evening-hournil
lambda
s
let
angle
+-0.35d0
*0.45d0
eases
values
+10.8d0
*1.2d0s
0d012d00d0wide

Evening: the fireworks crescendo over the finial.

list7d0evening-hournil
lambda
s
let
angle
+0.1d0
*0.2d0s
values
+12d0
*1.5d0s
0d0
+15d0
*7d0s
0d0wide

Morning afterparty: a gnome in the early light, telephoto, zooming in; the gazebo melts behind it.

list7d0morning-hourt
lambda
s
values17.8d07.9d0
+-1.2d0
*2.4d0
eases
10.5d07.0d00d0
-0.45d0
*0.13d0
eases

Its neighbour across the ring, backlit into the sun.

list6d0morning-hourt
lambda
s
values-17.8d07.8d0
-1.2d0
*2.2d0
eases
-10.5d07.0d00d0
-0.4d0
*0.1d0
eases

Morning: golden wide over the lawn and its turtles.

list7d0morning-hournil
lambda
s
let
angle
-1.15d0
*0.4d0
eases
values
+10.5d0s
0d010.5d00d0wide
seconds
reduce#'+shots:key#'first
luvcraft:film-luvcraft-sessionsessionpathname:secondsseconds:frame-rateframe-rate:before-frame
lambda
let
time
/frame-rate
loopfor
lengthhourfocus-ppose
inshotswhendo
multiple-value-bind
xyztxtytzfov
funcallpose
do
decftimelength

The party as a verb in the game itself: M-x Celebrate Birthday.

Commands run on the canvas thread, and celebrate-birthday tears that session down -- stop-playing waits for a frame boundary -- so like the quit action it runs beside the game, not inside it.

clim:define-command
com-celebrate-birthday:command-tableluvcraft.clim::luvcraft-world:name"Celebrate Birthday"
sb-thread:make-thread:name"birthday party"