2 This file is part of POCA - a puzzle game
4 POCA is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This software is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>.
19 -- create new canvas for menu element, if nonexistent or resized
20 function new_canvas_needed(e
)
23 (e
.canvas
:getWidth() == math
.floor(
24 e
.w
* screen
.playfieldsize
29 -- math.floor probably redundant here, but
30 -- just to be sure, to prevent unnecesary resizing
31 e
.canvas
= love
.graphics
.newCanvas(
32 math
.floor(e
.w
* screen
.playfieldsize
),
33 e
.h
* screen
.playfieldsize
40 -- menu elements and menus (in corresponding tables)
44 menu_elements
= { -- relative to playfield size and position
47 x
= .5, y
= .5, w
= .25, h
= .25,
52 local ml
= menus
.menu_casette
.music_list
53 if not current
.played
then
54 current
.played
= backgrounds
[level_labels
[game
.currentlevel]]
.music
or
58 if ml
[a
] == current
.played
then
60 play_music(ml
[a
], true)
61 menus
.menu_casette
.play
= true
68 x
= .25, y
= .5, w
= .25, h
= .25,
70 if current
.music
and current
.music
:isPlaying() then
77 if not current
.played
then
78 current
.played
= backgrounds
[level_labels
[game
.currentlevel]]
.music
or
81 if current
.music
and current
.music
:isPlaying() then
83 menus
.menu_casette
.play
= nil
85 play_music(current
.played
, true)
86 menus
.menu_casette
.play
= true
92 x
= 0, y
= 0, w
= .25, h
= .25,
96 x
= .25, y
= 0, w
= .5, h
= .25,
97 content
= 'Wise man says:'
101 x
= 0, y
= 0, w
= 1, h
= .2,
103 if #game
.trinkets
== 0 then return 'You have no objects.' end
104 if not current
.trinket
then current
.trinket
= #game
.trinkets
end
105 return current
.trinket
.. '. ' .. trinkets
[game
.trinkets
[current
.trinket]]
.name
109 x
= .25, y
= .25, w
= .5, h
= .5,
111 content
= function(self
, dx
)
112 -- do nothing if no trinket
113 local t
= game
.trinkets
[current
.trinket
]
114 if not t
then return end
115 -- return text or draw on canvas, then return canvas
116 local _
, _
, real_w
, real_h
= get_absolute_dimensions(self
)
117 local function drawbmp(c
, dx
)
119 love
.graphics
.draw(c
.bmp
,
120 dx
+ c
.offsetx
* screen
.tilesize
, c
.offsety
* screen
.tilesize
,
121 0, real_w
/ c
.scale
, real_h
/ c
.scale
124 if self
.page
== 1 then -- trinket image
125 -- draw previous or next image when sweeping
126 if self
.sweep_x
or new_canvas_needed(self
) then
127 love
.graphics
.setCanvas(self
.canvas
)
128 love
.graphics
.clear()
130 dx
= math
.max(- real_w
, dx
)
131 dx
= math
.min(real_w
, dx
)
132 -- draw left, right and center imgs
133 drawbmp(menus
.menu_objects
.get_object_img(-1), dx
- real_w
)
134 drawbmp(menus
.menu_objects
.get_object_img(1), dx
+ real_w
)
135 drawbmp(menus
.menu_objects
.get_object_img(0), dx
)
136 love
.graphics
.setCanvas()
139 bmp
= self
.canvas
, offsetx
= 0, offsety
= 0,
140 scale
= screen
.tilesize
/ self
.w
142 elseif self
.page
== 2 then -- trinket description
144 if game
.trinkets
[current
.trinket
] == 'cigarettes' then
145 d
= '\n\nThere are ' .. game
.cigarettes
.. ' cigarettes left.'
147 return trinkets
[game
.trinkets
[current
.trinket]]
.description
.. d
150 onclick
= function(self
)
151 local t
= game
.trinkets
[current
.trinket
]
152 if not t
then return end
154 trinkets
[t
].onclick
and
155 not ( -- drawing in 'Secrets' is default
157 level_labels
[game
.currentlevel
] == 'Secrets'
160 trinkets
[t
].onclick()
161 elseif trinkets
[t
].description
then
162 self
.page
= self
.page
% 2 + 1
165 onsweep
= function(self
, dx
)
166 local w
= self
.w
* screen
.playfieldsize
168 menu_elements
.previous_object
:onclick()
169 elseif (dx
< - w
/ 2) then
170 menu_elements
.next_object
:onclick()
171 elseif math
.abs(dx
) < w
/ 8 then
178 x
= 0, y
= -.25, w
= .5, h
= .25,
179 content
= 'Select an object to donate',
181 big_object_donate
= {
182 x
= .25, y
= .25, w
= .5, h
= .5,
183 page
= 1, -- show pic, not description
184 content
= function(self
, dx
)
185 -- borrow content from big_object
186 -- uses own canvas, page, dx
187 return menu_elements
.big_object
.content(self
, dx
)
189 onclick
= function(self
)
190 menu_elements
.donate_title
.hidden
= true
191 menu_elements
.confirm_donate
.hidden
= false
193 onsweep
= function(self
, dx
)
194 local w
= self
.w
* screen
.playfieldsize
196 menu_elements
.previous_object_donate
:onclick()
197 elseif (dx
< - w
/ 2) then
198 menu_elements
.next_object_donate
:onclick()
199 elseif math
.abs(dx
) < w
/ 8 then
205 x
= 0, y
= .4, w
= .2, h
= .2,
208 change_screen('menu_objects')
209 current
.trinket
= select_trinket(-1)
213 x
= .8, y
= .4, w
= .2, h
= .2,
216 change_screen('menu_objects')
217 current
.trinket
= select_trinket(1)
220 previous_object_donate
= {
221 x
= 0, y
= .4, w
= .2, h
= .2,
224 change_screen('menu_objects_donate')
225 current
.trinket
= select_trinket(-1)
228 next_object_donate
= {
229 x
= .8, y
= .4, w
= .2, h
= .2,
232 change_screen('menu_objects_donate')
233 current
.trinket
= select_trinket(1)
238 x
= 0, y
= .8, w
= .3, h
= .2,
239 content
= 'Light a cigarette',
241 game
.matches
= game
.matches
- 1
242 if game
.matches
== 0 then lose_trinket('matches') end
243 change_screen('story_cigarettes')
248 x
= .35, y
= .8, w
= .4, h
= .2,
249 content
= 'Light surroundings',
251 game
.matches
= game
.matches
- 1
252 if game
.matches
== 0 then lose_trinket('matches') end
254 change_screen('playfield')
259 x
= 0, y
= -.25, w
= .5, h
= .25,
260 content
= 'Really donate?',
262 table.insert(game
.wise_man_has
, game
.trinkets
[current
.trinket
])
263 if game
.trinkets
[current
.trinket
] == 'map' then
264 wise_man_lose('advice_map')
266 lose_trinket(game
.trinkets
[current
.trinket
])
267 menus
.menu_objects
.onload()
268 menus
.wise_man_offers
.offer_pool
= wise_get_offer() -- get list of 3 elements or less
269 change_screen('wise_man_offers')
274 x
= 0, y
= -.25, w
= .5, h
= .25,
276 onclick
= function() change_screen('playfield') end,
279 x
= .5, y
= -.25, w
= .5, h
= .25,
280 content
= 'Main menu',
281 onclick
= function() change_screen('menu_main') end,
285 x
= .75, y
= .75, w
= .25, h
= .25,
286 content
= 'Delete all',
288 menu_elements
.confirm_delete
.hidden
= false
289 menu_elements
.delete_saved
.hidden
= true
294 x
= .75, y
= .5, w
= .25, h
= .25,
295 content
= 'Are you sure?',
297 for a
= 1, 4 do love
.filesystem
.remove('game' .. a
) end
298 menus
.menu_save
.onload()
303 x
= .75, y
= .75, w
= .25, h
= .25,
304 content
= 'Restart game',
306 menu_elements
.confirm_restart
.hidden
= false
307 menu_elements
.restart_game
.hidden
= true
312 x
= .75, y
= .5, w
= .25, h
= .25,
313 content
= 'Are you sure?',
315 menu_elements
.restart_game
.hidden
= false
316 menu_elements
.confirm_restart
.hidden
= true
323 menu_elements
['save_slot' .. a
] = {
324 x
= 0, y
= (a
- 1) * .25, w
= .5, h
= .25,
326 return 'Slot ' .. a
.. ': ' .. (menus
.save_slots_info
[a
] or 'empty')
329 save(game
, 'game' .. a
)
330 change_screen('playfield')
333 menu_elements
['load_slot' .. a
] = {
334 x
= 0, y
= (a
- 1) * .25, w
= .5, h
= .25,
336 return 'Slot ' .. a
.. ': ' .. (menus
.save_slots_info
[a
] or 'empty')
339 load_game('game' .. a
)
340 change_screen('playfield')
348 background
= function()
349 inplayfield(function (x
, y
, p
)
350 draw_floor(x
- .5, y
- .5)
353 onload
= function(self
)
355 for k
, v
in pairs(backgrounds
) do
356 table.insert(self
.music_list
, v
.music
)
358 table.insert(self
.music_list
, 'stairway')
361 menu_elements
.exit_to_title
,
362 { -- return to objects menu
363 x
= .75, y
= .75, w
= .25, h
= .25,
368 if current
.music
then
370 current
.music
:release()
373 change_screen('menu_objects')
377 x
= 0, y
= 0, w
= .75, h
= .25,
378 content
= 'POCA Soundtrack'
381 x
= .75, y
= 0, w
= .25, h
= .25,
382 content
= function() return trinkets
.casette
end,
385 x
= 0, y
= .25, w
= 1, h
= .25,
386 content
= function(self
, dx
)
387 -- return text only when not playing
388 if not settings
.music
then
389 return 'Music turned off in Options menu.'
390 elseif not current
.music
or not current
.music
:isPlaying() then
393 -- return canvas when playing
394 local ml
= menus
.menu_casette
.music_list
398 until order
> #ml
or ml
[order
] == current
.played
399 local title
= order
.. '/' .. #ml
.. '\n' .. current
.played
400 local seconds
= math
.floor(current
.music
:tell()) .. 's /\n' ..
401 math
.floor(current
.music
:getDuration()) .. 's'
402 local oy
= screen
.tilesize
/ 2 - screen
.fontheight
403 local snd_pos
= current
.music
:tell('samples') /
404 current
.music
:getDuration('samples') * screen
.playfieldsize
405 local new_pos
= snd_pos
+ dx
406 new_pos
= math
.max(0, new_pos
)
407 new_pos
= math
.min(screen
.playfieldsize
, new_pos
)
409 new_canvas_needed(self
)
410 love
.graphics
.setCanvas(self
.canvas
)
411 love
.graphics
.clear(.15, .15, .15)
412 love
.graphics
.setColor(0, .5, 0)
413 love
.graphics
.rectangle('fill',
415 snd_pos
, screen
.tilesize
417 if new_pos
< snd_pos
then
418 love
.graphics
.setColor(.15, .15, .15)
420 love
.graphics
.polygon('fill',
422 snd_pos
, screen
.tilesize
,
423 new_pos
, screen
.tilesize
/ 2
425 love
.graphics
.setColor(1, 1, 1)
426 love
.graphics
.printf(title
,
427 0, oy
, 3 * screen
.tilesize
, 'center'
429 love
.graphics
.printf(seconds
,
430 3 * screen
.tilesize
, oy
, screen
.tilesize
, 'center'
432 love
.graphics
.setCanvas()
434 bmp
= self
.canvas
, offsetx
= 0, offsety
= 0,
435 scale
= screen
.playfieldsize
438 onsweep
= function(self
, dx
)
439 if not current
.music
then return end
440 -- seeking in samples doesn't work for modules
441 local snd_pos
= current
.music
:tell()
442 local d
= current
.music
:getDuration()
443 local new_pos
= snd_pos
+ dx
* d
/ screen
.playfieldsize
444 new_pos
= math
.max(0, new_pos
)
445 new_pos
= math
.min(d
, new_pos
)
446 current
.music
:seek(new_pos
)
450 x
= 0, y
= .5, w
= .25, h
= .25,
455 local ml
= menus
.menu_casette
.music_list
456 if not current
.played
then
457 current
.played
= backgrounds
[level_labels
[game
.currentlevel]]
.music
or
461 if ml
[a
] == current
.played
then
462 a
= (a
- 2) % #ml
+ 1
463 play_music(ml
[a
], true)
464 menus
.menu_casette
.play
= true
470 menu_elements
.casette_play
,
471 menu_elements
.casette_next
,
473 x
= .75, y
= .5, w
= .25, h
= .25,
476 -- better not setLooping, as songs can be skipped
478 menus
.menu_casette
.play
and
479 current
.music
and not current
.music
:isPlaying()
481 if menus
.menu_casette
.loop
== 1 then
482 menu_elements
.casette_play
.onclick()
483 elseif menus
.menu_casette
.loop
== 2 then
484 menu_elements
.casette_next
.onclick()
488 local l
= menus
.menu_casette
.loop
or 0
490 {images
.loop_none
, images
.loop_1
, images
.loop_all
},
495 menus
.menu_casette
.loop
= ((menus
.menu_casette
.loop
or 0) + 1) % 3
496 if -- repeat all, while music in progress
497 menus
.menu_casette
.loop
> 0 and
498 current
.music
and current
.music
:isPlaying()
500 menus
.menu_casette
.play
= true
502 menus
.menu_casette
.play
= false
508 { -- hint book description
509 x
= .25, y
= .25, w
= .5, h
= .5,
510 content
= function(self
)
511 return trinkets
['hints_book'].description
513 onclick
= function(self
)
514 change_screen('menu_objects')
517 { -- open hint book button
518 x
= 0, y
= .8, w
= .4, h
= .2,
519 content
= 'Open anyway',
520 onclick
= function(self
)
521 change_screen('menu_show_hint')
524 menu_elements
.previous_object
,
525 menu_elements
.next_object
,
527 menu_elements
.exit_to_title
,
528 menu_elements
.object_name
,
531 background
= function()
532 -- show plafield in background
533 draw_initial_playfield()
537 menus
.menu_show_hint
.text
= (
538 hints
[levels
[game
.currentlevel
].setup
] or
539 "The page is missing."
541 -- possibly destroy book
542 if math
.random (1,6) == 1 then -- bad luck
543 menus
.menu_show_hint
.text
= (
544 menus
.menu_show_hint
.text
..
545 '\n\nThe book crumbles into dust!'
547 lose_trinket('hints_book')
550 { -- draw hint book picture
551 x
= .375, y
= -.25, w
= .25, h
= .25,
552 content
= function(self
)
553 return trinkets
['hints_book']
555 onclick
= function() change_screen('playfield') end,
559 x
= 0, y
= 0, w
= 1, h
= 1,
560 content
= function(self
)
561 return menus
.menu_show_hint
.text
563 onclick
= function() change_screen('playfield') end,
568 onload
= function (self
)
569 menu_elements
.big_object
.page
= 2
570 menu_elements
.light_cig
.hidden
= not has_trinket('cigarettes')
571 menu_elements
.light_secret
.hidden
= level_labels
[game
.currentlevel
] ~= 'Secrets'
573 { -- custom big_object
574 x
= .25, y
= .25, w
= .5, h
= .5,
575 content
= function(self
)
576 return trinkets
['matches'].description
..
577 '\n\nThere are ' .. game
.matches
.. ' matches left.'
580 onclick
= function(self
)
581 change_screen('menu_objects')
584 menu_elements
.object_name
,
585 menu_elements
.previous_object
,
586 menu_elements
.next_object
,
587 menu_elements
.exit_to_title
,
589 menu_elements
.light_cig
,
590 menu_elements
.light_secret
,
593 -- shows current, previous or next trinket
594 get_object_img
= function(s
) -- s can be -1, 0 or 1
595 local t
= trinkets
[game
.trinkets
[select_trinket(s
)]]
596 if -- show minimap instead of frame in Secrets
597 t
.name
== 'Scintilating frame' and
598 level_labels
[game
.currentlevel
] == 'Secrets'
601 bmp
= screen
.minimap
, offsetx
= 0, offsety
= 0,
602 scale
= screen
.tilesize
* 4/5
607 onload
= function(self
)
608 -- hide + and - when objects < 2
609 menu_elements
.next_object
.hidden
= #game
.trinkets
< 2
610 menu_elements
.previous_object
.hidden
= menu_elements
.next_object
.hidden
611 if (not current
.trinket
) or (not game
.trinkets
[current
.trinket
]) then
612 current
.trinket
= #game
.trinkets
614 menu_elements
.big_object
.page
= 1
615 menu_elements
.big_object
.canvas
= nil -- force redraw
617 menu_elements
.big_object
,
618 menu_elements
.object_name
,
619 menu_elements
.previous_object
,
620 menu_elements
.next_object
,
622 menu_elements
.exit_to_title
,
625 menu_objects_donate
= { -- used only with the wise man
626 onload
= function(self
)
627 -- hide "donate" if there are no objects
628 menu_elements
.big_object_donate
.hidden
= (#game
.trinkets
== 0)
629 menu_elements
.confirm_donate
.hidden
= true
630 menu_elements
.donate_title
.hidden
= false
631 -- hide + and - when objects < 2
632 menu_elements
.next_object_donate
.hidden
= #game
.trinkets
< 2
633 menu_elements
.previous_object_donate
.hidden
= menu_elements
.next_object_donate
.hidden
634 if (not current
.trinket
) or (not game
.trinkets
[current
.trinket
]) then
635 current
.trinket
= #game
.trinkets
637 menu_elements
.big_object_donate
.canvas
= nil -- force redraw
639 menu_elements
.big_object_donate
,
640 menu_elements
.object_name
,
641 menu_elements
.previous_object_donate
,
642 menu_elements
.next_object_donate
,
644 x
= .5, y
= -.25, w
= .5, h
= .25,
647 if not menu_elements
.confirm_donate
.hidden
then
648 menu_elements
.confirm_donate
.hidden
= true
649 menu_elements
.donate_title
.hidden
= false
651 change_screen('playfield')
655 menu_elements
.donate_title
,
656 menu_elements
.confirm_donate
,
661 menus
.save_slots_info
= read_slots()
662 menu_elements
.delete_saved
.hidden
= false
663 menu_elements
.confirm_delete
.hidden
= true
665 menu_elements
.exit_to_title
,
666 menu_elements
.save_slot1
, menu_elements
.save_slot2
,
667 menu_elements
.save_slot3
, menu_elements
.save_slot4
,
668 menu_elements
.delete_saved
,
669 menu_elements
.confirm_delete
,
671 x
= 0, y
= -.25, w
= .5, h
= .2,
672 content
= 'Save where?',
677 menus
.save_slots_info
= read_slots()
678 menu_elements
.restart_game
.hidden
= false
679 menu_elements
.confirm_restart
.hidden
= true
681 menu_elements
['load_slot' .. a
].hidden
= not menus
.save_slots_info
[a
]
685 x
= 0, y
= -.25, w
= .5, h
= .2,
686 content
= 'Load from which slot?',
688 menu_elements
.exit_to_title
,
689 menu_elements
.load_slot1
, menu_elements
.load_slot2
,
690 menu_elements
.load_slot3
, menu_elements
.load_slot4
,
691 menu_elements
.restart_game
,
692 menu_elements
.confirm_restart
,
694 menu_map
= { -- full-playfield map
696 x
= 0, y
= 0, w
= 1, h
= 1,
697 content
= function() return trinkets
.map
end,
698 onclick
= function() change_screen('menu_objects') end,
700 menu_elements
.exit_to_title
,
703 menu_frame
= { -- full-playfield frame
705 x
= 0, y
= 0, w
= 1, h
= 1,
707 if -- show minimap instead of frame in Secrets
708 game
.trinkets
[current
.trinket
] == 'frame' and
709 level_labels
[game
.currentlevel
] == 'Secrets'
712 bmp
= screen
.minimap
, offsetx
= 0, offsety
= 0,
713 scale
= screen
.tilesize
* 4/5
716 return trinkets
[game
.trinkets
[current
.trinket]]
719 onclick
= function() change_screen('menu_objects') end,
722 menu_elements
.exit_to_title
,
726 get_newvolume
= function(oldvolume
, dx
)
728 local newvolume
= oldvolume
+ dx
/
729 screen
.playfieldsize
/ .9
730 newvolume
= math
.max(0, newvolume
)
731 return math
.min(1, newvolume
)
733 -- sets volume or clicks to turn audio on or off
734 -- audio_kind = 'music' or 'sounds'
735 audio_onsweep
= function(e
, dx
, audio_kind
)
736 local w
= e
.w
* screen
.playfieldsize
737 if math
.abs(dx
) < w
/ 90 then
740 settings
[audio_kind
.. '_volume'] =
741 menus
.menu_settings
.get_newvolume(
742 settings
[audio_kind
.. '_volume'], dx
744 -- turn on or off music or sounds depending on volume
745 settings
[audio_kind
] = settings
[audio_kind
.. '_volume'] ~= 0
748 -- shows text and volume bar
749 -- audio_kind = 'music' or 'sounds'
750 audio_content
= function(e
, dx
, audio_kind
)
751 if e
.sweep_x
or new_canvas_needed(e
) then
752 local _
, _
, real_w
, real_h
= get_absolute_dimensions(e
)
753 love
.graphics
.setCanvas(e
.canvas
)
754 love
.graphics
.clear()
755 local newvolume
= menus
.menu_settings
.get_newvolume(
756 settings
[audio_kind
.. '_volume'], dx
759 love
.graphics
.setColor(0, .5, 0)
760 love
.graphics
.rectangle('fill',
761 real_w
* .05, real_h
/ 2,
762 real_w
* .9 * newvolume
, real_h
/ 3
764 love
.graphics
.setColor(0, 1, 0)
765 love
.graphics
.rectangle('line',
766 real_w
* .05, real_h
/ 2,
767 real_w
* .9, real_h
/ 3
769 love
.graphics
.setColor(1, 1, 1)
770 love
.graphics
.printf(
773 (audio_kind
== 'music' and 'Music is ' or 'Sounds are '),
774 (settings
[audio_kind
] and {1, 1, 1} or {1,1,1, .5}),
775 (settings
[audio_kind
] and 'on' or 'off') ..
777 math
.floor(newvolume
* 100) .. '%'
778 }, 0, screen
.tilesize
/ 10, real_w
, 'center'
780 love
.graphics
.setCanvas()
783 bmp
= e
.canvas
, offsetx
= 0, offsety
= 0,
784 scale
= screen
.playfieldsize
/ e
.w
788 menu_elements
.exit_to_title
,
790 x
= 0, y
= 0, w
= 1, h
= .25,
791 content
= function(self
, dx
)
792 return menus
.menu_settings
.audio_content(self
, dx
, 'music')
794 onsweep
= function(self
, dx
)
795 menus
.menu_settings
.audio_onsweep(self
, dx
, 'music')
796 if current
.music
then
797 current
.music
:setVolume(settings
.music_volume
)
801 settings
.music
= not settings
.music
802 if not settings
.music
and current
.music
then
803 current
.music
:pause()
804 elseif current
.music
then
805 current
.music
:play(true)
810 x
= 0, y
= .25, w
= 1, h
= .25,
811 content
= function(self
, dx
)
812 return menus
.menu_settings
.audio_content(self
, dx
, 'sounds')
814 onsweep
= function(self
, dx
)
815 menus
.menu_settings
.audio_onsweep(self
, dx
, 'sounds')
816 load_sounds() -- sets volume
820 settings
.sounds
= not settings
.sounds
824 x
= 0, y
= .5, w
= 1, h
= .25,
831 return 'Animations are ' ..
832 a
[settings
.speed
+ 1]
835 settings
.speed
= (settings
.speed
+ 1) % 3
838 { -- vibration toggle
839 x
= 0, y
= .75, w
= 1, h
= .25,
841 return 'Vibration is ' ..
842 (settings
.vibrate
and 'on' or 'off')
845 settings
.vibrate
= not settings
.vibrate
851 x
= 0, y
= 0, w
= 1, h
= 1,
852 content
= 'My journey began when arrows invaded every room of my apartment, including my bedroom.',
853 onclick
= function() change_screen('playfield') end,
858 onload
= function(self
)
859 self
.content
= click_corpse()
862 x
= 0, y
= 0, w
= 1, h
= 1,
863 content
= function() return menus
.story_corpse
.content
end,
864 onclick
= function() change_screen('playfield') end,
869 x
= 0, y
= .25, w
= 1, h
= .5,
870 content
= 'You sense everything got back where it was.',
871 onclick
= function() change_screen('playfield') end,
876 x
= 0, y
= 0, w
= 1, h
= 1,
877 content
= 'The end. Thanks for playing!',
878 onclick
= function() love
.event
.push('quit') end,
882 menu_elements
.wise_man_says
,
884 x
= 0, y
= .25, w
= 1, h
= .5,
885 content
= 'Are you mad? You are going to finish the game!',
886 onclick
= function() start_wise_man_battle() end,
891 game
.cigarettes
= game
.cigarettes
- 1
892 if game
.cigarettes
== 0 then
893 lose_trinket('cigarettes')
897 x
= 0, y
= 0, w
= 1, h
= 1,
899 if game
.cigarettes
== 2 then
900 return 'You hastily light a cigarette and inhale. You feel somewhat better.'
901 elseif game
.cigarettes
== 1 then
902 return 'You take your time smoking a cigarette. It feels good.'
903 elseif game
.cigarettes
== 0 then
904 return 'You nervously smoke a cigarette. It feels slightly nauseating.'
907 onclick
= function() change_screen('playfield') end,
911 wise_man
= { -- wise man accepts donations
912 background
= function()
913 inplayfield(function (x
, y
, p
)
914 draw_floor(x
- .5, y
- .5, false, nil, nil)
917 menu_elements
.wise_man_picture
,
919 x
= 0, y
= .25, w
= 1, h
= .5,
920 content
= 'I am the wise man.\n' ..
921 'Not to talk much is wise.\n\n' ..
922 'I accept your generous donations.',
925 x
= 0, y
= -.25, w
= .5, h
= .25,
928 change_screen('menu_objects_donate')
929 menu_elements
.confirm_donate
.hidden
= true
933 x
= .5, y
= -.25, w
= .5, h
= .25,
936 change_screen('playfield')
941 onload
= function(self
)
942 -- offer pool created when donate button was pressed
943 if #self
.offer_pool
== 0 then
944 change_screen('wise_man_thanks')
947 self
.offer
= self
.offer_pool
[#self
.offer_pool
]
948 self
.offer_pool
[#self
.offer_pool
] = nil
950 wise_advices
[self
.offer
] and wise_advices
[self
.offer
][1]
952 trinkets
[self
.offer
].description
or
953 trinkets
[self
.offer
].name
954 local text_ending
= string.sub(self
.offer_text
, -1)
955 if not (text_ending
== '.' or text_ending
== '!') then
956 self
.offer_text
= self
.offer_text
.. '.'
960 x
= .2, y
= .75, w
= .2, h
= .2,
963 local offer
= menus
.wise_man_offers
.offer
964 -- Wise man loses offered
966 -- player gains offered, if trinket
967 if trinkets
[offer
] then
971 menus
.wise_man_thanks
.advice
= wise_advices
[offer
]
973 change_screen('wise_man_thanks')
976 { -- No button ; make a new offer
977 x
= .6, y
= .75, w
= .2, h
= .2,
979 onclick
= function() change_screen('wise_man_offers') end,
981 menu_elements
.wise_man_picture
,
983 x
= 0, y
= .25, w
= 1, h
= .5,
985 return 'Wise man offers: ' ..
986 menus
.wise_man_offers
.offer_text
..
992 onload
= function(self
)
994 self
.towrite
= 'Hear this ' .. self
.advice
[1] .. '!\n\n' .. self
.advice
[2]
998 self
.advice
= nil -- reset for the next time
1000 menu_elements
.wise_man_picture
,
1001 menu_elements
.wise_man_says
,
1003 x
= 0, y
= .25, w
= 1, h
= .5,
1004 content
= function()
1005 return menus
.wise_man_thanks
.towrite
or 'Thank you for your donation!'
1008 { -- donation button
1009 x
= 0, y
= -.25, w
= .5, h
= .26,
1010 content
='Trade another item',
1011 onclick
= function() change_screen('menu_objects_donate') end
1014 x
= .5, y
= -.25, w
= .5, h
= .25,
1015 content
='Goodbye!',
1016 onclick
= function() change_screen('playfield') end
1021 background
= function()
1022 inplayfield(function (x
, y
, p
)
1023 draw_floor(x
- .5, y
- .5)
1026 onload
= function(self
)
1030 menu_elements
.exit_to_title
,
1032 x
= .5, y
= 0, w
= .5, h
= .25,
1033 content
= 'Copyright 2007, 2019, 2020 Pajo\n<xpio@tut.by>'
1036 x
= 0, y
= 0, w
= .25, h
= .25,
1037 content
= images
.po
,
1040 x
= .25, y
= 0, w
= .25, h
= .25,
1041 content
= images
.ca
,
1043 { -- text - refreshes bg which gets destroyed on resize
1044 x
= 0, y
= .25, w
= 1, h
= .5,
1045 content
= function()
1046 local page
= menus
.menu_about
.page
1048 return '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.'
1049 elseif page
== 2 then
1050 return '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.'
1051 elseif page
== 3 then
1052 return 'You should find a copy of the GNU General Public License somewhere in this game. If not, see <https://www.gnu.org/licenses/>.'
1056 { -- button: Copyright
1057 x
= 0, y
= .75, w
= .33, h
= .25,
1058 content
= 'Copyright',
1059 onclick
= function()
1060 menus
.menu_about
.page
= 1
1063 { -- button: Warranty
1064 x
= .33, y
= .75, w
= .34, h
= .25,
1065 content
= 'Warranty',
1066 onclick
= function()
1067 menus
.menu_about
.page
= 2
1070 { -- button: License
1071 x
= .67, y
= .75, w
= .33, h
= .25,
1072 content
= 'License',
1073 onclick
= function()
1074 menus
.menu_about
.page
= 3
1082 -- pfcanvas will be used in foreground
1083 screen
.pfcanvas
:renderTo(draw_title
)
1087 x
= .5, y
= -.25, w
= .25, h
= .25,
1089 onclick
= function() change_screen('menu_about') end,
1092 x
= .75, y
= -.25, w
= .25, h
= .25,
1094 onclick
= function() love
.event
.push('quit') end,
1097 x
= 0, y
= 0, w
= 1, h
= 1,
1098 content
= function()
1100 bmp
= screen
.pfcanvas
,
1101 offsetx
= 0, offsety
= 0,
1102 scale
= screen
.playfieldsize
1107 x
= 0, y
= .0, w
= .25, h
= .25,
1109 onclick
= function() change_screen('menu_load') end,
1112 x
= 0, y
= .25, w
= .25, h
= .25,
1114 onclick
= function() change_screen('menu_save') end,
1117 x
= .75, y
= .5, w
= .25, h
= .25,
1118 content
= 'Objects',
1119 onclick
= function() change_screen('menu_objects') end,
1122 x
= .75, y
= .75, w
= .25, h
= .25,
1123 content
= 'Options',
1124 onclick
= function() change_screen('menu_settings') end,
1133 function get_absolute_dimensions(e
)
1135 e
.x
* screen
.playfieldsize
+ screen
.playfieldx
,
1136 e
.y
* screen
.playfieldsize
+ screen
.playfieldy
,
1137 e
.w
* screen
.playfieldsize
,
1138 e
.h
* screen
.playfieldsize
1142 function draw_menu_elements()
1143 local function draw_element(e
, selected
)
1144 if e
.hidden
then return end
1145 local x
, y
, w
, h
= get_absolute_dimensions(e
)
1147 love
.graphics
.setColor(.5, 0, 1)
1149 love
.graphics
.setColor(.1, .1, .1)
1151 if e
.onclick
and not e
.transparent
then
1152 love
.graphics
.rectangle('fill', x
, y
, w
, h
)
1153 love
.graphics
.setColor(.5, .5, .5)
1154 love
.graphics
.rectangle('line', x
+ 2, y
+ 2, w
- 4, h
- 4)
1156 love
.graphics
.setColor(1,1,1)
1159 if type(e
.content
) == 'function' then
1162 dx
= (love
.mouse
.getPosition() - e
.sweep_x
)
1168 if type(c
) == 'string' then
1169 local lines
= select(2, screen
.defaultfont
:getWrap(c
, w
- 4))
1170 local offsety
= (h
- screen
.defaultfont
:getHeight() * #lines
) / 2
1171 love
.graphics
.printf(c
, x
+ 2, y
+ 2 + offsety
, w
- 4 ,'center')
1172 elseif type(c
) == 'table' and c
.bmp
then
1173 local function drawbmp(c
)
1174 love
.graphics
.draw(c
.bmp
,
1175 x
+ c
.offsetx
* screen
.tilesize
, y
+ c
.offsety
* screen
.tilesize
,
1183 for a
= 1, #menus
.current
do
1184 draw_element(menus
.current
[a
], menus
.current
.selected
== a
)
1189 function draw_menu()
1190 menus
.current
= menus
.current
or menus
[current
.screen
]
1191 if menus
.current
.background
then
1192 screen
.pfcanvas
:renderTo(menus
.current
.background
)
1194 love
.graphics
.setColor(.5,.5,.5)
1195 if game
.shuffled
and not menus
.current
.background
then
1198 love
.graphics
.draw(screen
.pfcanvas
, screen
.playfieldx
, screen
.playfieldy
)
1200 draw_menu_elements()
1204 function menu_click(mx
, my
, sweep_end
)
1205 if not menus
.current
then return end
1207 for _
, e
in ipairs(menus
.current
) do
1209 local dx
= mx
- e
.sweep_x
1211 e
.canvas
= nil -- force redraw
1215 else -- not sweep_end: start sweep or normal click if no sweep defined
1216 for _
, e
in ipairs(menus
.current
) do
1217 if (not e
.hidden
) and isin(mx
, my
, get_absolute_dimensions(e
)) then
1220 elseif e
.onclick
then
1229 function menu_select(dx
, dy
)
1230 -- if nothing selected, select first
1231 local selected
= menus
.current
.selected
1232 if not selected
or not dx
then
1233 menus
.current
.selected
= 1
1237 -- search for suitable element in the desired direction
1238 -- start from the element' center, then check in the desired direction
1239 -- but with deviation along the opposite (passive; pas) axis
1240 local function searchelement(startx
, starty
, dx
, dy
)
1241 local RESOLUTION
= 50 -- dots per playfield
1242 local DEVIATION
= .5
1243 local start_act
, start_pas
, d_act
= startx
, starty
, dx
1244 if dx
== 0 then start_act
, start_pas
, d_act
= starty
, startx
, dy
end
1245 for act
= start_act
, start_act
+ d_act
, d_act
/ RESOLUTION
do
1246 for pas
= 0, math
.abs(start_act
- act
), 1 / RESOLUTION
do
1247 for dir
= - DEVIATION
, DEVIATION
, 2 * DEVIATION
do
1248 for e
= 1, #menus
.current
do
1249 if not (selected
== e
) then
1250 local mce
= menus
.current
[e
]
1251 local x
, y
= act
, start_pas
+ pas
* dir
1252 if dx
== 0 then x
, y
= y
, x
end
1254 mce
.onclick
and not mce
.hidden
and
1255 isin(x
, y
, mce
.x
, mce
.y
, mce
.w
, mce
.h
)
1266 -- search from center of current element
1267 local newselected
= searchelement(
1268 menus
.current
[selected
].x
+ menus
.current
[selected
].w
/ 2,
1269 menus
.current
[selected
].y
+ menus
.current
[selected
].h
/ 2,
1273 if newselected
then menus
.current
.selected
= newselected
end
1277 function menu_press()
1278 for a
= 1, #menus
.current
do
1279 local e
= menus
.current
[a
]
1280 if e
.onclick
and (a
== menus
.current
.selected
) and not e
.hidden
then
1285 -- if nothing selected, execute the first with onclick
1286 for i
, e
in ipairs(menus
.current
) do
1295 function change_screen(newscreen
, only_once
)
1296 if only_once
and game
.shown_screens
[newscreen
] then return end
1297 game
.shown_screens
[newscreen
] = true
1299 current
.screen
= newscreen
1300 if menus
[current
.screen
] and menus
[current
.screen
].onload
then menus
[current
.screen
]:onload() end