forked from Eddy34743/Corona-Comics-SDK-2.0
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathReader.lua
1697 lines (1516 loc) · 61.8 KB
/
Reader.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
--
-- Abstract: Corona Comics 2 Demo
-- Be Confident in Who You Are: A Middle School Confidential™ Graphic Novel Lite Version
--
-- Version: 2.0 (September 20, 2011)
-- Sample code is MIT licensed, see http://developer.anscamobile.com/code/license
-- Copyright (C) 2011 Electric Eggplant. All Rights Reserved.
-- Copyright (C) 2011 ANSCA Inc. All Rights Reserved.
--
-- Images and text excerpted from "Be Confident in Who You Are" by Annie Fox, M.Ed.,
-- (C) 2008. Used with permission of Free Spirit Publishing Inc., Minneapolis,
-- MN; 800-735-7323; www.freespirit.com. All Rights Reserved.
--
-- Reader.lua - the framework code, or the guts of the comic reader, loads pages,
-- handles all camera moves and page changes, has sound routines, and more
--
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
--
module(..., package.seeall)
------------------------------------------------------------
-- Set-up
------------------------------------------------------------
local imgUIDir = "images/ui/"
if _G.device ~= "android" then
removeAllPages = {} -- forward declare function
-- Add low memory event handler to remove pages immediately if there's a warning
local function handleLowMemory( event ) -- this only works in iOS
timer.performWithDelay(1500, removeAllPages)
end
Runtime:addEventListener( "memoryWarning", handleLowMemory )
end
local lastScale = 0
local Object = Runtime.Object
local kTime = 350 -- how long most transitions will last
------------------------------------------------------------
-- Routines to recursively remove display objects/groups
------------------------------------------------------------
local coronaMetaTable = getmetatable(display.getCurrentStage())
-- Returns whether aDisplayObject is a Corona display object.
-- note that all Corona types seem to share the same metatable, which is used for the test.
-- @param aDisplayObject table - possible display object.
-- @return boolean - true if object is a display object
isDisplayObject = function(aDisplayObject)
return (type(aDisplayObject) == "table" and getmetatable(aDisplayObject) == coronaMetaTable)
end
-- Function will bottom-up recursively removeSelf all display objects and groups that may be part of objectOrGroup, before removeSelf'ing itself.
-- @param objectOrGroup a display object or group to be removeSelf'ed with all its possible members.
local function cleanGroups ( objectOrGroup )
if (not isDisplayObject(objectOrGroup)) then return end
if objectOrGroup.numChildren then
-- we have a group, so first clean that out
while objectOrGroup.numChildren > 0 do
-- clean out the last member of the group (work from the top down!)
cleanGroups ( objectOrGroup[objectOrGroup.numChildren])
end
end
-- Do we also need to remove the touch and tap objects? Currently not doing that.
-- if objectOrGroup.touch then
-- print("Object had touch event, remove it")
-- objectOrGroup:removeEventListener( "touch", Reader )
-- end
-- if objectOrGroup.tap then
-- print("Object had tap event, remove it")
-- objectOrGroup:removeEventListener( "tap", Reader )
-- end
-- we have either an empty group or a normal display object - remove it
objectOrGroup:removeSelf()
objectOrGroup = nil
return
end
-------------------------------------------------------------------------------
local Frame = Object:new()
------------------------------------------------------------
-- Initialize the frame
------------------------------------------------------------
function Frame:initialize()
local view = display.newGroup()
self.view = view
-- Center view in content
local contentW = display.contentWidth
local contentH = display.contentHeight
local halfContentW = 0.5*contentW
local halfContentH = 0.5*contentH
view.x = halfContentW
view.y = halfContentH
-- max dimension
local unit = ( contentH > contentW ) and contentH or contentW
self.unit = unit
-- length needs to be long enough to cover diagonal of screen
-- and also be an even integer
local len = 2 * unit
self.len = len
-- Insert rects and position relative to view's origin
local top = display.newRect( -halfContentW, -halfContentH, len, 0 )
view:insert( top )
local bottom = display.newRect( -halfContentW, halfContentH, len, 0 )
view:insert( bottom )
local left = display.newRect( -halfContentW, -halfContentH, 0, contentH )
view:insert( left )
local right = display.newRect( halfContentW, -halfContentH, 0, contentH )
view:insert( right )
view.top = top
view.bottom = bottom
view.left = left
view.right = right
end
------------------------------------------------------------
-- Math function - floor (not used?)
------------------------------------------------------------
local function floor( a )
return a - a%1
end
------------------------------------------------------------
-- Set the boundaries of the frame, crop with masking rectangles
------------------------------------------------------------
function Frame:setBounds( w, h, animate )
local view = self.view
local contentW = display.contentWidth
local contentH = display.contentHeight
-- only exchange values if we're not on the simulator. Work-around for Corona bug?
if _G.isDeviceLandscape and system.getInfo( "environment" ) ~= "simulator" then
contentW,contentH = contentH,contentW
end
-- Force h to be even integer
h = math.ceil(h)
if math.mod( h, 2 )~= 0 then
h = h + 1
end
-- view group is at the center of the content bounds
local len = self.len
local wNew = 0.5*(len - w)
local hNew = 0.5*(len - h)
local xNew = 0.5*(w + wNew)
local yNew = 0.5*(h + hNew)
h = h + 2 -- add extra pixels to avoid the gaps during transition
-- fixes gaps, but during transition to full page we now see faint black line
-- at intersection where two rectangles overlap
view.top.x = 0
view.bottom.x = 0
if animate then
transition.to( view.top, { height=hNew, y=-yNew, time=kTime, transition=easing.inOutQuad } )
transition.to( view.bottom, { height=hNew, y=yNew, time=kTime, transition=easing.inOutQuad } )
else
local top = view.top
top.height = hNew
top.x = 0
top.y = -yNew
local bottom = view.bottom
bottom.height = hNew
bottom.x = 0
bottom.y = yNew
end
if animate then
transition.to( view.left, { width=wNew, height=h, x=-xNew, y=0, time=kTime, transition=easing.inOutQuad } )
transition.to( view.right, { width=wNew, height=h, x=xNew, y=0, time=kTime, transition=easing.inOutQuad } )
else
local left = view.left
left.width = wNew
left.height = h
left.x = -xNew
left.y = 0
local right = view.right
right.width = wNew
right.height = h
right.x = xNew
right.y = 0
end
end
------------------------------------------------------------
-- Set the color and alpha of the masking rectangles
------------------------------------------------------------
function Frame:setColor( ... )
local view = self.view
local alpha = 255 -- Debug values:
view.top:setFillColor( 0,0,0,alpha ) -- 120,0,0
view.bottom:setFillColor( 0,0,0,alpha ) -- 0,120,0
view.left:setFillColor( 0,0,0,alpha ) -- 0,0,120
view.right:setFillColor( 0,0,0,alpha ) -- 200,0,0
end
-------------------------------------------------------------------------------
local Reader = Object:new()
------------------------------------------------------------
-- Routines to play the UI sounds
------------------------------------------------------------
-- Play sound on free channel
local function playSound(sound, volume)
if not volume then volume = 1 end
local channel = audio.findFreeChannel()
if channel then
audio.setVolume( volume, { channel=channel } )
audio.play( sound, { channel=channel } )
end
end
-- UI sound effect for moving to next/previous frame or page
local function frameChangeSound()
if _G.showFullPages or _G.currentPage >= firstAboutPage then -- always use page turn sound on About pages
playSound( sndUIPageTurn )
else
playSound( sndUIBeep1 )
end
end
-- UI sound effect for zooming in or out
local function zoomSound()
if _G.showFullPages then
playSound( sndUIZoom1In, .3 )
else
playSound( sndUIZoom1Out, .3 )
end
end
------------------------------------------------------------
-- Initialize the Reader
-- reader:initialize( basename, data [, startPage, options] )
--
-- Options:
-- loadLastPage - should we load the bookmarked last page?
-- minPage - do not go earlier than this page number
-- maxPage - do not go past this page number
-- frameNum - go to this specific frame
-- fadeIn - if true, fade in from a black screen
-- doubleTapped - store flag from saved game so double-tap help message doesn't show up again
-- infoTapped - store flag from saved game so info-tap help message doesn't show up again
--
------------------------------------------------------------
function Reader:initialize( basename, data, startPage, options )
local contentWidth = display.contentWidth
local contentHeight = display.contentHeight
local viewableContentWidth, viewableContentHeight = display.viewableContentWidth, display.viewableContentHeight
local isLandscape = _G.isDeviceLandscape
local frameNum = 0
local fadeIn = false
-- only exchange values if we're not on the simulator. Work-around for Corona bug?
if isLandscape and system.getInfo( "environment" ) ~= "simulator" then
contentWidth,contentHeight = contentHeight, contentWidth
end
self.basename = basename
self.data = data
self.loadLastPage = false
self.minPage, self.maxPage = nil, nil
-- check for passed option parameters
if options then
if options.loadLastPage == true then
self.loadLastPage = true
end
if options.minPage then -- do not go earlier than this page
self.minPage = options.minPage
end
if options.maxPage then -- do not go past this page
self.maxPage = options.maxPage
end
if options.frameNum then -- go to a specific frame
frameNum = options.frameNum
end
if options.fadeIn then -- fade in from black screen
fadeIn = options.fadeIn
end
if options.doubleTapped then -- store whether saved game had double-tapped stored
self.doubleTapped = options.doubleTapped
end
if options.infoTapped then -- store whether saved game had infoTapped stored
self.infoTapped = options.infoTapped
end
end
local view = display.newGroup()
self.view = view
if isLandscape then
view:translate( 0.5*contentHeight, 0.5*contentWidth )
else
view:translate( 0.5*contentWidth, 0.5*contentHeight )
end
-- set up touchable rectangle that covers the screen (for page navigation)
local maxWH = math.max(contentWidth,contentHeight) -- get the widest value of width or height
local fullscreenRect = display.newRect( 0, 0, maxWH, maxWH ) -- wide enough so it works in landscape mode as well!
fullscreenRect.isHitTestable = true
fullscreenRect:setFillColor( 0,0,0 )
fullscreenRect.isVisible = false
fullscreenRect:addEventListener( "touch", self )
view:insert( fullscreenRect, true )
-- store screen size info, and calculate tap margin area
self.screenW = contentWidth
self.screenH = contentHeight
self.maxWH = maxWH
self.margin = math.ceil(maxWH/10)
if _G.iPhone then
self.margin = 10
elseif _G.iPhone4 then
self.margin = 20
end
local book = display.newGroup()
view:insert( book )
local pages = {}
book.pages = pages
if _G.hasBalloons then
local balloons = {}
book.pages.balloons = balloons
end
self.book = book
self.firstOrientationEventSuppressed = nil
local f = Frame:new()
f:initialize()
f:setColor( 0, 0, 0 )
self.frame = f
view:insert( f.view, true )
f.view.alpha = 0
startPage = startPage or 0
self:loadPage( startPage, frameNum, { fadeIn = fadeIn } )
-- adjust screen orientation
self:setOrientation( system.orientation )
end
------------------------------------------------------------
-- Look up and return coordinate data and page number
------------------------------------------------------------
function Reader:loadData( pageNum )
local data = self.data
if pageNum < 0 then
pageNum = #data
end
return data[pageNum], pageNum
end
------------------------------------------------------------
-- Display the Double-Tap message on page 1, along with 'dot' animation
------------------------------------------------------------
local function doubleTapAnimation(page)
local page = Reader.book.current
if page.number == 1 then -- still on page 1?
local balGroup = page.balGroup
local dotImg, doubleTapImg
if not page.doubleTap then
dotImg = display.newImage(balGroup,imgUIDir .. "dot.png",0,0,true)
doubleTapImg = display.newImage(balGroup,imgUIDir .. "double-tap.png",0,0,true)
page.doubleTap = doubleTapImg
page.dot = dotImg
doubleTapImg:setReferencePoint(display.TopLeftReferencePoint)
else
doubleTapImg = page.doubleTap
dotImg = page.dot
end
doubleTapImg.alpha = 0
dotImg.alpha = 0
------------------------------------------------------------
-- Update the position/scale of the double-tap image and dot
-- based on screen size and orientation
------------------------------------------------------------
local function positionDoubleTap()
local balScale = balGroup.yScale
local bottomEdge = display.contentHeight/balScale/2
local doubleTapImgOffsetY = 93/balScale
if _G.isDeviceLandscape then
-- only do this if we're not on the simulator. Work-around for Corona bug?
if system.getInfo( "environment" ) ~= "simulator" then
bottomEdge = display.contentWidth/balScale/2
end
doubleTapImg.xScale = .65
doubleTapImg.yScale = .65
doubleTapImg.x = -370
doubleTapImg.y = bottomEdge-(262*doubleTapImg.yScale) -- actual size of doubleTapImg times scaled size
dotImg.xScale = .75
dotImg.yScale = .75
dotImg.x = doubleTapImg.x+4 --/balScale
dotImg.y = doubleTapImg.y
else
doubleTapImg.x = -400
doubleTapImg.y = 250
doubleTapImg.xScale = 1
doubleTapImg.yScale = 1
dotImg.x = -396
dotImg.y = 250
dotImg.xScale = 1
dotImg.yScale = 1
end
end
timer.performWithDelay(450, positionDoubleTap) -- give it a delay so there's time for the orientation changet to take place
transition.to( doubleTapImg, { delay = 500, alpha = 1, time = 500, transition=easing.inOutQuad } )
for i = 1500,1900,400 do -- two beeps, at 1500 and 1900 ms
timer.performWithDelay(i, function()
if _G.currentPage == 1 and not Reader.doubleTapped then -- still on page 1?
positionDoubleTap() -- update the position/scale of the double-tap image and dot
playSound( sndUIBeep1 )
transition.to( dotImg, { alpha = .6, time = 200, transition=easing.inOutQuad } )
transition.to( dotImg, { delay = 200, alpha = 0, time = 200, transition=easing.inOutQuad } )
end
end )
end
end
end
------------------------------------------------------------
-- Display the Info Button message on page 1, along with 'dot' animation
------------------------------------------------------------
local function infoTapAnimation()
local page = Reader.book.current
if page.number == 1 then -- still on page 1?
local balGroup = page.balGroup
local dotImg, infoTapImg
local animDelay = 500
if not Reader.doubleTapped then animDelay = 2300 end -- don't need to wait for double-tapped animation to finish
if not page.infoTap then
infoTapImg = display.newImage(balGroup,imgUIDir .. "info-tap.png",0,0,true)
dotImg = display.newImage(balGroup,imgUIDir .. "dot.png",0,0,true)
page.infoTap = infoTapImg
page.dot2 = dotImg
infoTapImg:setReferencePoint(display.BottomRightReferencePoint)
dotImg:setReferencePoint(display.CenterReferencePoint)
else
infoTapImg = page.infoTap
dotImg = page.dot2
end
------------------------------------------------------------
-- Update the position/scale of the info-tap image and dot
-- based on screen size and orientation
------------------------------------------------------------
local function positionInfoTap()
local balScale = balGroup.yScale
local bottomEdge = display.contentHeight/balScale/2
local rightEdge = display.contentWidth/balScale/2
local infoTapImgOffsetX = 27/balScale
local infoTapImgOffsetY = 24/balScale
local dotImgOffsetX = 26.3/balScale --/balScale*2
local dotImgOffsetY = 25.9/balScale
-- print("rightEdge, dotImgOffsetX",rightEdge, dotImgOffsetX)
if _G.isDeviceLandscape then
-- only change values if we're not on the simulator. Work-around for Corona bug?
if system.getInfo( "environment" ) ~= "simulator" then
bottomEdge = display.contentWidth/balScale/2
rightEdge = display.contentHeight/balScale/2
end
infoTapImg.x = rightEdge-infoTapImgOffsetX
infoTapImg.y = bottomEdge-infoTapImgOffsetY
infoTapImg.xScale = .65
infoTapImg.yScale = .65
dotImg.x = rightEdge-dotImgOffsetX
dotImg.y = bottomEdge-dotImgOffsetY
dotImg.xScale = .75
dotImg.yScale = .75
else
infoTapImg.x = 480 -- 512 is the right edge
infoTapImg.y = bottomEdge-infoTapImgOffsetY
infoTapImg.xScale = 1
infoTapImg.yScale = 1
dotImg.x = 512-dotImgOffsetX
dotImg.y = bottomEdge-dotImgOffsetY
dotImg.xScale = 1
dotImg.yScale = 1
end
end
infoTapImg.alpha = 0
dotImg.alpha = 0
timer.performWithDelay(animDelay, function()
if Reader.infoTapped or Reader.infoTapSeen then return end -- Info button has been tapped, or message seen no need to display it
if _G.currentPage == 1 and not Reader.infoTapped and _G.showFullPages then
positionInfoTap()
transition.to( infoTapImg, { alpha = 1, time = 500, transition=easing.inOutQuad } )
timer.performWithDelay(1000, function()
positionInfoTap() -- update the position/scale of the info-tap image and dot
if _G.currentPage == 1 and not Reader.infoTapped and _G.showFullPages then -- still on page 1?
Reader.infoTapSeen = true
playSound( sndUIBeep1 )
transition.to( dotImg, { alpha = .6, time = 200, transition=easing.inOutQuad } )
transition.to( dotImg, { delay = 200, alpha = 0, time = 200, transition=easing.inOutQuad } )
else
infoTapImg.alpha = 0
end
end )
end
end )
end
end
------------------------------------------------------------
-- Load a page and its data into memory
-- (or if it's already there, make sure settings are correct)
------------------------------------------------------------
function Reader:loadPage( pageNum, frameNum, options )
local book = self.book
local pages = book.pages
local page = pages[pageNum]
local fadeIn = false
if options then
if options.fadeIn then -- fade in from black screen
fadeIn = options.fadeIn
end
end
_G.currentPage = pageNum -- store in global variable for control issues
_G.currentFrame = 0
if not page or (page and not page.removeSelf) then
-- lazily load page
local data, pageImg
data, pageNum = self:loadData( pageNum )
if data then
page = display.newGroup()
local basename = self.basename .. pageNum
local hires = ""
if _G.hires then -- should we use the hires or lores images?
hires = pageSize[pageNum]
end
pageImg = display.newImage( page, basename .. hires .. ".jpg",0,0,true ) -- insert new page image into page group
pages[pageNum] = page -- add into array of pages
book:insert( page, true )
if math.max(pageImg.width,pageImg.height) > 1024 then
pageImg.xScale = .5
pageImg.yScale = .5
end
pageImg.x = 0
pageImg.y = 0
pageImg.centerX = pageImg.width/2
pageImg.centerY = pageImg.height/2
page.data = data
page.number = pageNum
local sData = sound[pageNum]
page.sound = sData
page.balGroup = display.newGroup()
local balGroup = page.balGroup
view = self.view
view:insert( balGroup, true )
local pageOffsetX, pageOffsetY = pageImg.xScale*pageImg.centerX, pageImg.yScale*pageImg.centerY
if _G.hasBalloons then
local bData = balloonsTable[pageNum]
local balloons = {}
page.balloons = balloons
local balloonSet
if #bData > 1 then -- balloons not used on pages with less than 2 frames
local spriteSize = "@2x" -- for iPad, iPhone 4, NOOKColor
if _G.iPhone then spriteSize = "" end -- use lores spritesheets for iPhone 3
local spriteImg = "Page" .. pageNum .. spriteSize
local spriteFile = spriteImg -- tried putting these files in a separate folder, works fine on simulator, breaks on iPad (build 2011.619)
local balloonData = require(spriteFile)
local spriteData = balloonData.getSpriteSheetData()
local spriteCount = #spriteData.frames
local balloonSprite = sprite.newSpriteSheetFromData( "images/sprites/" .. spriteImg .. ".png", spriteData )
balloonSet = sprite.newSpriteSet(balloonSprite,1,spriteCount)
for i = 1,spriteCount do -- set up pointers to each of the balloons
sprite.add(balloonSet,i,i,1,1,0)
end
page.balloonSprite = balloonSprite -- save handle to sprite sheet so we can dispose it later
end
-- Load balloon data
-- if your graphic novel doesn't use separate balloons, may want to consider rewriting this section
for i = 1,#bData do
local bArray = bData[i]
if #bArray>0 then
local bNum,bX,bY,bW,bH,preScaled = unpack(bArray, 1, 6)
balloons[i] = sprite.newSprite(balloonSet)
balGroup:insert( balloons[i], false )
if not preScaled and not iPhone then -- for balloons that are not going to scale up, we'll use
balloons[i].xScale = .5
balloons[i].yScale = .5
end
balloons[i].x = bX-pageOffsetX + bW/2 --384 --hack because sprite positioning is off
balloons[i].y = bY-pageOffsetY + bH/2 -- 512
balloons[i].bX = bX
balloons[i].bY = bY
balloons[i].bW = bW
balloons[i].bH = bH
balloons[i].bxMax = bX + bW
balloons[i].byMax = bY + bH
balloons[i]:prepare(bNum)
else -- no balloons for this frame, so just load tiny place-holder png.
balloons[i] = display.newImage("images/sprites/null.png")
end
end
end
if fadeIn then
page.alpha = 0
transition.to( page, { alpha=1, time=kTime, transition=easing.inOutQuad } )
balGroup.alpha = 0
if not _G.showFullPages then
transition.to( balGroup, { alpha=1, time=kTime, transition=easing.inOutQuad } )
end
end
-- set up touchable zoom-in rectangles for each frame
local fData = frames[pageNum]
local borderOverlap
for i = 1,#fData do
local fArray = fData[i]
if #fData>0 then
local fX,fY,fW,fH,targetFrame,fURL = unpack(fArray, 1, 6)
if fX then
borderOverlap = border - fX
if borderOverlap > 0 then -- make sure the double-tap region doesn't overlap the page turn region
fX = border
fW = fW - borderOverlap
end
if (fX + fW) > pageImg.width - border then fW = pageImg.width - fX - border end
local tapRect = display.newRect( balGroup, 0, 0, fW, fH )
if targetFrame == nil then
targetFrame = i
end
tapRect.isHitTestable = true
tapRect:setFillColor( 255,0,0,128)
tapRect.isVisible = false
if fURL then -- if there's a URL, store it in the rectangle
if targetFrame == -4 then -- iTunes link, check for platform
if _G.device == "android" then
fURL = "http" .. fURL
targetFrame = -2 -- Android dev can't go into the iTunes store, so switch to standard URL
else
fURL = "itms" .. fURL
end
end
tapRect.URL = fURL
tapRect:addEventListener( "touch", self )
else
if _G.device == "android" then
tapRect:addEventListener( "touch", self )
else
tapRect:addEventListener( "tap", self )
end
end
tapRect:setReferencePoint(display.TopLeftReferencePoint)
tapRect.x = fX-pageOffsetX
tapRect.y = fY-pageOffsetY
tapRect.targetFrame = targetFrame
end
end
end
end
else
page.alpha = 1
page.xOrigin = 0
page.yOrigin = 0
page.xReference = 0
page.yReference = 0
page.balGroup.alpha = 1
page.balGroup.xOrigin = 0
page.balGroup.yOrigin = 0
page.balGroup.xReference = 0
page.balGroup.yReference = 0
end
if ( page ) then
book.current = page
if(frameNum) then
page.index = frameNum
else
page.index = 0 -- show entire page
end
end
return page
end
------------------------------------------------------------
-- Update first frame and on orientation. Play the sound only
-- the first time, otherwise no need to trigger sounds again.
------------------------------------------------------------
function Reader:invalidateFrame( params )
local suppressSound, fadeIn, suppressAnimation = false, false, false
if params then
if params.suppressSound then suppressSound = params.suppressSound end
if params.fadeIn then fadeIn = params.fadeIn end
if params.suppressAnimation then suppressAnimation = params.suppressAnimation end
end
if (self and self.book) then
local page = self.book.current
local index = page.index
if page.number == 0 then
suppressSound = false
end -- don't do this for the start of the book
self:showFrame( index, { orientationChange = true, suppressSound = suppressSound, fadeIn = fadeIn, suppressAnimation = suppressAnimation } )
end
end
------------------------------------------------------------
-- Remove a page from memory, along with its balloons
-- Uncomment code below if you want to watch memory handling.
------------------------------------------------------------
function Reader:removePage( pgNumber, params )
local infoPage = false
if params and params.infoPage then infoPage = true end
if _G.currentPage == pgNumber then return false end
if pgNumber >= 0 and pgNumber < #self.data or infoPage then
-- print( "TextureMemory: " .. system.getInfo("textureMemoryUsed")/1024 .. " Kbytes")
local losePage = self.book.pages[pgNumber]
if losePage and losePage.removeSelf then
-- print( "TextureMemory start: " .. system.getInfo("textureMemoryUsed")/1024 .. " Kbytes" )
-- print("dropping page "..pgNumber)
local loseBalGroup = losePage.balGroup
if _G.hasBalloons then
local loseBalloons = losePage.balloons
local loseSpritesheet = losePage.balloonSprite
for i = #loseBalloons,1,-1 do
loseBalloons[i]:removeSelf()
loseBalloons[i] = nil
end
loseBalloons = nil
if loseSpritesheet then loseSpritesheet:dispose() end
end
cleanGroups(loseBalGroup)
losePage:removeSelf()
losePage = nil
collectgarbage("collect")
-- timer.performWithDelay(1, function()
-- print("collect garbage "..collectgarbage("count"))
-- print( "TextureMemory finished: " .. system.getInfo("textureMemoryUsed")/1024 .. " Kbytes" )
-- for i = 0,#self.data do -- debug: what pages are still in memory?
-- losePage = self.book.pages[i]
-- if losePage and losePage.removeSelf then
-- print("in mem",i)
-- end
-- end
-- end)
end
end
return true
end
------------------------------------------------------------
-- Remove all pages (except current one) - received memory warning
------------------------------------------------------------
function removeAllPages()
-- print("Memory low, removing all pages")
local losePage
for i = 0,#Reader.data do
losePage = Reader.book.pages[i]
if losePage and losePage.removeSelf then
Reader:removePage( i )
end
end
end
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
-- Jump to a page, or move to the next or previous page
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
------------------------------------------------------------
-- Jump to a specific page and Frame:initialize
-- Used from main.lua when we want to jump to a chapter or Return to book
------------------------------------------------------------
function Reader:gotoPage(pageNum, frameNum, options)
local page = self.book.current
local aboutFlag, suppressAnimation = false, false
if options then
if options.minPage then -- do not go earlier than this page
self.minPage = options.minPage
end
if options.maxPage then -- do not go past this page
self.maxPage = options.maxPage
end
if options.aboutFlag then -- currently not being used here
aboutFlag = options.aboutFlag
end
if options.suppressAnimation then
suppressAnimation = options.suppressAnimation
end
end
local balGroup = page.balGroup
local index = 0
if frameNum then index = frameNum end
-- Show entire next page
local newPage = self:loadPage( pageNum )
if newPage then
self:showFrame( index, {suppressAnimation = suppressAnimation} )
else
newPage = self:loadPage( 0 ) -- load cover
self:showFrame( 0 ) -- hide frame
end
local newBalGroup = newPage.balGroup
transition.to( page, { alpha = 0, time=kTime, transition=easing.linear } )
transition.to( balGroup, { alpha = 0, time=kTime, transition=easing.linear } )
transition.from( newPage, { alpha = 0, time=kTime, transition=easing.linear } )
transition.from( newBalGroup, { alpha = 0, time=kTime, transition=easing.linear } )
newPage.index = index
return newPage
end
------------------------------------------------------------
-- Move to the next frame (or page)
-- Called from the touch event
------------------------------------------------------------
function Reader:nextFrame(orientation)
if "portraitUpsideDown" == orientation or "landscapeLeft" == orientation then
self:prevFrame()
return
end
local page = self.book.current
if self.maxPage == page.number then
return
end
frameChangeSound()
local balGroup = page.balGroup
-- If full page mode, always go to the next full page
-- If not full page mode, always go to the next frame.
-- If there is no next frame on the current page, go to the first frame on the next page.
-- If the next page has no frames, just show the full page.
local index = page.index + 1
if not _G.showFullPages and ( self:existsFrame( index ) ) then -- not in full page mode, and there is another frame
self:showFrame( index )
page.index = index
else
-- Show entire next page if not in full page mode
-- if value stays 0, then either we're in full page mode, or there are no frames on page
index = 0
-- remove objects for previous page
Reader:removePage( page.number - 1 )
if _G.device ~= "android" and not _G.liteVer and page.number == _G.promotePriorPage then
-- Check if this is the first time triggered for this pass through the story
-- and show please rate dialogue. If user accepts, send him to rating link.
promote.offerRating(_G.promoteFlag, _G.iTunesRatingURL )
end
local newPage = self:loadPage( page.number + 1 )
if newPage then -- we have a next page
local framesOnNewPage = self:existsFrame( 1 )
if not _G.showFullPages and framesOnNewPage then -- and we have a next frame and not in full page mode
index = 1 -- so show the first frame
end
self:showFrame( index, { newPage = true } ) -- passing flag so balloons turn off immediately
else
newPage = self:loadPage( 0 ) -- load cover
self:showFrame( 0 ) -- hide frame
end
local newBalGroup = newPage.balGroup
local transDelay = 1
transition.to( page, { delay = transDelay, alpha = 0, time=kTime, transition=easing.inOutQuad } )
transition.to( balGroup, { delay = transDelay, alpha = 0, time=kTime, transition=easing.inOutQuad } )
transition.from( newPage, { delay = transDelay, alpha = 0, time=kTime, transition=easing.inOutQuad } )
transition.from( newBalGroup, { delay = transDelay, alpha = 0, time=kTime, transition=easing.inOutQuad } )
newPage.index = index
end
return true
end
------------------------------------------------------------
-- Move to the previous frame (or page)
-- Called from the touch event
------------------------------------------------------------
function Reader:prevFrame(orientation)
if "portraitUpsideDown" == orientation or "landscapeLeft" == orientation then
self:nextFrame()
return
end
local page = self.book.current
--print("self.minPage",self.minPage, page.number)
if self.minPage == page.number then
return
end
frameChangeSound()
local balGroup = page.balGroup
-- If full page mode, always go to the previous full page
-- If not full page mode, always go to the previous frame.
-- If there is no previous frame on the current page, go to the first frame on the previous page.
-- If the previous page has no frames, just show the full page.
local index = page.index - 1
if (not _G.showFullPages and index >=1 and self:existsFrame( index ) ) then
self:showFrame( index )
page.index = index
else
-- Show entire previous page if not in full page mode
index = 0
-- remove objects for previous page
Reader:removePage( page.number + 1 )
local newPage = self:loadPage( page.number - 1 )
if newPage then
if not _G.showFullPages then -- in zoom in mode, so get the last frame of previous page
index = #newPage.data
end
self:showFrame( index, { newPage = true } ) -- passing flag so balloons turn off immediately
else
newPage = self:loadPage( 0 ) -- load cover
self:showFrame( 0 ) -- hide frame
end
local newBalGroup = newPage.balGroup
local transDelay = 1
transition.to( page, { delay = transDelay, alpha = 0, time=kTime, transition=easing.inOutQuad } )
transition.to( balGroup, { delay = transDelay, alpha = 0, time=kTime, transition=easing.inOutQuad } )
transition.from( newPage, { delay = transDelay, alpha = 0, time=kTime, transition=easing.inOutQuad } )
transition.from( newBalGroup, { delay = transDelay, alpha = 0, time=kTime, transition=easing.inOutQuad } )
newPage.index = index
end
return true
end
------------------------------------------------------------
-- Does the requested next/previous frame exist?
------------------------------------------------------------
function Reader:existsFrame( index )
local page = self.book.current
local data = page.data
if ( index <= #data ) then
return true
else
return false
end
end