my picotron drive, to be shared across my devices
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
picotron/atelier2.p64

1119 lines
34 KiB

picotron cartridge // www.picotron.net
version 2
:: gfx/
:: map/
:: sfx/
:: story.txt
--[[pod_format="raw",created="2024-03-30 15:58:22",modified="2024-04-11 01:41:32",revision=208]]
ATELIER HESTER
welcome to atelier phoebe 2! this is my document for
recording story ideas! please don't read this; it is
all the spoilers! if you want to know what happens
i recommend playing the game, that is the proper
way to experience this :)
--------------
main gameplay interface, you're shown a gridd of
cubbiess which contain various ingredients
you look at the things in the cubbies and label them?
labeling could be hard with just o/x
you have twwo books, one of recipes and one of
ingredients?
drag a label from the book to the drawer/plant?
--------------
plot ddetails follow!!!
main character amnesia from alchemy gone wrong.
main character badly in debt to loan shark
everyone in town has some debt and don't talk about it
most people don't like main character
mc has to mend relationships with everyone in order
to gather everyone together to defeat loan shark
player controls a fairy assistant for alchemy
possibly fairy is new to the atelier?
early dialog esstablishes it's common knowledge that
fairies can blink white (o) to confirm things or
red (x) to be uncertain / ask a question
maybe other colors at other timess but you get it,
those are our two verbs - ask about something and
suggest something
interface is an apothecary cabinet you can open
the drawerss of and ask about. you have to suggest
ingredients to mix together for the cauldron
at the beginning MC is unsure and doesn't know about
your suggestions - "i guess i can look that up?",
"maybe i'll try this?" because the player is guiding
them. towards the end MC responds as though teaching
the player fairy, like "that's right, that'll work!"
and "good job, yeah!" to communicate that the MC
is more confident and getting their mlemory of
alchemy back
the town generally ddoesn't like the alchemisst just
because she's been kindda nasty and rude at times,
just petty disagreements that have boiled over.
she's known to be a decent alchemist though i guesss
endgame is that you rally the town together to break
free of the loan shark; it's hard too convince
townsfolk both because they don't like you at first
and also because the loans ARE legitimate. but it
turnss out that the rest of the town also has debt
to the shark and if you convince everyone you can both
1. achieve independence from the supplies he sells
(maybe he's the intermediary with the trader? or buys
and ressells everything at a markup?)
2. convince people to just reject the loanss/debt;
quesstion here of whether they do this by force,
whether cops exist, idk, this is explicitly passtoral
fantasy. maybe at the end the player will muse about
whether it's good to have mob rule, "no one in toown
liked me, what if they threatened to burn down my
shop?" "yeah, that would suck. maybe we could build
something. but right now rule by mob is better than
rule by loan shark!" idk this feels overly didactic
but you get the idea
this is really getting into the weeds of how we view
the justice system but maybe he threatens to say
"i'll send for the king's knights!" and the crowd is
like "you can do that if you want, but by the time
they get here your house will be burned down.
alternately, you can keep your house and try to
be friendly. maybe even contribute to the town. it's
up to you. burn down your storehouse or genuinely
connect with people?"
definitely overly preachy but i like that
------------------
script!!!!!!!!!!!!
------------------
title screen
new game or load?
new game ->
hester with her back to cauldrron holding a little fairy doll in pieces
fairy shakes and then comes to life
hester: "ahhh, the synthesis wworked! ... i think"
hester: "a fairy like you should be a good help around the shop."
hester: "you might noot be able to speak but you can flash white for yes and red for not sure"
the whole time there's more and more bubbling in the cauldron
hester: "you can be my hands across the atelier!"
explosion!
soot-covered hester stands up
"what just happened."
"wait a second. who am i."
"alchemy books? a cauldron?"
outside view of shop, Atelier Hester on the sign
"Hester..."
back inside
"i must be an alchemist..."
loan shark arrives
"hester! you got the latest payment for me, girlie?"
"i'm afraid i don't knwo who i am - can you help me?"
"nice try. remember you owe me 4000Z by the end of the month. have a nice day!!!"
loan shark leaves
(SAVE POINT)
"am i... in debt?"
"i don't know hwo to do alchemy... but maybe these books..."
(player opens books)
"it looks like this one has recipes... this one has infoormation about ingredients... my head..."
(when quit uot of both books, old man comes in)
"hey, hester. can i get that purified water?"
"i don't knwo who i am"
"sorry, i can't hear so good these days - purified water? my usual order?"
(moves on when you've crafted purified water)
"thank you, hester. this will help me out. here's your payment."
(old man leaves)
"i guess i'll just try to... be an alchemist and pay off my debts..."
(SAVE POINT)
at the beginning MC is unsure and doesn't know about
your suggestions - "i guess i can look that up?",
"maybe i'll try this?" because the player is guiding
them. towards the end MC responds as though teaching
the player fairy, like "that's right, that'll work!"
and "good job, yeah!" to communicate that the MC
is more confident and getting their mlemory of
alchemy back
the town generally ddoesn't like the alchemisst just
because she's been kindda nasty and rude at times,
just petty disagreements that have boiled over.
she's known to be a decent alchemist though i guesss
endgame is that you rally the town together to break
free of the loan shark; it's hard too convince
townsfolk both because they don't like you at first
and also because the loans ARE legitimate. but it
turnss out that the rest of the town also has debt
to the shark and if you convince everyone you can both
1. achieve independence from the supplies he sells
(maybe he's the intermediary with the trader? or buys
and ressells everything at a markup?)
2. convince people to just reject the loanss/debt;
quesstion here of whether they do this by force,
whether cops exist, idk, this is explicitly passtoral
fantasy. maybe at the end the player will muse about
whether it's good to have mob rule, "no one in toown
liked me, what if they threatened to burn down my
shop?" "yeah, that would suck. maybe we could build
something. but right now rule by mob is better than
rule by loan shark!" idk this feels overly didactic
but you get the idea
this is really getting into the weeds of how we view
the justice system but maybe he threatens to say
"i'll send for the king's knights!" and the crowd is
like "you can do that if you want, but by the time
they get here your house will be burned down.
alternately, you can keep your house and try to
be friendly. maybe even contribute to the town. it's
up to you. burn down your storehouse or genuinely
connect with people?"
definitely overly preachy but i like that
:: art.lua
--[[pod_format="raw",created="2024-03-31 01:52:08",modified="2024-04-12 14:41:51",revision=629]]
function bob(s, x, y, w, h, t)
sspr(s, 0, 0, w, h, x+t/2, y-t/2, w-t, h+t)
end
function ragged_box(x1, y1, x2, y2)
ragged_box_color(x1, y1, x2, y2, 15)
end
function ragged_box_color(x1, y1, x2, y2, bgcolor)
rectfill(x1+1, y1+1, x2, y2, bgcolor)
sspr(004, 0,0, 8,2, x1+1, y1, x2-x1, 2)
sspr(004, 0,0, 8,2, x1+1, y2, x2-x1, 2)
sspr(004, 0,0, 2,4, x1, y1, 2, y2-y1)
sspr(004, 0,0, 2,4, x2, y1+2, 2, y2-y1)
end
function easeTrig(t)
if t<0 then return 0 end
if t>1 then return 1 end
return 0.5-0.5*cos(t/2)
end
function easeAccelerate(t)
if t<0 then return 0 end
if t>1 then return 1 end
return t*t
end
function easeDecelerate(t)
if t<0 then return 0 end
if t>1 then return 1 end
return 1-(1-t)*(1-t)
end
function easeSine(t)
if t<0 then return 0 end
if t>1 then return 1 end
return sin(t/4)
end
-- add newlines to a string to fit it into lines.
function wrap(str, line_length)
-- thanks to https://stackoverflow.com/questions/17586/best-word-wrap-algorithm, i did not feel like thinking this out myself
local words = split(str, " ", false)
local output = ""
local c = 0
for w in all(words) do
while sub(w, 1, 1) == "\n" do
output = output .. "\n"
c = 0
w = sub(w, 2)
end
if c + print(w,0,-10) > line_length then
if c > 0 then
output = output .. "\n"
c = 0
end
end
output = output .. w .. " "
c += print(w,0,-10) + print(" ", 0, -10)
end
return output
end
function collides(position, object)
return position.x < object.right and position.x > object.left and position.y > object.top and position.y < object.bottom
end
:: cabinet.lua
--[[pod_format="raw",created="2024-04-03 19:46:51",modified="2024-04-10 01:58:59",revision=627]]
Cabinet = {}
function Cabinet:new(x, y, gaps)
local c = {
x=40,
y=10,
drawers = {},
shelves = {},
hover_plant = nil,
height = 4*(gaps+16)
}
-- populate the big drawers
local xoff = x
for i=0,0 do
for j=0,1 do
add(c.drawers,
Drawer:new(xoff + gaps + i*(gaps+32), y + gaps + j*(gaps+32), 009, 32))
end
end
xoff += gaps + (gaps + 32)
-- populate ssmall drawers
for i=0,3 do
for j=0,3 do
add(c.drawers,
Drawer:new(xoff + gaps + i*(gaps+16), y + gaps + j*(gaps+16), 008, 16))
end
end
xoff += gaps + 4*(gaps+16)
local s = Shelf:new(xoff+gaps, y + gaps, 76, 32)
add(s.plants, {13, 4})
add(s.plants, {14, 14})
add(c.shelves, s)
local s = Shelf:new(xoff+gaps, y + 3*gaps + 32, 76, 32)
add(s.plants, {15, 10})
add(c.shelves, s)
setmetatable(c, self)
self.__index = self
return c
end
function Cabinet:draw()
-- cabinet!
rectfill(self.x, self.y, 230, self.y + self.height, 2)
-- drawers
for a in all(self.drawers) do
a:draw()
end
-- plant shelves
for s in all(self.shelves) do s:draw(self.hover_plant) end
end
function Cabinet:update(x,y)
for d in all(self.drawers) do
if d:contains(x,y) then
d.offset = 4
else
d.offset = 0
end
end
local closest_distance = 1000
local closest_plant = nil
for s in all(self.shelves) do
for p in all(s.plants) do
local dist = abs(p[2] + s.x - x+16) + abs(s.y + 24 - y)
if dist < closest_distance then
closest_distance = dist
closest_plant = p
end
end
end
if closest_distance < 32 then self.hover_plant = closest_plant
else self.hover_plant = nil end
end
function Cabinet:drawer_at(x, y)
for d in all(self.drawers) do
if d:contains(x,y) then
return d
end
end
return nil
end
Drawer = {}
function Drawer:new(x, y, sprite, size)
local c = {x=x, y=y, sprite=sprite, size=size, offset=0}
setmetatable(c, self)
self.__index = self
return c
end
function Drawer:draw()
rectfill(self.x+2, self.y+2, self.x+self.size-3, self.y+self.size-3, 0)
spr(self.sprite, self.x, self.y+self.offset)
end
function Drawer:contains(x, y)
return x >= self.x and
x <= self.x + self.size and
y >= self.y and
y <= self.y + self.size
end
Shelf = {}
function Shelf:new(x, y, width, height)
local c = {x=x, y=y, width=width, height=height, plants={}}
setmetatable(c, self)
self.__index = self
return c
end
function Shelf:draw(highlighted)
rectfill(self.x, self.y, self.x + self.width, self.y + self.height, 0)
for p in all(self.plants) do
if highlighted ~= p then
palt(0, true)
palt(10, true)
end
spr(p[1], self.x+p[2], self.y + self.height - 32+2)
palt(10, false)
palt(0, true)
end
end
:: conversation.lua
--[[pod_format="raw",created="2024-04-03 02:38:22",modified="2024-04-10 14:42:28",revision=642]]
default_script = {
{"welcome to Atelier Hester!", talk_sprite}
}
Conversation = {}
function Conversation:new(script, final)
local c = {script=script or default_script, final=final or function() end, index=1}
setmetatable(c, self)
self.__index = self
return c
end
function Conversation:before()
self.behind = scene
self.before = function() end -- only ddo othis the firstt time this scene is played
end
function Conversation:update()
local now = self.script[self.index]
if now.during ~= nil then now.during() end
if btnp(4) or btnp(5) then
if now.after ~= nil then now.after() end
if self.index < #self.script then
self.index += 1
else
--self:after()
end
end
end
function Conversation:draw()
self.behind:draw()
ragged_box(30, 100, 240-30, 130)
color(0)
print(self.script[self.index][1], 34, 104)
end
function Conversation:after()
self.final()
--change_scene(self.behind)
end
:: fairy.lua
--[[pod_format="raw",created="2024-04-03 17:32:32",modified="2024-04-10 01:58:59",revision=384]]
Fairy = {
velocity = 1,
accel_frames = 20,
min_velocity = 0,
max_velocity = 3,
f = false
}
function Fairy:new(ff)
local f = ff or {x=120, y=68}
setmetatable(f, self)
self.__index = self
return f
end
function Fairy:draw()
if flr(30*t()) % 2 == 0 then
spr(5, self.x-8, self.y-8, self.f)
else
spr(6, self.x-8, self.y-8, self.f)
end
end
function Fairy:move()
if self.velocity < self.max_velocity then
self.velocity += (self.max_velocity - self.min_velocity) / self.accel_frames
end
if btn(0) and self.x > 0 then self.x -= self.velocity end
if btn(1) and self.x < 240 then self.x += self.velocity end
if btn(2) and self.y > 0 then self.y -= self.velocity end
if btn(3) and self.y < 135 then self.y += self.velocity end
if not (btn(0) or btn(1) or btn(2) or btn(3)) then
self.velocity = self.min_velocity
end
if btn(1) then self.f = false end
if btn(0) then self.f = true end
end
Glow = {}
function Glow:new(x,y)
local g = {x=x, y=y, t=0, length=0.7, radius=10, start_color=8, end_color=9}
setmetatable(g, self)
self.__index = self
return g
end
function Glow:draw()
for i=0,4 do
local eff_radius = self.t*self.radius / self.length
if eff_radius - i >= 0 then
local c = self.start_color
if i < (self.t/self.length)*4 then
c = self.end_color
end
circ(self.x, self.y, eff_radius-i, c)
end
end
end
function Glow:update()
self.t += 1/30.0
return self.t < self.length
end
:: gfx/0.gfx
b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTAzLTI5IDAxOjE0OjUxIixtb2RpZmllZD0iMjAyNC0w
NC0xMiAxNDo0MTo1MSIscmV2aXNpb249MTA1MF1dbHo0AJQUAADWVQAA8Rp7WzBdPXtibXA9cHh1
AEMgEBAE8PAsZmxhZ3M9MCxwYW5feD0wLjM3NQwA_wJ5PS0wLjEyNSx6b29tPTV9LDoAkCR6cAoP
HU8eCgcAcD8ODx4KYBoJAP8CDRpQCh8dPw4dClAKDx1fDg0IAAT-Aw8eClAKDl8ODgpQCn4KUJrw
A4sAJqUGOrAKHxMKsAoeBADwAKAaHhqQCgMOEQqQCgMPEgcAPQ4dAQYArw4KkAo_CpBa8AV9ACYQ
Jn0AER1xAP8GYDoPFy46MAo9HxweHxIKMAodbxwPCAACIF8cIAD-ARodPxwODxIaQBpeGmB68ASE
ACJRCAgECQACABIQCAAfGQ4AFh8J9QAm-yAQLxyQPgAuUC4AHiAuEC4gLjAOEA4wHgBeEF5ALBos
YDwaPFAcEAogHIAa0ArwOWgAJccgLDAsUAwADDAMAAwIADAcEBwIABEQAgD-EVAcAAwQDAAcYCwQ
LIAcGhyQHxwaHpAeCgAegB4aEB6QfwAaDx4DNVD-FAHUHgIAsAS5BB4ECT8PGT0JCQAanQYAMQ8V
DggAhwwOBLkEDA7UAwAf7HsAIp8gIAT-FBH0Dh4DAALANPkGNB40CX8POX0JCQAxjRmNCAAv-QQH
AAsyDxUOCQAeDAgAf-kGNAwO9A4EABI-HvwO1gAlctQPFPAB1A4EAP8SxB7wAcQO8AK0HvACpB7w
A5Qe8AR0LvAFVC7wB27w--_rZgAl-wn5MCD6CoD5BND6APAB2fAC2fADufD---xQACVf8P----M9
ACfwCKIq8A0KAwrwDBoDCvAISgMACvAICgMgCAAjBToIAFAKAxADAAIAZBrwBAoAAwIAHwoOAApE
AToAAwIA8A5KwAr-EgEKwCrOKuAKzgrwARquGvACCq4K8AMajlUAb44K8AWqoMYAJ8AAOvAMCh8T
GvAIOi7BAPAiHgAuOvAFCi4AThrwAiouJD4K8AIKFA4ADgQQHgAa4DoALgAEDxQzCvAACi4AHiQN
PgoAUQAuEAMkCgDgICQQFBAq8AAKHhQANCOwAKBeJBAa8AEKfhQeSgAhLkAIANIaYBQQSuAK7xgK
4ArsBAChGswa8AAKzArwAQUAfxqsGvACypDZACfwDl1K8AZaLxMK8AYKTgQeCvAGGh4TBBMq8AUK
IA5DOgHwByAeBD4a8AMKLhAELgAOCvADCkMkEwCdABAwugBAAgoOQ7sAsAIKDgMODxQuFA4AdQEw
EB0QwgDTBAogDRAUEDrwAQrGCgUAIBqmLgAQpiYAEIZhABCGAQIfhqABJ-1iYGAE-x4P-xUS-x4j
-xUo-x4R-xU2-x4G-xU_3x7-FUZ-Hv8VSj8e-xUM8Bj_Cg8e-gHwMP4FBT7wPD4FnhUO8EAOFZ51
8DZ1rvUE8Bz1BM71Qv4A9T7_BPU6-gv1MP4c9Rj_Ch3_Th3_Ti3_TE3_Sl0DAE9N-kw9AwAEES05
AAUDADAN-v8BACQ1DREABVkABTIAAmIAQW3_SH0DAPtYjf5Gnf5Grf5Ezf5C3f5C7f5A-QH_Pv0C
-j79A-48-QX_Ov0H-jj9Cf42-Qv_NP0N-jL9D-4w-RH_Lv0T-iz9Ff4q-Rj_Jv0b-iT9Hv4g-SH_
Hv0k-hr9Kf4U-S7_EP00-gj9Pd79GXoBdC01LjE1ODMBAASEAVcxNC44MBcAAY4BGTOOAfMgQEAE
8P8HjxjwJt7wIf4D8B3_CPAY-gvwFP4Q8A-_FfAL-hnwBf4b8AT_HPAE-hsMACIbBwUAwAP_Gxfw
Av4cBwbwAQYAMBcO4AcA8QIHLsD_HQYHBi7A-hwGBwY_sBUADw8ACKAGBwYHPqBOV-4SCgByLqBO
Fnf_DhUA8wU_JzZH-gwGFwYHLrAuFxY3Nhf_CxoA8AE3Njc2-ggGBwYXLrA_Jkc2HAAQASoAID6w
NgBBNjc27ioAQcAuJkcPADFXBi4uAELQPhc2EgAAiAARFyUAdNBORzZHRjcUAFA_0H5XRiIAEhci
AEE_8AG_MwARFhEAQD7wAu4uAPAIJwYHBic_8AbuVzY3JhcGJy7wDO5HNjcUAEEQzmcmKgDwABW_
AFcGFwA_8BquADdO8NECUCLO8CdeqQUbhskBNjIuOEMDETkNAAFDAx00tQHRYl8Y8B-_APAV-gvw
D60BURHwDv4RCAACEAAABAAQEhQAtBfwC-4TZ-AF-hN3BQBjEofwBP4TBQCQA-4Uh-AC-hSHxgEh
FXfGASEUh8YBIhSHqQEiFHeqAQQQACIThxAAIhOXEAAhDtcQAFB_d473BZwBNE73GMoBI-cYxwEl
9xTEASb3DcMBNhb3DMEBJvcGvgE4FvcBvwEp9wK8ARrXtgECxgEPuAGQ9Cz-Fl8b8CXO8CD_AfAb
-gbwFv4L8BD_EfAN-hTwBP4e8AH_IOD_IsD_JaD_JpD_J3D_KWD_KlAOCv4pQAUAsiowDgoOCv4j
Hw8uCgDwKB99QC4K-hwtJx0nUB4K-hktJy03HUAeCv4VPSctNz1QPgr_EC03LTdNJ1AuCv4MPTct
J10nHQ4NADAJLUcNADAtBy5DAEAFPTc9DgD0AF5gHgr_Ai1HLTddFz1_cAsAci2ukE4Kbj0MAHCe
4D4KTh1XVgByLW7wBT4KTkoAc37wCC4KThcMAFAMHgpOHQsA8QaO8A5_F10XPY7wEm5NFy0HjvAW
Xh0IAPAEGl4HHRd_8B3_AfAgzvAknvAoLmMBL-_hZAEmshQfG-ArXvAnnvAkaQG1HP4F8Bj_CfAU
-g1tAdIJ-hfwB-4a8AX_HPADeQHwA-AA-iHg-iOw-iQPD6D_JAcdgF0BgVcNcA4K-h1XDgBhGlct
Fx1gdgFSFkctVw0LAFAURx1HbUEBUxI3LTcdhwF0ECctJw0HDYwBRQ8XHRePAUYKFy0HkgFHCS0X
HZQBWAcdFy0nlgFHHQctF5gBRQ0nHReaASANJz0ABJwBVi0XLScNngE1Jx13ngE1Fy1XngEfTZ0B
ZA-gDBoPOgD--------------------------------------------------6TwI2CABPD-JL8U
8ED_AvA8-gbwOT8V-gTwNz3_BfA1Tf4H8DNN-gjwMk3_CvAwTf4L8C9dBQD4AE3_DPAuXf4N8C1N
-g3wLgUAMwzwLwUAMQsPDwcAEDwmAPAMCTzwLBxN-gg88C4cPf4HLA8X8DFN-gULHAsMCQDwrAQM
CwwLDPAzTb4tLg8ZHAsc8DNdjj0uGjzwNV1ObR4qPPA2-QFKPPA3zXos8DkKTdoc8Dv6AwzwO5oM
ivA7amx68DpafHrwOVofHAxpavA4WqlaOfA0WslKOfA0SgDpKknwMyogmS8MKQpZ8DQKSEn4BPAw
_BHwLmgPCvgM8CtoFygn_AfwKfgDNzgXqPAoJ-gMB7jwIgf4EieI8CH4FgeI8CD4GAd48CD4GSc4
8B-4IfAf_CLwHfggBxgGAPEHFxjwHPghByjwG-gl8Bv4JvAaKAn4IgYA8CMOCfgE8BoYCfgOGSgJ
_ADwGRgJ_A8ZGAmYB1jwGAgZ_BAJKAmIB1jwGQn4ERkoCRgJWBkAVAn4Eik4CwDxAhQZGBlYF1jw
Fwn4FRkIGWgHCwCBGBno8BYJ_Bk_AGQUGfgVGRgLAFAUCTgZ6AkAEBpyAIITGfgaGYgXOAkARvgA
8BIIABEbiACBEQn4HAmoB0gJACQZmAkAsPgB8BAZ_BspqAc4CQCmHBm4BzjwDwn4HQkAocgHGPAQ
CfgcKdhOACApyGUAMBOpuH0A8AEPSXgZqPAVGfgOGbgZiPAXQgDyAQggGPAaKfgbGQjwHyn4HAkH
ABAZBgDwIxo5CPAeKfgYWQjwHxn4F2kt8B0Z_BRpCAlN8BwZ_BF5CG3wHgn4DpltFfAeGfgKmY0l
CABQBqkYbVUJAPJCArkYfYXwHhnIyQiNxfAeCZi5zdXwHwlYqd31AvAfyQjN9QUABfAemRWN9Q3w
Hmmt9Q8ABfAdDRm99RXwG931GPAZjQUN9RzwGS31JPAZPQUNAgCi9RzwGC31JvAXLRAAQwUN9R0S
AAYkAKQe8BU99SjwFC0VEgCGIPAULfUq8BIkAIUi8BIt9SzwEBIAARQAhRAt9SwABfAPXgAAFgCJ
BvAPPfUqABUXAANXAIsPLfUtAAXwDO0Dbi0yOS4xMC4xXDM4Ljc1LjEeMQIE8S3--8NvFPBF7vA8
PxV_KV7wOF2OKV7wNz0OSW4JbvA2PQ5Z7vA1PQ5pbgpu8DQdPmnu8DQNTln_APA0bkkGAPCEKW6v
GfA03tzwNBmO-AHwNBlu-AMQHw-wMBle-AMQG-AxGV78AgsAK-Axbpzb8DFujNvwMl6Mmx8fK-Ay
XnyLKDvwMl5sSwgbKCcb8DNeXCs4Sx8OBwvwNF4sHxcbKEsIGx8OK-AzHnsfFxgLFw8OKxhb8DMO
mw8XCycfDjsYS-AzHksfF0sXHw5LCEvwMz4rLxe7FgDwJw1ODAsWuwhb8DM9HizrGCvwMz0OHAAs
qyg78DMtDmyLKEsfHPA1jGsYWwYPDB8c8DSMAMsGLwoA8kkyrKsWBT8MFfAxDAB8qwYbRCXwMJzb
RBUU8C_sy0QllPAnjEWLFSRFpPAkjAU0FXslBAX0AfAifBVEJXsl9AEV8CB8BYQFayX0AwXwIGwF
lAVrGvQF8CBcCgDxQAbwH1wFlBVrCgX0BvAeTAAFlBVbBAoF9AfwHiwAJXQFBBU7FBr0B-AhFQQV
ZAUUBUoFGqQFtPAfFQoUJUQFFBpUCqQFxPAcFQQKRCUkBaQOAPAGGhUECnQlBAW0CpQF1PAYFcQl
xAqECgBQBQQKxBUMAPAK5PAWBQQK9BcF9ADwFQUK9BgV9ADwExX0GggAYBIF9BIKhAoA8wARBfQT
CnQABfQC8A8F9BQLAHAOBfQVCmQQCwAQDQsAEXQLAEEMBfQWIQA0A-ALCwChBPAKBfQfABX0AwkA
EBAdADIK9CAIAFDUFfQQEBsA8gEIBeQF9AgKZCAV9ALwCPQADQAyJfQBDQCFCQpEEBUUJeQOAEEA
FUQFDgBgFfQICkQAOQEwCPQBGwAfNA0AAsEV9A0ABfQF8Aj0AwULADME8AkLAFAEKtTwCwwAgAwg
GgQatPAMDADACAokEAo0GpTwDfQCYwCgJAAKVAqE8A70AlcAAA4AQBpk8A8OAJIKCiQVVApU8BAN
AGE0BVQaNAUOAPEEBHX0AQo0BWQKJAXwEeRVZAX0AA8AgRQF8BL0DQXkDQBwBAXwEwX0DBwAsyQF
ZAoF8BQF9AsVDgDB8BUF9Awa5AokBQpUDQBwCzr0AnrwF2kAYBRaROvwGAoAUmQaFOsWCwCAdAoE
2wYr8BmlAFSECusWGxcAQcsGKwYNAFEJCnQK2w0AEhclAAANADEL8BgMAIRrPwU7BhsGCykAMUtj
Kw8AAVAARFQaK4MPAFHUtVrjGwwAYhoFZHXzDhgA8hIbZW3zDjsGCwMb8BqNAw3zEisGC0PwGS3z
HAtj8Bk9Aw0CABDzRgVS8yXwGC0QAIADDfMe8Bct80YFAhAAARIAMBU980YFFBMSAABGBRDzRgUC
EAAwAw3zRgUQ80YFJwMNFABlEC3zLvAPXACfAw3zJPAOPfMuJwUsD50JJw_FCv8gIP8UgjHxAXz0
P-4BBP8VPQT_AQT9PA4HACA7HgcAQS30Ny4IAE8E-jUECgD-NARSASH0OGIBMR30OnEBcg30Pf4B
9D8nOjH--_iUAR-9kwH-dh9cYgMUHzLlCQHyKxjfFvBALr8PHvA9Lu0e8Dse-QIO8Doe-QMe8DkO
-QMfEQ4N8DUcHs18DhzwMRwAHA7dDA0cDVzwMUwKAPYGHA0MDgzwMQwgHv0BDD0MDvA2Dv0IBgBg
LU4dTm0uDwCwBR4NDvA3Dl0O-QAIAEBNHv0BIQBRPR49Ds0qAFAeTQ69HgkAUA5dDp0KLQDwDR4d
Pi0OjQou8DgOTU6dCg7wOR79BA7wOg5NPq0HAPGALQ5tDm0e8DsOHR5NHl0_8DoeHX5NHh0u8Dk_
QH5NHvBBHs0ODQLwPx7NDxhi8DourQtyG-A5Dp0bYjvwNAs_nQtiGw0r8DICCw0enRtSGz0CC-Aw
Egu9G1IbXQIL8CwNCyIbrQtSG30CC-AqHQsyG30rQhudAgsC8CgdC1IrPRtiC70bAvAnHQtyW2Ib
zQsLAPIA8gQb3RvwJi0L8gMb7QILCgCwC-0AG-AlPQvyAhsKAPAEAggX8BA9C-IBG-0CC-ACCDfw
Dg4A8QAL-QML8AMIN-ANPQvyABsOAEAYR-ALDgBQC-0CDg0eAGAHGDfwCU0SADFNDr0TAEwYBxgn
EwBIAFfwCBIAYQQIEFfwBxIAIz0eEgByIEcALxXwAxUAIg7NFQA5MEcpEwCwAvADCDA3KQ3wAl2U
ABUtFQBwQAdJHfABXbcAUB0ezQ4bPABBWa0gvREAIQ7dEQBJIEn9DBAAMCn9DhAA0hIO3Q4CC-AK
-Q8L8gQMADoL-Q4MABEFCwBUDI0g-QENAPQKDW3wBBvyBc0eAgvwDk3wBQvyBs0OEgvwKAkAcB0O
vfIFvR4KAPACXhBObcLNDiIL8CANAG69ct0LAPEMGmVuC-0RHiIL8BYVDkVOEC4L-REOMgvwFYWe
CgDwTfAWhU4AHhAL-RAeIvAXhW4tC-0PHjIAC-AV1R0Q-REOQivwFLVgC-0NLjJL8BNlECVgCxL9
CS6SK-AShYALMv0EPtIb8BFVsAti3T7yAhvwEFWwC-ICPvIGK-AOCwAiHRsIAEAeG-ANCAAgHwsI
AKbyIBvwDFWg8iILBwBgsPIhG-ALJABnIBvwC2WgCAA-C-AMCAABAEwAAAgAYR4L8A9VoGwAAQgA
EByHAPAAoAvyGxvwEUXAC-IZG-ASCABAGBvwEwgAQBcb8BQIAEEVK-AVCAA-C-AXCAANITXQCABv
FkXgC-IUCAAUIxMbCABPCxLwFQkADR8bRAAAYvAA8hTwFwcAHwPyBBUPyAgnDzoA------------
---3UG09NX19
:: gfx/.info.pod
b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTAzLTI5IDAxOjE0OjUxIixzdG9yZWQ9IjIwMjQtMDMt
MjkgMDE6MTQ6NTEiXV1sejQABAAAAAMAAAAwbmls
:: library.lua
--[[pod_format="raw",created="2024-04-10 00:24:20",modified="2024-04-10 01:58:59",revision=35]]
plant_descriptions = {
[-1]={{"i don't know what that is. where did you find this?"}},
[14]={{"this is foxglove"}},
}
function get_conversation(plant)
local id = plant[1]
return plant_descriptions[id] or plant_descriptions[-1]
end
:: main.lua
--[[pod_format="raw",created="2024-04-02 02:59:37",modified="2024-04-12 14:41:51",revision=951]]
-- this is atelier hester!
-- except for the one line in _init that does change_screen(shop_screen)
-- this is just a file that sets up the framework we're using:
-- change_scene to change the displayed sscene
-- _update and _draw to run the current scene
-- and some stuff for setting animations.
include("art.lua")
include("script.lua")
include("s_title.lua")
include("s_shop.lua")
include("s_conversation.lua")
include("s_drawer.lua")
DEBUG = true
scene = {
before=function() end,
draw=function() end,
update=function() end
}
previous_scene = scene
animations = {}
script = {}
function _init()
vid(3)
change_scene(title_screen)
end
function _draw()
scene:draw()
for anim in all(animations) do
if anim.draw ~= nil then anim:draw() end
end
if DEBUG then
rectfill(0,0,90,10, 7)
print("animations: "..#animations, 0,0,0)
rectfill(0,10,90,20,7)
print("script nodes: "..#script, 0,10, 0)
end
end
function _update()
if #script == 0 or not script[1].blocking then
scene:update()
end
local new_animations = {}
for anim in all(animations) do
local result = anim:update()
if result then
add(new_animations, anim)
end
end
animations = new_animations
if #script > 0 then
local result = script[1]:update()
if not result then
deli(script, 1)
end
end
end
function change_scene(new_scene)
if new_scene.before ~= nil then
new_scene:before()
end
scene = new_scene
end
function play_script(s)
for action in all(s) do
add(script, action)
end
end
:: map/0.map
b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTAzLTI5IDAxOjE0OjUxIixtb2RpZmllZD0iMjAyNC0w
NC0wNSAxMzo1NDowNiIscmV2aXNpb249NjgzXV1sejQAaAAAAFgQAADwCHt7Ym1wPXVzZXJkYXRh
KCJpMTYiLDMyAwAvIjABAP--------------------vxCCIpLGhpZGRlbj1mYWxzZSxwYW5feD0w
CADSeT0wLHRpbGVfaD0xNgoAEHcKAIB6b29tPTF9fQ==
:: map/.info.pod
b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTAzLTI5IDAxOjE0OjUxIixzdG9yZWQ9IjIwMjQtMDMt
MjkgMDE6MTQ6NTEiXV1sejQABAAAAAMAAAAwbmls
:: s_conversation.lua
--[[pod_format="raw",created="2024-04-05 14:55:45",modified="2024-04-12 14:41:51",revision=118]]
default_script = {
{"welcome to Atelier Hester!", talk_sprite}
}
Conversation = {}
function Conversation:new(script, final)
local c = {script=script or default_script, final=final or function() end, index=1}
setmetatable(c, self)
self.__index = self
return c
end
function Conversation:before()
self.behind = scene
self.before = function() end -- only ddo othis the firstt time this scene is played
end
function Conversation:update()
local now = self.script[self.index]
if now.during ~= nil then now.during() end
if btnp(4) or btnp(5) then
if now.after ~= nil then now.after() end
if self.index < #self.script then
self.index += 1
else
self:after()
end
end
end
function Conversation:draw()
self.behind:draw()
ragged_box(30, 100, 240-30, 130)
color(0)
print(wrap(self.script[self.index][1], 240-68), 34, 104)
end
function Conversation:after()
self:final()
change_scene(self.behind)
end
:: s_drawer.lua
--[[pod_format="raw",created="2024-04-05 00:59:36",modified="2024-04-12 14:41:51",revision=273]]
DrawerScene = {
extension = 0,
behind = nil,
inset = 60,
}
function DrawerScene:new(drawer)
c = {extension=0, behind=nil, inset=60}
if drawer.size > 16 then c.inset=30 end
setmetatable(c, self)
self.__index = self
return c
end
function DrawerScene:before()
self.behind = scene
play_script({
Script.len(function(s)
self.extension = 115*easeDecelerate(s.timer/s.length)
end, 0.5)
})
self.before = function() end
end
function DrawerScene:update()
if btnp(5) then play_script({
Script.len(function(s)
self.extension = 115*(1-easeAccelerate(s.timer/s.length))
end, 0.5),
Script.once(function(s) change_scene(self.behind) end)
}) end
end
function DrawerScene:draw()
self.behind:draw()
draw_drawer(self.extension, self.inset)
end
function draw_drawer(distance, inset)
rectfill(inset, -1, 240-inset, distance, 20)
fillp(0b1010110110110101)
rectfill(inset, -1, inset+40, distance, 20 | 21<<8)
fillp()
for i=1,4 do
rect(inset-i, -i, 240-inset+i, distance+i, 4)
end
rectfill(inset-10, distance, 240-inset+10, distance+10, 4)
rect(inset-10, distance+10, 240-inset+10, distance+10, 20)
spr(10, inset-10-32, distance, true)
spr(10, 240-inset+10, distance)
rectfill(inset+10, distance+10+1, 240-inset-10, distance+10+2, 9)
spr(11, 120-16, distance+10+1)
end
:: s_shop.lua
--[[pod_format="raw",created="2024-04-05 14:55:27",modified="2024-04-12 14:41:51",revision=158]]
-- this manages the main shop interface, where you look at the cupboard and plantss
-- and sstuff.
include("art.lua")
include("fairy.lua")
include("cabinet.lua")
include("library.lua")
cabinet = Cabinet:new(40, 10, 2)
fairy = Fairy:new()
alchemist_sprite = 192
alchemist_bob = 0
customer_sprite = 0
customer_bob = 0
customer_visible = false
door_open = 0
shop_screen = {}
function shop_screen.draw()
cls()
cabinet:draw()
-- alchemist!
bob(alchemist_sprite, 0, 7, 96, 128, alchemist_bob)
palt(30, true)
palt(0, false)
spr(16, 240-110, 135-36)
palt(30, false)
palt(0, true)
-- countertop!
draw_counter(14)
local book1 = {left=10, right=74, top=100, bottom=164}
if collides(fairy, book1) then spr(18, book1.left, book1.top)
else spr(17, book1.left, book1.top) end
local book2 = {left=80, right=144, top=100, bottom=164}
if collides(fairy, book2) then spr(20, book2.left, book2.top)
else spr(19, book2.left, book2.top) end
if customer_visible then
bob(customer_sprite, 240-96, 7, 96, 128, customer_bob)
end
fairy:draw()
draw_door(door_open)
end
function draw_counter(height)
rectfill(0, 135-height, 240, 135, 4)
for i=1,height do
rectfill(0,135-height, 0.5*i*i, 135-i, 4+16)
rectfill(240,135-height, 240-0.5*i*i, 135-i, 4+16)
end
line(0,135-height, 240, 135-height, 4+16)
end
function shop_screen.update()
fairy:move()
cabinet:update(fairy.x, fairy.y)
if btnp(4) then
local glow = Glow:new(fairy.x, fairy.y)
glow.start_color = 7
glow.end_color = 12
add(animations, glow)
local drawer = cabinet:drawer_at(fairy.x, fairy.y)
if drawer then change_scene(DrawerScene:new(drawer)) end
end
if btnp(5) then
add(animations, Glow:new(fairy.x, fairy.y))
if cabinet.hover_plant ~= nil then
change_scene(Conversation:new(get_conversation(cabinet.hover_plant)))
end
end
end
function draw_door(open)
palt(0x00, true)
if open >=0.0 then fillp(0b1111111111111111) end
if open > 0.1 then fillp(0b1011010111100101) end
if open > 0.2 then fillp(0b1010010110100101) end
if open > 0.4 then fillp(0b1010000110100100) end
if open > 0.7 then fillp(0x0000) end
rectfill(240-(96*open), 135-128, 240, 135, 0x00)
sspr(200, 0,0,96,128, 240-(96*open), 135-128, 96*open, 128)
palt()
fillp()
end
:: s_title.lua
--[[pod_format="raw",created="2024-04-10 01:43:09",modified="2024-04-10 15:04:40",revision=101]]
title_screen = {
continue=false
}
function title_screen:update()
if btnp(2) or btnp(3) then self.continue = not self.continue end
if btnp(4) and not self.continue then
change_scene(shop_screen)
play_script(the_script)
end
end
function title_screen:draw()
cls()
print("Atelier Hester ", 120-(68/2), 50, 7)
print("Start", 120-25/2, 70, 7)
print("Continue", 120-39/2, 90, 7)
local y = 70
if self.continue then y=90 end
rect(120-50/2, y-5, 120+50/2-1, y+7+5, 7)
end
:: screen_drawer.lua
--[[pod_format="raw",created="2024-04-05 00:59:25",modified="2024-04-05 00:59:26",revision=1]]
:: script.lua
--[[pod_format="raw",created="2024-04-04 01:01:55",modified="2024-04-12 14:41:51",revision=538]]
Script = {}
-- sscriptanim generates animation functions and adds the boilerplate we want
-- can track time, but generally the ssystem we're working with is that
-- animations are objects which repeatedly get :update() called on them
-- they run at least once, every frame, *until they return false*.
-- so keep returning true to keep playing, rerturn false to end
-- the blocking argument determines whether update() is called on the underlying
-- scene, i think.
-- the ScriptAnim.len variant will take nil returnss as carte blanche to keep
-- running, checking for exactly the value false to cancel out.
function Script.len(func, len, blocking)
local b = blocking
if b == nil then b = true end
return {
timer = 0,
length=len,
blocking=b,
update=function(self)
-- we wrap the function we got passsed so that it'll update the timer
-- and end appropriately
self.timer += 1/60.0
if func(self) == false then return false end
return self.timer < self.length
end
}
end
-- ScriptAnim.once is cnofusingly named, and will call func(self) every frame
-- until it returns false.
function Script.once(func, blocking)
local b = blocking
if b == nil then b = true end
return {
timer=0,
blocking=b,
update=function(self)
self.timer += 1/60.0
return func(self)
end
}
end
-- stay in this script node until func(self) sets self.done to true
function Script.waitTilAfter(func, blocking)
local b = blocking
if b == nil then b = true end
return {
first=true,
done=false,
timer=0,
blocking=b,
update=function(self)
if self.first then
func(self)
self.first = not self.first
end
return not self.done
end
}
end
-- functions for making a cnoversation script line that bobs the alchemist or the
-- custmoer respectively
function A(arg)
arg.during=function() alchemist_bob=abs(5*sin(3*t())) end
arg.after=function() alchemist_bob=0 end
return arg
end
function B(arg)
arg.during=function() customer_bob=abs(5*sin(3*t())) end
arg.after=function() customer_bob=0 end
return arg
end
-- here's a sample script for a guy walking in and asking for flibbertygibbets
the_script = {
Script.len(function(s) -- open door
door_open = easeTrig(s.timer/s.length)
end, 0.5, false),
Script.once(function() -- person appearss
alchemist_sprite = 193
customer_visible = true
customer_sprite = 201
return false
end, false),
Script.len(function(s) -- door closes
door_open = easeTrig(1-(s.timer/s.length))
end, 0.5, false),
Script.waitTilAfter(function(s) -- cnoversation starts
change_scene(Conversation:new({
A{"Welcome to Atelier Hester!"},
B{"hey do yuo have flibbertygibbetss"},
A{"Fresh out, sorry."}
}, function() s.done = true end))
end, false),
Script.len(function(s) -- open door
door_open = easeTrig(s.timer/s.length)
end, 0.5),
Script.once(function() -- person appearss
alchemist_sprite = 192
customer_visible = false
return false
end),
Script.len(function(s) -- door closes
door_open = easeTrig(1-(s.timer/s.length))
end, 0.5),
}
:: sfx/0.sfx
b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTAzLTI5IDAxOjE0OjUxIixtb2RpZmllZD0iMjAyNC0w
NC0wNSAxMzo1NDowNiIscmV2aXNpb249NjY4XV1sejQAEAEAAFEHAADwJ3B4dQADKAAAAwAED0AQ
Ag4AAaABIAKgDgAPEAAN8MoBAgMEBQYHAA--kAgJCgsPDA8NDw4PDxAA8AANDxEPEg8TDxQPFQ8W
DxcTAPEBDxgPGQ8aDxsPHA8dDx4PHxQA8QAgDyEPIg8jDyQPJQ8mDycUAPEAKA8pDyoPKw8sDy0P
Lg8vFADxADAPMQ8yDzMPNA81DzYPNxQA-wU4DzkPOg87DzwPPQ8_Dz8AD--w-wEA6-8nWgEQBg8g
EAEgASAB8AACEAIMEAEgDyEgATAPQPDDDygP--DGD-gKD-8PgA-3Dw0B8AkBEAYMMAD-OR--AQDc
v-gPQAANQP--sPBwCgD--2Qf-wEAl1D-----Hw==
:: sfx/.info.pod
b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTAzLTI5IDAxOjE0OjUxIixzdG9yZWQ9IjIwMjQtMDMt
MjkgMDE6MTQ6NTEiXV1sejQABAAAAAMAAAAwbmls
:: shop.lua
--[[pod_format="raw",created="2024-03-29 01:14:51",modified="2024-04-05 14:54:05",revision=1117]]
-- this manages the main shop interface, where you look at the cupboard and plantss
-- and sstuff.
include("art.lua")
include("fairy.lua")
include("cabinet.lua")
cabinet = Cabinet:new(40, 10, 2)
fairy = Fairy:new()
alchemist_sprite = 192
alchemist_bob = 0
customer_sprite = 0
customer_bob = 0
customer_visible = false
door_open = 0
shop_screen = {}
function shop_screen.draw()
cls()
cabinet:draw()
-- alchemist!
bob(alchemist_sprite, 0, 7, 96, 128, alchemist_bob)
fairy:draw()
-- countertop!
draw_counter(14)
if customer_visible then
bob(customer_sprite, 240-96, 7, 96, 128, customer_bob)
end
draw_door(door_open)
end
function draw_counter(height)
rectfill(0, 135-height, 240, 135, 4)
for i=1,height do
rectfill(0,135-height, 0.5*i*i, 135-i, 4+16)
rectfill(240,135-height, 240-0.5*i*i, 135-i, 4+16)
end
line(0,135-height, 240, 135-height, 4+16)
end
function shop_screen.update()
fairy:move()
cabinet:update(fairy.x, fairy.y)
if btnp(4) then
local drawer = cabinet:drawer_at(fairy.x, fairy.y)
if drawer then change_scene(DrawerScene:new(drawer)) end
end
end
function draw_door(open)
palt(0x00, true)
if open >=0.0 then fillp(0b1111111111111111) end
if open > 0.1 then fillp(0b1011010111100101) end
if open > 0.2 then fillp(0b1010010110100101) end
if open > 0.4 then fillp(0b1010000110100100) end
if open > 0.7 then fillp(0x0000) end
rectfill(240-(96*open), 135-128, 240, 135, 0x00)
sspr(200, 0,0,96,128, 240-(96*open), 135-128, 96*open, 128)
palt()
fillp()
end
:: [eoc]