3 Copyright 2019 Pajo <xpio at tut dot by>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
30 {1, 0}, {1, -1}, {0, -1}, {-1, -1},
31 {-1, 0}, {-1, 1}, {0, 1}, {1, 1}
35 screen
= 'playfield', -- 'playfield', 'menu_objects', 'story_beginning' ...
37 change_screen
= function(self
, newscreen
, only_once
)
38 if only_once
and self
.shown_screens
[newscreen
] then return end
39 self
.shown_screens
[newscreen
] = true
40 menu_elements
.drawn
= nil
41 self
.screen
= newscreen
42 if newscreen
== 'playfield' then
44 elseif not screen
.bgcanvas
then
45 screen
.bgcanvas
= love
.graphics
.newCanvas(screen
.width
, screen
.height
)
46 screen
.bgcanvas
:renderTo(draw_playfield
)
49 phase
= 'play', -- see next_phase()
62 playfield
= {{}, {}, {}, {}}
68 function explode_clicked()
69 local p
= playfield
[playfield
.clickedx
][playfield
.clickedy
]
70 if p
.kind
== 'memory' then
71 playfield
[playfield
.clickedx
][playfield
.clickedy
] = p
.memory
--recognizeink(p.of)
74 -- clicked arrows should explode, even if player dies
75 -- to explain turning of yellow arrows, and just for fun
76 -- first, copy them to new table
77 local newtile
= recognizeink('0')
78 if p
.shape
== 'x' or p
.shape
== '+' then -- perhaps needs more conditions later
80 newtile
.color
= images
[p
.name
].color
81 newtile
.shape
= p
.shape
83 newtile
.arrowgift
= p
.arrowgift
86 newtile
.sleeping_name
= p
.sleeping_name
or p
.name
89 for dir
, arr
in ipairs(p
.arrows
) do
90 -- arrows are false or integer: distance from tile center
91 newtile
.arrows
[dir
] = arr
93 local discard
, distance
, pf
= check_direction(playfield
.clickedx
, playfield
.clickedy
, directions
[dir
][1], directions
[dir
][2])
95 if pf
and pf
.kind
== 'yellow' then
98 end -- mark yellow arrows for rotation
99 explode_arrow(playfield
.clickedx
, playfield
.clickedy
, dir
, distance
, false, target
)
103 -- mark all purple arrow tiles if a purple arrow is clicked
104 -- even if only a memory --!!
105 if p
.kind
== 'purple' then
106 game
.temppurple
= not game
.temppurple
107 inplayfield(function (x
, y
, p
) if p
.kind
== 'purple' or (p
.memory
and p
.memory
.kind
== 'purple') then
108 playfield
[x
][y
].hit
= true
109 local pf
= playfield
[x
][y
].memory
or playfield
[x
][y
]
113 playfield
[playfield
.clickedx
][playfield
.clickedy
] = newtile
117 function check_player_death()
118 inplayfield(function(x
, y
, p
)
119 if not p
.arrows
then return end
120 for direction
, arrow
in ipairs(p
.arrows
) do
121 if not p
.backgrounded
and arrow
then
122 local death
, distance
= check_direction(x
, y
,
123 directions
[direction
][1], directions
[direction
][2])
125 explode_arrow(x
, y
, direction
, distance
, true) -- kill=true
134 function influence_and_rotate()
135 -- devil influence on yellow arrows
138 local yellow_arrows
= {}
139 inplayfield(function(x
, y
, p
)
140 if p
.kind
== 'yellow' then
141 if p
.devil
then -- don't insert last influenced yellow arrow
142 playfield
[x
][y
].devil
= nil -- unset but remember in case there are no more arrows
145 table.insert(yellow_arrows
, {x
, y
})
149 if #yellow_arrows
> 0 then
150 -- choose among available yellow arrows
151 dx
, dy
= unpack(yellow_arrows
[math
.random(#yellow_arrows
)])
152 playfield
[dx
][dy
].center
= 'dot'
153 if playfield
[dx
][dy
].hit
then
154 rotate_arrows(dx
, dy
, 0) -- will just remove dot
156 rotate_arrows(dx
, dy
, -1)
158 playfield
[dx
][dy
].hit
= false -- so won't be rotated later
160 if dx
then playfield
[dx
][dy
].devil
= true end -- new or last dx, dy in case there was only 1
162 -- turn yellow and purple arrows
163 inplayfield(function(x
, y
, p
)
164 if (p
.kind
== 'yellow' or p
.kind
== 'purple' or (p
.kind
== 'memory' and p
.memory
.kind
== 'purple')) and p
.hit
then
169 -- wake regenerating arrows up
170 inplayfield(function(x
, y
, p
)
171 if p
.sleep
and p
.sleep
> 0 then
172 p
.sleep
= p
.sleep
- 1
174 playfield
[x
][y
] = recognizeink(p
.sleeping_name
)
180 inplayfield(function(x
, y
, p
)
181 if p
.kind
== 'red' or p
.kind
== 'loadred' then rotate_arrows(x
, y
) end
186 function next_phase()
187 local nextphases
= {'rotate', 'die', 'level', 'play', 'explode'}
188 setmetatable(nextphases
, {__index
= function (table, key
)
190 if rawget(table, a
) == key
then return table[a
+ 1] or table[1] end
194 game
.phase
= nextphases
[game
.phase
] or 'play' -- nextphases['opengifts'] == nil , etc.
195 if game
.phase
== 'explode' then -- 1. explode clicked arrow
197 elseif game
.phase
== 'rotate' then
198 influence_and_rotate()
199 elseif game
.phase
== 'die' then -- 2. check for player death
201 elseif game
.phase
== 'level' then -- 3. check for level end
203 elseif game
.phase
== 'play' then -- 4. await next click
208 function remember_arrows()
209 playfield
.clickedx
, playfield
.clickedy
= nil, nil
210 inplayfield(function (x
, y
, p
)
211 if p
.memory
then playfield
[x
][y
] = p
.memory
end
216 function love
.resize()
217 screen
.w
= love
.graphics
.getWidth()
218 screen
.h
= love
.graphics
.getHeight()
219 screen
.playfieldsize
= math
.min(screen
.w
, screen
.h
) * .9
220 screen
.tilesize
= screen
.playfieldsize
/ 4
221 screen
.playfieldx
= (screen
.w
/ screen
.playfieldsize
- 1) * 2
222 screen
.playfieldy
= (screen
.h
/ screen
.playfieldsize
- 1) * 2
223 if screen
.bgcanvas
then
224 screen
.bgcanvas
:renderTo(love
.graphics
.clear
)
225 screen
.bgcanvas
:renderTo(draw_playfield
)
227 screen
.defaultfont
= love
.graphics
.newFont(screen
.tilesize
/ 5)
231 function love
.update(dt
)
232 if game
.screen
~= 'playfield' then return end
234 love
.timer
.sleep(1/30 - dt
)
236 game
.timer
= game
.timer
+ dt
242 love
.graphics
.setColor(1, 1, 1)
243 love
.graphics
.print(game
.currentlevel
, 1, 2) --debug
244 if anim
[1] then love
.graphics
.print(anim
[1].step
, 1, 12) end
248 function inplayfield(f
)
249 for y
= 1, 4 do for x
= 1, 4 do
250 f(x
, y
, playfield
[x
][y
])
256 if game
.screen
== 'playfield' then
265 function check_direction(x
, y
, dx
, dy
)
268 xx
, yy
= x
+ dx
* a
, y
+ dy
* a
269 if not playfield
[xx
] then return false, a
end -- arrow hits playfield edge
270 p
= playfield
[xx
][yy
]
271 if not p
then return false, a
end --||--
272 if xx
== playfield
.clickedx
and yy
== playfield
.clickedy
then return true, a
end -- arrow hits player
273 if not p
.passive
then return false, a
, p
end -- arrow hits another object. return that object
279 function check_level()
280 -- checks for the level end (when all arrows are eliminated)
281 -- game should generally check for arrows not shapes!
282 local anymemories
, anyactivearrows
= false, false
283 inplayfield(function(x
, y
, p
)
284 anymemories
= anymemories
or p
.memory
285 if p
.arrows
then for direction
, arrow
in ipairs(p
.arrows
) do
286 anyactivearrows
= anyactivearrows
or arrow
289 if not anyactivearrows
and not open_gifts() then
293 game
.purple
= game
.temppurple
294 setuplevel(game
.currentlevel
+ 1)
300 function open_gifts()
302 inplayfield(function(x
, y
, p
)
303 if p
.name
== 'J' or p
.name
== 'K' then
304 if p
.name
== 'K' then game
.devil
= true end
305 playfield
[x
][y
] = recognizeink(p
.gift
)
306 playfield
[x
][y
].gift
= p
.gift
307 playfield
[x
][y
].ingift
= true
313 --playfield.clickedx, playfield.clickedy = nil, nil
314 --necessary to preserve clicked for opening arrowgifts
320 function clicked(mx
, my
)
321 if game
.screen
~= 'playfield' then
324 if game
.phase
~= 'play' then
333 -- clickedx, clickedy from previous turn
334 if playfield
.clickedx
and
335 playfield
[playfield
.clickedx
][playfield
.clickedy
].arrowgift
and
336 not (mx
== playfield
.clickedx
and my
== playfield
.clickedy
) then
337 local gift
= playfield
[playfield
.clickedx
][playfield
.clickedy
].gift
339 animate_arrow_gift(playfield
.clickedx
, playfield
.clickedy
)
341 playfield
[playfield
.clickedx
][playfield
.clickedy
].gift
= gift
343 -- new clickedx, clickedy
344 playfield
.clickedx
, playfield
.clickedy
= mx
, my
345 if playfield
[mx
][my
].onclick
then playfield
[mx
][my
].onclick() end
351 function isin(mx
, my
, x
, y
, w
, h
)
352 return mx
>= x
and mx
<= x
+ w
and my
>= y
and my
<= y
+ h
356 function love
.mousepressed(mx
, my
, button
)
357 if game
.screen
== 'playfield' then
358 inplayfield(function (x
, y
, p
)
359 if isin(mx
, my
, (screen
.playfieldx
+ x
- 1) * screen
.tilesize
, (screen
.playfieldy
+ y
- 1) * screen
.tilesize
, screen
.tilesize
, screen
.tilesize
) then
369 function love
.keypressed(k
)
370 if k
== 'escape' then
371 if game
.screen
~= 'playfield' then
372 game
:change_screen('playfield')
374 love
.event
.push('quit')
377 love
.event
.push('quit')
379 setuplevel(game
.currentlevel
+ 1)
381 setuplevel(game
.currentlevel
- 1)
383 game
.temppurple
= not game
.temppurple
384 print('purple', game
.temppurple
)
386 game
.underpill
= not game
.underpill
387 print('underpill', game
.underpill
)
389 game
.devil
= not game
.devil
390 print('devil', game
.devil
)
392 if game
.screen
~= 'playfield' then
393 game
:change_screen('playfield')
395 game
:change_screen('menu_levels')