13 Commits

Author SHA1 Message Date
Squishy (C6H12O6+NaCl+H2O)
c9a97856fd Update virtual control scene 2024-05-29 22:24:48 +07:00
Squishy (C6H12O6+NaCl+H2O)
8f902caa22 Update virtual control texture 2024-05-29 20:32:29 +07:00
Squishy (C6H12O6+NaCl+H2O)
4e0815e13a Updating README file 2024-05-29 15:53:29 +07:00
Squishy (C6H12O6+NaCl+H2O)
c555e98081 Replace font 2024-05-29 15:33:46 +07:00
Squishy (C6H12O6+NaCl+H2O)
fbfd9ee0db Update font related stuff 2024-05-29 10:34:57 +07:00
Squishy (C6H12O6+NaCl+H2O)
3c473fd5f3 convert indents to spaces 2024-05-29 10:01:34 +07:00
Squishy (C6H12O6+NaCl+H2O)
dd55171204 Replacing font, to avoid license issue 2024-05-29 09:56:59 +07:00
Squishy (C6H12O6+NaCl+H2O)
6beacc51f5 Update conf.lua 2024-05-29 00:27:15 +07:00
Squishy (C6H12O6+NaCl+H2O)
c14c702d17 Update touch control 2024-05-29 00:26:05 +07:00
Squishy (C6H12O6+NaCl+H2O)
9679378b1b Add importing and exporting replays 2024-05-29 00:09:48 +07:00
Squishy (C6H12O6+NaCl+H2O)
d7726c1854 Add icon font 2024-05-28 23:43:16 +07:00
Squishy (C6H12O6+NaCl+H2O)
e79bf246bf Update README 2024-05-27 20:24:34 +07:00
SweetSea-ButImNotSweet
7cf450868a Fix virtual button method 2024-05-27 20:18:24 +07:00
24 changed files with 845 additions and 302 deletions

View File

@@ -36,22 +36,22 @@ Ported to Android (mobile and TV) by SweetSea with on-screen control (with some
Navigate to where you put ``tromi_mobile.love`` in the File manager (the one you downloaded from the link in Install for TV section), just opening and Tromi should be launched.
# Differences from original Tromi
> This is required, to follow the used license (GNU GPL v3)
> I must make this list to follow the used license (GNU GPL v3)<br>
> :no_entry: There are ***very much*** breaking changes right now, and I can't always finish this list. I may try hard to do it.
* No differences in gameplay
* Files will be saved into ``Android/data/org.love2d.android/tromi_mobile`` instead the location where the game files in
* Add ``simple-button`` module, made by SweetSea
* Add ``simple-button`` module, made by me (SweetSea)
* All UIs are touch-able
* Add on-screen buttons
* Replaced icons for 3 direction buttons (Left, Down, Right)
* Replaced icons for 3 direction buttons (Left, Down, Right), using from Techmino's font (outdated image)
<img src="https://gitea.com/SweetSea-ButImNotSweet/tromi_mobile/raw/branch/main/screenshot/Replay_screen_differences.png">
* Add a special pre-made keybind for Android TV (only supports TV models have their remote has numerical area (0-9))<img src="https://gitea.com/SweetSea-ButImNotSweet/tromi_mobile/raw/branch/main/screenshot/SPOILER_tv_code.png">
* <details><summary>Changes the way to input secret code to activate Pentominoes mode</summary><img src="https://gitea.com/SweetSea-ButImNotSweet/tromi_mobile/raw/branch/main/screenshot/SPOILER_pento_code.png">To insert the left arrow, tap on the left, so does to right arrow.</details>
* Add a loading screen (this need to be updated later)
* Add a loading screen ~~(this need to be updated later)~~
* Update ``binser`` library, this fixes some weird bugs related to saving
* Replaced old Cambridge's ``config`` module with the new one inspired by "the sequel of Techmino"s ``SETTINGS`` module
# TODO
- [ ] Add a way to export ~~replay~~ data for Android > 11
- [x] Revert ``bitser`` with ``binser`` (if and only if I can make it works)
- [x] Design a new on-screen buttons skin (the current one is come from [C₂₉H₂₅N₃O₅](https://github.com/C29H25N3O5), I am aware that it's not fit to Tromi's design language)
@@ -63,15 +63,15 @@ Please read ``COPYING.txt`` for more information.<br>
A small note about the music:
> Only mycophobia right now having the permission to music from Jerry Martin, I haven't got.<br>
>
>
> I sent an email about this issue (not mentioning about this game), but I haven't got any replies from them (I checked both my main inbox and spam too, nothing)<br>
>
>
> Both me and mycophobia can't give you a sub-license if you ask. What can I do is suggesting you to send an email about this (there is a chance that you may not receive any replies, but it's better than do nothing)
# Special thanks
* mycophobia for writing the original Tromi
* MrZ_26 for the base of ``VCTRL`` module (yea I stole from him his code ;-;)
* C₂₉H₂₅N₃O₅ for his virtual key design, I used it during developing when I hadn't made my own yet
* MrZ_26 for the base of ``VCTRL`` module (yea I stole from him his code ;-; )
* C₂₉H₂₅N₃O₅ for his virtual key design (used while during inital development), and icon font (from Techmino).
# Don't forget to check
* [Original Tromi](https://mycophobia.org/tromi)

415
char.lua Normal file
View File

@@ -0,0 +1,415 @@
local function utf8_convert(n)
local floorint = math.floor
local char = string.char
assert(type(n)=='number',"Wrong type ("..type(n)..")")
assert(n>=0 and n<2^31,"Out of range ("..n..")")
if n<2^7 then return char(n)
elseif n<2^11 then return char(192+floorint(n/2^06),128+n%2^6)
elseif n<2^16 then return char(224+floorint(n/2^12),128+floorint(n/2^06)%2^6,128+n%2^6)
elseif n<2^21 then return char(240+floorint(n/2^18),128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6)
elseif n<2^26 then return char(248+floorint(n/2^24),128+floorint(n/2^18)%2^6,128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6)
elseif n<2^31 then return char(252+floorint(n/2^30),128+floorint(n/2^24)%2^6,128+floorint(n/2^18)%2^6,128+floorint(n/2^12)%2^6,128+floorint(n/2^06)%2^6,128+n%2^6)
end
end
local L={
zChan={-- F0000 - F003F
none= 0xF0000,
normal= 0xF0001,
full= 0xF0002,
happy= 0xF0003,
confused= 0xF0004,
grinning= 0xF0005,
frowning= 0xF0006,
tears= 0xF0007,
anxious= 0xF0008,
rage= 0xF0009,
fear= 0xF000A,
question= 0xF000B,
angry= 0xF000C,
shocked= 0xF000D,
ellipses= 0xF000E,
sweatDrop= 0xF000F,
cry= 0xF0010,
cracked= 0xF0011,
qualified= 0xF0012,
unqualified= 0xF0013,
understand= 0xF0014,
thinking= 0xF0015,
spark= 0xF0016,
},
mino={-- F0040 - F007F
Z=0xF0040,
S=0xF0041,
J=0xF0042,
L=0xF0043,
T=0xF0044,
O=0xF0045,
I=0xF0046,
Z5=0xF0047,
S5=0xF0048,
P= 0xF0049,
Q= 0xF004A,
F= 0xF004B,
E= 0xF004C,
T5=0xF004D,
U= 0xF004E,
V= 0xF004F,
W= 0xF0050,
X= 0xF0051,
J5=0xF0052,
L5=0xF0053,
R= 0xF0054,
Y= 0xF0055,
N= 0xF0056,
H= 0xF0057,
I5=0xF0058,
I3=0xF0059,
C= 0xF005A,
I2=0xF005B,
O1=0xF005C,
},
icon={-- F0080 - F00FF
menu= 0xF0080,
music= 0xF0081,
language= 0xF0082,
back= 0xF0083,
play_pause= 0xF0084,
info= 0xF0085,
help= 0xF0086,
mute= 0xF0087,
volume_up= 0xF0088,
volume_down= 0xF0089,
retry_spin= 0xF008A,
filledLogo= 0xF008B,
hollowLogo= 0xF008C,
toUp= 0xF008D,
toDown= 0xF008E,
toLeft= 0xF008F,
toRight= 0xF0090,
checkMark= 0xF0091,
crossMark= 0xF0092,
musicMark= 0xF0093,
infoMark= 0xF0094,
warnMark= 0xF0095,
console= 0xF0096,
globe= 0xF0097,
video_camera= 0xF0098,
settings= 0xF0099,
mrz= 0xF009A,
apple= 0xF009B,
home= 0xF009C,
cross_thick= 0xF009D,
num0InSpin= 0xF009E,
num1InSpin= 0xF009F,
num2InSpin= 0xF00A0,
num3InSpin= 0xF00A1,
num4InSpin= 0xF00A2,
play= 0xF00A3,
pause= 0xF00A4,
nextFrame= 0xF00A5,
yen= 0xF00A6,
dollar= 0xF00A7,
euro= 0xF00A8,
pound= 0xF00A9,
bitcoin= 0xF00AA,
onebag= 0xF00AB,
export= 0xF00AC,
import= 0xF00AD,
trash= 0xF00AE,
loadOne= 0xF00AF,
saveOne= 0xF00B0,
loadTwo= 0xF00B1,
saveTwo= 0xF00B2,
zBook= 0xF00B3,
rankX= 0xF00B4,
rankU= 0xF00B5,
rankA= 0xF00B6,
rankB= 0xF00B7,
rankC= 0xF00B8,
rankD= 0xF00B9,
rankE= 0xF00BA,
rankF= 0xF00BB,
rankZ= 0xF00BC,
rankS= 0xF00C2,
speedOneEights= 0xF00BD,
speedOneHalf= 0xF00BE,
speedOne= 0xF00BF,
speedTwo= 0xF00C0,
speedFive= 0xF00C1,
bone= 0xF00C3,
invis= 0xF00C4,
bomb= 0xF00C5,
garbage= 0xF00C6,
copy= 0xF00C7,
tas= 0xF00C8,
pencil= 0xF00C9,
magGlass= 0xF00CA,
zoomIn= 0xF00CB,
zoomOut= 0xF00CC,
zoomDefault= 0xF00CD,
share= 0xF00CE,
save= 0xF00CF,
fastForward= 0xF00D0,
rewind= 0xF00D1,
nextSong= 0xF00D2,
previousSong= 0xF00D3,
cycle= 0xF00D4,
cycleOne= 0xF00D5,
cycleOff= 0xF00D6,
random= 0xF00D7,
randomOff= 0xF00D8,
randomAuto= 0xF00D9,
closedCaption= 0xF00DA,
fullBeat= 0xF00DB,
rewind10= 0xF00DC,
rewind30= 0xF00DD,
foward10= 0xF00DE,
foward30= 0xF00DF,
fontUp= 0xF00E0,
fontDown= 0xF00E1,
erase= 0xF00E2,
auto= 0xF00E3,
},
key={-- F0100 - F017F
macCmd= 0xF0100,
macOpt= 0xF0101,
macCtrl= 0xF0102,
shift= 0xF0103,
capsLock= 0xF0104,
enter_or_return= 0xF0105,
backspace= 0xF0106,
clear= 0xF0107,
macFowardDel= 0xF0108,
macEsc= 0xF0109,
macTab= 0xF010A,
fn= 0xF010B,
macHome= 0xF010C,
macEnd= 0xF010D,
macPgup= 0xF010E,
macPgdn= 0xF010F,
macEnter= 0xF0110,
space= 0xF0111,
windows= 0xF0112,
alt= 0xF0113,
ctrl= 0xF0114,
winMenu= 0xF0115,
tab= 0xF0116,
esc= 0xF0117,
up= 0xF0118,
down= 0xF0119,
left= 0xF011A,
right= 0xF011B,
del= 0xF011C,
enterText= 0xF011D,
keyboard= 0xF011E,
macMediaEject= 0xF011F,
isoCtrl= 0xF0120,
isoAlt= 0xF0121,
macHomeAlt= 0xF0122,
macEndAlt= 0xF0123,
macPgupAlt= 0xF0124,
macPgdnAlt= 0xF0125,
iecPower= 0xF0126,
},
controller={-- F0180 - F01FF
xbox= 0xF0180,
lt= 0xF0181,
rt= 0xF0182,
lb= 0xF0183,
rb= 0xF0184,
xboxX= 0xF0185,
xboxY= 0xF0186,
xboxA= 0xF0187,
xboxB= 0xF0188,
joystickL= 0xF0189,
joystickR= 0xF018A,
jsLU= 0xF018B,
jsLD= 0xF018C,
jsLR= 0xF018D,
jsLL= 0xF018E,
jsRU= 0xF018F,
jsRD= 0xF0190,
jsRR= 0xF0191,
jsRL= 0xF0192,
jsLPress= 0xF0193,
jsRPress= 0xF0194,
dpad= 0xF0195,
dpadU= 0xF0196,
dpadD= 0xF0197,
dpadL= 0xF0198,
dpadR= 0xF0199,
xboxView= 0xF019A,
xboxMenu= 0xF019B,
xboxShare= 0xF019C,
xboxConnect= 0xF019D,
ps= 0xF019E,
psTriangle= 0xF019F,
psCircle= 0xF01A0,
psCross= 0xF01A1,
psSquare= 0xF01A2,
psMute= 0xF01A3,
psCreate= 0xF01A4,
psOption= 0xF01A5,
},
mahjong={-- F0200 - F027F
m1= 0xF0200,
m2= 0xF0201,
m3= 0xF0202,
m4= 0xF0203,
m5= 0xF0204,
m6= 0xF0205,
m7= 0xF0206,
m8= 0xF0207,
m9= 0xF0208,
s1= 0xF0209,
s2= 0xF020A,
s3= 0xF020B,
s4= 0xF020C,
s5= 0xF020D,
s6= 0xF020E,
s7= 0xF020F,
s8= 0xF0210,
s9= 0xF0211,
p1= 0xF0212,
p2= 0xF0213,
p3= 0xF0214,
p4= 0xF0215,
p5= 0xF0216,
p6= 0xF0217,
p7= 0xF0218,
p8= 0xF0219,
p9= 0xF021A,
ton= 0xF021B,
nan= 0xF021C,
sha= 0xF021D,
pe= 0xF021E,
chun= 0xF021F,
hatsu= 0xF0220,
haku= 0xF0221,
hatsuAlt= 0xF0222,
hakuAlt= 0xF0223,
haru= 0xF0224,
natsu= 0xF0225,
aki= 0xF0226,
fuyu= 0xF0227,
ume= 0xF0228,
ran= 0xF0229,
kiku= 0xF022A,
take= 0xF022B,
m5Red= 0xF022C,
s5Red= 0xF022D,
p5Red= 0xF022E,
m1Base= 0xF022F,
m2Base= 0xF0230,
m3Base= 0xF0231,
m4Base= 0xF0232,
m5Base= 0xF0233,
m6Base= 0xF0234,
m7Base= 0xF0235,
m8Base= 0xF0236,
m9Base= 0xF0237,
mComb= 0xF0238,
s1Base= 0xF0239,
s1Comb= 0xF023A,
s5Base= 0xF023B,
s5Comb= 0xF023C,
s7Base= 0xF023D,
s7Comb= 0xF023E,
s9Base= 0xF023F,
s9Comb= 0xF0240,
p2Base= 0xF0241,
p2Comb= 0xF0242,
p3Base= 0xF0243,
p3Comb1= 0xF0244,
p3Comb2= 0xF0245,
p4Base= 0xF0246,
p4Comb= 0xF0247,
p5Base= 0xF0248,
p5Comb1= 0xF0249,
p5Comb2= 0xF024A,
p6Base= 0xF024B,
p6Comb= 0xF024C,
p7Base= 0xF024D,
p7Comb= 0xF024E,
p9Base= 0xF024F,
p9Comb1= 0xF0250,
p9Comb2= 0xF0251,
frameComb= 0xF0252,
s1j= 0xF0253,
s1jBase= 0xF0254,
s1jComb= 0xF0255,
},
cards={-- F0300 - F070F
cardBack= 0xF0300,
clubA= 0xF0301,
club2= 0xF0302,
club3= 0xF0303,
club4= 0xF0304,
club5= 0xF0305,
club6= 0xF0306,
club7= 0xF0307,
club8= 0xF0308,
club9= 0xF0309,
club10= 0xF030A,
clubJ= 0xF030B,
clubC= 0xF030C,
clubQ= 0xF030D,
clubK= 0xF030E,
heartA= 0xF0401,
heart2= 0xF0402,
heart3= 0xF0403,
heart4= 0xF0404,
heart5= 0xF0405,
heart6= 0xF0406,
heart7= 0xF0407,
heart8= 0xF0408,
heart9= 0xF0409,
heart10= 0xF040A,
heartJ= 0xF040B,
heartC= 0xF040C,
heartQ= 0xF040D,
heartK= 0xF040E,
diamondA= 0xF0501,
diamond2= 0xF0502,
diamond3= 0xF0503,
diamond4= 0xF0504,
diamond5= 0xF0505,
diamond6= 0xF0506,
diamond7= 0xF0507,
diamond8= 0xF0508,
diamond9= 0xF0509,
diamond10= 0xF050A,
diamondJ= 0xF050B,
diamondC= 0xF050C,
diamondQ= 0xF050D,
diamondK= 0xF050E,
jokerBlack= 0xF050F,
clubA= 0xF0601,
club2= 0xF0602,
club3= 0xF0603,
club4= 0xF0604,
club5= 0xF0605,
club6= 0xF0606,
club7= 0xF0607,
club8= 0xF0608,
club9= 0xF0609,
club10= 0xF060A,
clubJ= 0xF060B,
clubC= 0xF060C,
clubQ= 0xF060D,
clubK= 0xF060E,
jokerWhite= 0xF060F,
}
}
for _,pack in next,L do
for name,unicode in next,pack do
pack[name]=utf8_convert(unicode)
end
end
return L

View File

@@ -1,5 +1,5 @@
function love.conf(t)
t.identity = "tromi_ver3"
t.identity = "tromi_mobile"
t.externalstorage=true
t.console = true

View File

@@ -143,6 +143,15 @@ function drawText(text, x, y, size, orientation, color)
love.graphics.printf(text, x, y, size*2, orientation, nil, 0.5)
end
function drawBoldText(text, x, y, size, orientation, color)
if color == nil then color = {1, 1, 1, 1} end
love.graphics.setFont(FONT_bold)
love.graphics.setColor(0, 0, 0, 0.8)
love.graphics.printf(text, x+1, y+1, size*2, orientation, nil, 0.50)
love.graphics.setColor(color)
love.graphics.printf(text, x, y, size*2, orientation, nil, 0.5)
end
function drawBigText(text, x, y, size, orientation, color)
if color == nil then color = {1, 1, 1, 1} end
love.graphics.setFont(FONT_big)

View File

@@ -13,9 +13,9 @@ local GameMode = Object:extend()
function GameMode:new(player_name, input_file, replay_grade)
VCTRL.toggle(MOBILE and not input_file and not SETTINGS.tvMode)
if player_name == nil then self.training = true else self.training = false end
if player_name == nil then self.training = true else self.training = false end
if input_file ~= nil then
input_file = love.filesystem.newFile(REPLAY_DIR..input_file, 'r'):read()
input_file = love.filesystem.read(REPLAY_DIR..input_file)
input_file = lualzw.decompress(input_file)
local seed = self:getInputPieceSeq(input_file)
self.replay_inputs = self:getReplayInputs(input_file)
@@ -23,10 +23,10 @@ function GameMode:new(player_name, input_file, replay_grade)
self.input_playback = true
self.grade = replay_grade
self.frames = 1
elseif self.training then
player_name = 'TRN'
replay_grade = 'N/A'
self.randomizer = Randomizer(false, nil)
elseif self.training then
player_name = 'TRN'
replay_grade = 'N/A'
self.randomizer = Randomizer(false, nil)
self.input_playback = false
self.frames = 0
else
@@ -64,22 +64,22 @@ function GameMode:new(player_name, input_file, replay_grade)
"9k", "8k", "7k", "6k", "5k", "4k", "3k", "2k", "1k",
"1D", "2D", "3D", "4D", "5D", "6D", "7D", "8D", "9D"
}
self.promo_table = {
self.promo_table = {
26666, 53333, 79999, 106666, 133333, 159999, 186666, 213333, 239999, 266666,
293333, 319999, 346666, 373333, 399999, 426666, 453333, 479999, 506666,
533333, 559999, 586666, 613333, 639999, 666666, 693333, 719999, 719999
}
self.autopromo_table = {
self.autopromo_table = {
79999, 106666, 133333, 159999, 186666, 213333, 239999, 266666, 293333, 319999,
346666, 373333, 399999, 426666, 453333, 479999, 506666, 533333, 559999,
586666, 613333, 639999, 666666, 693333, 719999, 746666, 773333, 1000000
}
self.demo_table = {
}
self.demo_table = {
-1, 13332, 25000, 40000, 50000, 60000, 60000, 120000, 120000, 120000,
180000, 180000, 240000, 240000, 300000, 300000, 360000, 360000, 360000,
420000, 420000, 480000, 480000, 480000, 480000, 540000, 540000, 540000
}
self.speed_divisor = 10000
self.speed_divisor = 10000
self.line_clear_flash = 0
self.lines_cleared = 0
SOUNDS['bgm_firsthalf']:setVolume(0.3)
@@ -89,7 +89,7 @@ function GameMode:new(player_name, input_file, replay_grade)
self.input_saved = false
self.end_grid_clear = false
self.last_active = 0
self.move_count = 0
self.move_count = 0
self.target = 0
self.last_percent = 0
self.total_speed_loss = 0
@@ -162,15 +162,15 @@ end
function GameMode:updateGradeHistory()
if self.grade_score >= self.promo_table[self.grade] then
if (self.grade == 28 and self.grade_history[2] < 4) or self.grade < 28 then self.grade_history[2] = self.grade_history[2] + 1 end
self.point_flash = 60
self.point_flash_color = {0,1,0,1}
elseif self.grade_score <= self.demo_table[self.grade] then
if (self.grade == 1 and self.grade_history[2] > 0) or self.grade > 1 then self.grade_history[2] = self.grade_history[2] - 1 end
self.point_flash = 60
self.point_flash_color = {1,0,0,1}
end
local auto_flag = false
if (self.grade == 28 and self.grade_history[2] < 4) or self.grade < 28 then self.grade_history[2] = self.grade_history[2] + 1 end
self.point_flash = 60
self.point_flash_color = {0,1,0,1}
elseif self.grade_score <= self.demo_table[self.grade] then
if (self.grade == 1 and self.grade_history[2] > 0) or self.grade > 1 then self.grade_history[2] = self.grade_history[2] - 1 end
self.point_flash = 60
self.point_flash_color = {1,0,0,1}
end
local auto_flag = false
while self.grade_score >= self.autopromo_table[self.grade] and self.grade < 28 do
self.grade = self.grade + 1
self.point_flash = 1
@@ -178,7 +178,7 @@ function GameMode:updateGradeHistory()
self.grade_change_color = {0,0,1,1}
self.end_game_sound = "autopromote"
auto_flag = true
end
end
if self.grade_history[2] >= 5 and self.grade < 28 and auto_flag == false then
self.grade = self.grade + 1
self.point_flash = 1
@@ -194,7 +194,7 @@ function GameMode:updateGradeHistory()
end
if self.starting_grade ~= self.grade then
self.grade_history[1] = self.grade
self.grade_history[2] = 2
self.grade_history[2] = 2
end
self.grade_history[4] = self.grade_history[4] + 1
end
@@ -227,31 +227,31 @@ end
function GameMode:getARR() return 1 end
function GameMode:getDropSpeed() return 1 end
function GameMode:getARE()
if self.training then return 20 end
if self.speed_level <= #self.delay_table then return self.delay_table[self.speed_level][2]
if self.training then return 20 end
if self.speed_level <= #self.delay_table then return self.delay_table[self.speed_level][2]
else return self.delay_table[#self.delay_table][2] end
end
function GameMode:getLineARE() return self:getARE() end
function GameMode:getLockDelay()
if self.training then return 99999999999 end
if self.speed_level <= #self.delay_table then return self.delay_table[self.speed_level][3]
if self.training then return 99999999999 end
if self.speed_level <= #self.delay_table then return self.delay_table[self.speed_level][3]
else return self.delay_table[#self.delay_table][3] end
end
function GameMode:getLineClearDelay() return self:getARE() end
function GameMode:getDasLimit()
if self.training then return 8 end
if self.speed_level <= #self.delay_table then return self.delay_table[self.speed_level][4]
if self.training then return 8 end
if self.speed_level <= #self.delay_table then return self.delay_table[self.speed_level][4]
else return self.delay_table[#self.delay_table][4] end
end
function GameMode:getDasCutDelay() return 0 end
function GameMode:getGravity()
if self.training then return 20 end
if self.speed_level <= #self.delay_table then return self.delay_table[self.speed_level][1]
if self.training then return 20 end
if self.speed_level <= #self.delay_table then return self.delay_table[self.speed_level][1]
else return self.delay_table[#self.delay_table][1] end
end
@@ -346,8 +346,8 @@ function GameMode:update(inputs, ruleset)
if self.grade_change_flash > 0 and self.game_over_frames >= 2 then self.grade_change_flash = self.grade_change_flash - 1 end
if self.point_flash > 0 and self.game_over_frames >= 2 then self.point_flash = self.point_flash - 1 end
if self.game_over_frames == 2 then
PlaySEOnce(self.end_game_sound)
end
PlaySEOnce(self.end_game_sound)
end
if self.game_over or self.completed then
self.game_over_frames = self.game_over_frames + 1
if self.game_over_frames == 1 then self:storeInput(inputs) end
@@ -369,24 +369,24 @@ function GameMode:update(inputs, ruleset)
self.up_lock = true
end
if not inputs["up"] then self.up_lock = false end
local dir_list = {"down", "left", "right"}
for i = 1, #dir_list do
if inputs[dir_list[i]] and not table.contains(self.directions_pressed, dir_list[i]) then
table.insert(self.directions_pressed, dir_list[i])
elseif not inputs[dir_list[i]] then
for j=1, #self.directions_pressed do
if self.directions_pressed[j] == dir_list[i] then table.remove(self.directions_pressed, j) end
end
end
end
if #self.directions_pressed > 0 then
for i=1, #self.directions_pressed-1 do
inputs[self.directions_pressed[i]] = false
end
inputs[self.directions_pressed[#self.directions_pressed]] = true
if inputs['left'] then self.lastdir = -1
elseif inputs['right'] then self.lastdir = 1 end
end
local dir_list = {"down", "left", "right"}
for i = 1, #dir_list do
if inputs[dir_list[i]] and not table.contains(self.directions_pressed, dir_list[i]) then
table.insert(self.directions_pressed, dir_list[i])
elseif not inputs[dir_list[i]] then
for j=1, #self.directions_pressed do
if self.directions_pressed[j] == dir_list[i] then table.remove(self.directions_pressed, j) end
end
end
end
if #self.directions_pressed > 0 then
for i=1, #self.directions_pressed-1 do
inputs[self.directions_pressed[i]] = false
end
inputs[self.directions_pressed[#self.directions_pressed]] = true
if inputs['left'] then self.lastdir = -1
elseif inputs['right'] then self.lastdir = 1 end
end
-- advance one frame
@@ -521,7 +521,7 @@ function GameMode:stackQualityCheck()
local stack_clean = self:detectHoles(x, y)
if not stack_clean then
hole_num = hole_num + 1
end
end
end
end
end
@@ -541,26 +541,26 @@ function GameMode:doStackQuality()
if total_speed <= 0 then total_speed = 0.001 end
self.total_speed_loss = total_speed / #self.speed_table
if #self.speed_table == 0 then self.total_speed_loss = 0 end
self.last_holes = contiguous_holes
self.last_holes = contiguous_holes
end
function GameMode:doSpeedCheck()
self.target = self.move_count
local speed = (self.target/self.active_frames)
if speed > 1 then speed = 1 end
table.insert(self.speed_table, 1, speed)
self.target = self.move_count
local speed = (self.target/self.active_frames)
if speed > 1 then speed = 1 end
table.insert(self.speed_table, 1, speed)
while #self.speed_table > 50 do
table.remove(self.speed_table, #self.speed_table)
end
local total_speed = 0
for i=1, #self.speed_table do
total_speed = total_speed + self.speed_table[i]
end
local total_speed = 0
for i=1, #self.speed_table do
total_speed = total_speed + self.speed_table[i]
end
if total_speed <= 0 then total_speed = 0.001 end
self.total_speed_loss = total_speed / #self.speed_table
self.total_speed_loss = total_speed / #self.speed_table
if #self.speed_table == 0 then self.total_speed_loss = 0 end
self.last_active = self.active_frames
self.last_percent = speed
self.last_active = self.active_frames
self.last_percent = speed
self.active_frames = 0
self.move_count = 0
end
@@ -571,7 +571,7 @@ function GameMode:updateScore(cleared_lines)
while cleared_lines+self.total_lines > 300 do
cleared_lines = cleared_lines - 1
end
self.last_speed = math.ceil(self.total_speed_loss * self.bonus_components['speed'] * cleared_lines)
self.last_speed = math.ceil(self.total_speed_loss * self.bonus_components['speed'] * cleared_lines)
self.line_clear_flash = 240
self.lines_cleared = cleared_lines
self.score_to_add = self.lineClearPoints[cleared_lines] + self.last_speed
@@ -590,25 +590,25 @@ function GameMode:advanceOneFrame()
love.audio.stop()
self.audio_stopped = true
end
end
end
if self.training and not SOUNDS['bgm_title']:isPlaying() and SETTINGS["music"] then SOUNDS['bgm_title']:play() end
if not self.training then
if self.nextbgmflag and SOUNDS['bgm_firsthalf']:isPlaying() then
if SOUNDS['bgm_firsthalf']:getVolume() > 0.1 then
SOUNDS['bgm_firsthalf']:setVolume(SOUNDS['bgm_firsthalf']:getVolume()-0.01)
else
SOUNDS['bgm_firsthalf']:stop()
end
end
if self.ready_frames < 1 and not SOUNDS['bgm_firsthalf']:isPlaying() and not SOUNDS['bgm_secondhalf']:isPlaying() and SETTINGS["music"] then
if not self.nextbgmflag then SOUNDS['bgm_firsthalf']:play()
elseif self.total_lines < 296 then SOUNDS['bgm_secondhalf']:play() end
end
if self.total_lines >= 296 then
SOUNDS['bgm_firsthalf']:stop()
SOUNDS['bgm_secondhalf']:stop()
end
end
if not self.training then
if self.nextbgmflag and SOUNDS['bgm_firsthalf']:isPlaying() then
if SOUNDS['bgm_firsthalf']:getVolume() > 0.1 then
SOUNDS['bgm_firsthalf']:setVolume(SOUNDS['bgm_firsthalf']:getVolume()-0.01)
else
SOUNDS['bgm_firsthalf']:stop()
end
end
if self.ready_frames < 1 and not SOUNDS['bgm_firsthalf']:isPlaying() and not SOUNDS['bgm_secondhalf']:isPlaying() and SETTINGS["music"] then
if not self.nextbgmflag then SOUNDS['bgm_firsthalf']:play()
elseif self.total_lines < 296 then SOUNDS['bgm_secondhalf']:play() end
end
if self.total_lines >= 296 then
SOUNDS['bgm_firsthalf']:stop()
SOUNDS['bgm_secondhalf']:stop()
end
end
if self.clear then
self.completed = true
end
@@ -637,29 +637,29 @@ end
function GameMode:onAttemptPieceMove(piece, grid) end
function GameMode:onAttemptPieceRotate(piece, grid) end
function GameMode:onPieceMove(piece, grid, dx)
if not self.moved then
self.move_count = self.move_count + 1
self.moved = true
end
if not self.moved then
self.move_count = self.move_count + 1
self.moved = true
end
end
function GameMode:onPieceRotate(piece, grid, drot)
if not self.moved then
self.move_count = self.move_count + 1
self.moved = true
end
if not self.moved then
self.move_count = self.move_count + 1
self.moved = true
end
end
function GameMode:onPieceDrop(piece, grid, dy)
if not self.moved then
self.move_count = self.move_count + 1
self.moved = true
end
if not self.moved then
self.move_count = self.move_count + 1
self.moved = true
end
end
function GameMode:onPieceLock(piece, cleared_row_count)
if not self.moved then
self.move_count = self.move_count + 1
self.moved = true
end
self.lastdir = 0
if not self.moved then
self.move_count = self.move_count + 1
self.moved = true
end
self.lastdir = 0
PlaySE("lock")
end
@@ -677,9 +677,10 @@ function GameMode:onPieceEnter()
end
function GameMode:onGameOver()
if not self.training then VCTRL.toggle(false) end
if not self.input_playback and not self.training and not PENTO_MODE then
if not self.did_grades then
self.grade_score = self.grade_score + self.speed_level
self.grade_score = self.grade_score + self.speed_level
if #self.speed_table >= 49 then
self:updateGradeHistory()
end
@@ -689,16 +690,16 @@ function GameMode:onGameOver()
end
self:drawEndScoringInfo()
elseif not self.did_grades then
self.grade_score = self.grade_score + self.speed_level
self.did_grades = true
end
self.grade_score = self.grade_score + self.speed_level
self.did_grades = true
end
if PENTO_MODE and not self.training then self:drawEndScoringInfo() end
end
function GameMode:drawEndScoringInfo()
love.graphics.setColor(1, 1, 1, 1)
drawText("Score: ", 247, 135, 1000, "left")
drawBigText(string.format("%s", self.grade_score), 247, 150, 100, "center")
drawText("Score: ", 247, 135, 1000, "left")
drawBigText(string.format("%s", self.grade_score), 247, 150, 100, "center")
if not PENTO_MODE then
drawText("Best scores:", 247, 220, 1000, "left")
@@ -868,10 +869,10 @@ function GameMode:drawLineClearAnimation()
fade_timer = self.lcd/20
love.graphics.setColor(1,1,1,fade_timer)
love.graphics.draw(BLOCKS[block.skin][block.colour..'_d'], real_x, real_y+fall_timer)
if self.lcd > self:getLineClearDelay() - 5 then
love.graphics.setColor(1,1,1,fade_timer*0.3)
love.graphics.draw(BLOCKS[block.skin]['W'], real_x, real_y+fall_timer)
end
if self.lcd > self:getLineClearDelay() - 5 then
love.graphics.setColor(1,1,1,fade_timer*0.3)
love.graphics.draw(BLOCKS[block.skin]['W'], real_x, real_y+fall_timer)
end
end
end
end
@@ -920,61 +921,61 @@ function GameMode:drawGrid()
end
function GameMode:drawInputDisplay(left, top)
if self.replay_inputs[self.frames] ~= nil then
drawText("+", left+10, top+8, 1000, "left")
drawText("", left+10, top+18, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['down']),1,1-boolToInt(self.replay_inputs[self.frames]['down']),1})
drawText("", left, top+8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['left']),1,1-boolToInt(self.replay_inputs[self.frames]['left']),1})
drawText("", left+20, top+8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['right']),1,1-boolToInt(self.replay_inputs[self.frames]['right']),1})
drawText("L", left+35, top+8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['rotate_left']),1,1-boolToInt(self.replay_inputs[self.frames]['rotate_left']),1})
drawText("R", left+50, top+8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['rotate_right']),1,1-boolToInt(self.replay_inputs[self.frames]['rotate_right']),1})
drawText("L", left+65, top+8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['rotate_left2']),1,1-boolToInt(self.replay_inputs[self.frames]['rotate_left2']),1})
drawText("R", left+80, top+8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['rotate_right2']),1,1-boolToInt(self.replay_inputs[self.frames]['rotate_right2']),1})
else
drawText("+", left+10, top+8, 1000, "left")
drawText("", left+10, top+18, 1000, "left")
drawText("", left, top+8, 1000, "left")
drawText("", left+20, top+8, 1000, "left")
drawText("L", left+35, top+8, 1000, "left")
drawText("R", left+50, top+8, 1000, "left")
drawText("L", left+65, top+8, 1000, "left")
drawText("R", left+80, top+8, 1000, "left")
end
if self.replay_inputs[self.frames] ~= nil then
drawText("", left+7.5, top+ 8, 1000, "left")
drawText(CHAR.key.down , left+ 5, top+18, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['down']),1,1-boolToInt(self.replay_inputs[self.frames]['down']),1})
drawText(CHAR.key.left , left- 5, top+ 8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['left']),1,1-boolToInt(self.replay_inputs[self.frames]['left']),1})
drawText(CHAR.key.right, left+ 15, top+ 8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['right']),1,1-boolToInt(self.replay_inputs[self.frames]['right']),1})
drawText("L", left+ 35, top+ 8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['rotate_left']),1,1-boolToInt(self.replay_inputs[self.frames]['rotate_left']),1})
drawText("R", left+ 50, top+ 8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['rotate_right']),1,1-boolToInt(self.replay_inputs[self.frames]['rotate_right']),1})
drawText("L", left+ 65, top+ 8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['rotate_left2']),1,1-boolToInt(self.replay_inputs[self.frames]['rotate_left2']),1})
drawText("R", left+ 80, top+ 8, 1000, "left",{1-boolToInt(self.replay_inputs[self.frames]['rotate_right2']),1,1-boolToInt(self.replay_inputs[self.frames]['rotate_right2']),1})
else
drawText("" , left+7.5, top+ 8, 1000, "left")
drawText(CHAR.key.down , left+ 5, top+18, 1000)
drawText(CHAR.key.left , left- 5, top+ 8, 1000)
drawText(CHAR.key.right, left+ 15, top+ 8, 1000)
drawText("L" , left+ 35, top+ 8, 1000, "left")
drawText("R" , left+ 50, top+ 8, 1000, "left")
drawText("L" , left+ 65, top+ 8, 1000, "left")
drawText("R" , left+ 80, top+ 8, 1000, "left")
end
end
function GameMode:drawSpeedStats(left, top)
love.graphics.setColor(0,0,0,0.5)
love.graphics.rectangle("fill", left+3, top+3, 190, 145, 10, 10)
love.graphics.setColor(0.05,0.05,0.05,1)
love.graphics.rectangle("fill", left, top, 190, 145, 10, 10)
drawText("Efficiency Bonus: ", left+15, top+10, 1000, "left")
love.graphics.setColor(0,0,0,0.5)
love.graphics.rectangle("fill", left+3, top+3, 190, 145, 10, 10)
love.graphics.setColor(0.05,0.05,0.05,1)
love.graphics.rectangle("fill", left, top, 190, 145, 10, 10)
drawText("Efficiency Bonus: ", left+15, top+5, 1000, "left")
local lines = self.total_lines
if lines == 0 then lines = 1 end
if self.move_count == 0 then
drawText(string.format(" %4d Num. of Moves", self.target), left+15, top+25, 1000, "left")
else
drawText(string.format(" %4d Num. of Moves", self.move_count), left+15, top+25, 1000, "left")
end
drawText(string.format("/ %4d Active Frames", self.last_active), left+15, top+40, 1000, "left")
drawText(string.format("= %1.2f\n (0 added for hole)\n %1.2f %dpc Average\nx %s x Lines\n+ %4d", self.last_percent, self.total_speed_loss, #self.speed_table, self.bonus_components['speed'], self.last_speed), left+15, top+55, 1000, "left")
if lines == 0 then lines = 1 end
if self.move_count == 0 then
drawText(string.format(" %4d Num. of Moves", self.target), left+15, top+20, 1000, "left")
else
drawText(string.format(" %4d Num. of Moves", self.move_count), left+15, top+20, 1000, "left")
end
drawText(string.format("/ %4d Active Frames", self.last_active), left+15, top+35, 1000, "left")
drawText(string.format("= %1.2f\n (0 added for hole)\n %1.2f %dpc Average\nx %s x Lines\n+ %4d", self.last_percent, self.total_speed_loss, #self.speed_table, self.bonus_components['speed'], self.last_speed), left+15, top+50, 1000, "left")
end
function GameMode:drawLinesStats(left, top)
love.graphics.setColor(0,0,0,0.5)
love.graphics.rectangle("fill", left+3, top+3, 190, 90, 10, 10)
love.graphics.setColor(0.05,0.05,0.05,1)
love.graphics.rectangle("fill", left, top, 190, 90, 10, 10)
love.graphics.setColor(0,0,0,0.5)
love.graphics.rectangle("fill", left+3, top+3, 190, 90, 10, 10)
love.graphics.setColor(0.05,0.05,0.05,1)
love.graphics.rectangle("fill", left, top, 190, 90, 10, 10)
local lines = self.total_lines
if lines == 0 then lines = 1 end
if lines == 0 then lines = 1 end
drawText("Lines Bonus: ", left+15, top+10, 1000, "left")
-- drawText(string.format("+ %4d %3d%%", self.lineClearPoints[self.lines_cleared], (self.score_totals['lines']/(self.lineClearPoints[4]*math.ceil(lines/4)))*100), left+15, top+25, 1000, "left")
drawText(string.format("2 x Lines = %d\n3 x Lines = %d\n4 x Lines = %d", self.lineClearPoints[2], self.lineClearPoints[3], self.lineClearPoints[4]), left+15, top+25, 1000, "left")
-- drawText(string.format("+ %4d %3d%%", self.lineClearPoints[self.lines_cleared], (self.score_totals['lines']/(self.lineClearPoints[4]*math.ceil(lines/4)))*100), left+15, top+25, 1000, "left")
drawText(string.format("2 x Lines = %d\n3 x Lines = %d\n4 x Lines = %d", self.lineClearPoints[2], self.lineClearPoints[3], self.lineClearPoints[4]), left+15, top+25, 1000, "left")
end
function GameMode:drawScoringInfo()
-- Name & Grade
love.graphics.setColor(0,0,0,0.5)
love.graphics.rectangle("fill", 98, 83, 110, 180, 10, 10)
love.graphics.setColor(0.05,0.05,0.05,1)
love.graphics.setColor(0.05,0.05,0.05,1)
love.graphics.rectangle("fill", 95, 80, 110, 180, 10, 10)
if not PENTO_MODE then drawText("Grade:", 100, 128, 1000, "left") end
-- Line & Level
@@ -987,46 +988,46 @@ function GameMode:drawScoringInfo()
end
-- REPLAY
if self.input_playback then
love.graphics.setColor(0,0,0,0.5)
love.graphics.rectangle("fill", 68, 270, 140, 190, 10, 10)
love.graphics.setColor(0.05,0.05,0.05,1)
love.graphics.rectangle("fill", 65, 267, 140, 190, 10, 10)
drawText(string.format("Replay in progress\n\n\n\n\n\n\n\n\n%s", formatTime(self.frames)), 70, 275, 1000, "left")
love.graphics.setColor(0,0,0,0.5)
love.graphics.rectangle("fill", 68, 270, 140, 190, 10, 10)
love.graphics.setColor(0.05,0.05,0.05,1)
love.graphics.rectangle("fill", 65, 267, 140, 190, 10, 10)
drawBoldText(string.format("REPLAY IN PROGRESS\n\n\n\n\n\n\n\n\n%s", formatTime(self.frames)), 70, 275, 1000, "left")
drawBigText(string.format("%s", self.grade), 100, 143, 1000, "left")
self:drawInputDisplay(103,185)
elseif not PENTO_MODE then
if math.mod(self.grade_change_flash, 5) ~= 0 then
drawBigText(string.format("%s", self.gradeNames[self.grade]), 100, 143, 1000, "left", self.grade_change_color)
else
drawBigText(string.format("%s", self.gradeNames[self.grade]), 100, 143, 1000, "left")
end
local points_text = nil
elseif not PENTO_MODE then
if math.mod(self.grade_change_flash, 5) ~= 0 then
drawBigText(string.format("%s", self.gradeNames[self.grade]), 100, 143, 1000, "left", self.grade_change_color)
else
drawBigText(string.format("%s", self.gradeNames[self.grade]), 100, 143, 1000, "left")
end
local points_text = nil
if self.grade == 1 and self.grade_history[2] == 2 then
points_text = " |.."
elseif self.grade == 1 and self.grade_history[2] == 3 then
points_text = " .|."
elseif self.grade == 1 and self.grade_history[2] == 4 then
points_text = " ..|"
elseif self.grade_history[2] == 0 then
points_text = "|...."
elseif self.grade_history[2] == 1 then
points_text = ".|..."
elseif self.grade_history[2] == 2 then
points_text = "..|.."
elseif self.grade_history[2] == 3 then
points_text = "...|."
elseif self.grade_history[2] == 4 then
points_text = "....|"
end
if self.grade > 1 then points_text = '-'..points_text
else points_text = ' '..points_text end
if self.grade < 28 then points_text = points_text..'+' end
drawText("Promotion\nMeter:", 100, 174, 1000, "left")
if self.point_flash > 0 then
drawBigText(points_text, 100, 208, 1000, "left", self.point_flash_color)
else
drawBigText(points_text, 100, 208, 1000, "left")
end
elseif self.grade_history[2] == 0 then
points_text = "|...."
elseif self.grade_history[2] == 1 then
points_text = ".|..."
elseif self.grade_history[2] == 2 then
points_text = "..|.."
elseif self.grade_history[2] == 3 then
points_text = "...|."
elseif self.grade_history[2] == 4 then
points_text = "....|"
end
if self.grade > 1 then points_text = '-'..points_text
else points_text = ' '..points_text end
if self.grade < 28 then points_text = points_text..'+' end
drawText("Promotion\nMeter:", 100, 174, 1000, "left")
if self.point_flash > 0 then
drawBigText(points_text, 100, 208, 1000, "left", self.point_flash_color)
else
drawBigText(points_text, 100, 208, 1000, "left")
end
end
if (self.game_over or self.completed) and self.game_over_frames <= 50 and not self.input_playback and not self.training and not PENTO_MODE then
drawText("SAVING, PLEASE WAIT", 232, 460, 1000, "left")
@@ -1035,8 +1036,8 @@ function GameMode:drawScoringInfo()
drawBigText(self.player_name:upper(), 100, 98, 1000, "left")
drawText("Ver. 2", 550, 435, 1000, "left")
if self.input_playback then
self:drawSpeedStats(385, 99)
self:drawLinesStats(385, 251)
self:drawSpeedStats(385, 99)
self:drawLinesStats(385, 251)
love.graphics.setColor(0,0,0,0.5)
love.graphics.rectangle("fill", 385+3, 348+3, 190, 50, 10, 10)
love.graphics.setColor(0.05,0.05,0.05,1)
@@ -1065,17 +1066,17 @@ function GameMode:drawBackground()
if BACKGROUNDS[bg]:tell() >= limit then
BACKGROUNDS[bg]:rewind()
end
if bg == 0 or bg == 8 or bg == 9 or bg == 3 then brightness = 0.7 end
love.graphics.setColor(brightness, brightness, brightness, 1)
if bg == 0 or bg == 8 or bg == 9 or bg == 3 then brightness = 0.7 end
love.graphics.setColor(brightness, brightness, brightness, 1)
love.graphics.draw(BACKGROUNDS[bg])
end
function GameMode:drawFrame()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.line(216,80,216,80+(16*self.grid.height))
love.graphics.line(216+(16*self.grid.width),80,216+(16*self.grid.width),80+(16*self.grid.height))
love.graphics.line(216,80+(16*self.grid.height),216+(16*self.grid.width),80+(16*self.grid.height))
love.graphics.line(216,80,216+(16*self.grid.width),80)
love.graphics.setColor(1, 1, 1, 1)
love.graphics.line(216,80,216,80+(16*self.grid.height))
love.graphics.line(216+(16*self.grid.width),80,216+(16*self.grid.width),80+(16*self.grid.height))
love.graphics.line(216,80+(16*self.grid.height),216+(16*self.grid.width),80+(16*self.grid.height))
love.graphics.line(216,80,216+(16*self.grid.width),80)
love.graphics.setColor(0, 0, 0, 1)
love.graphics.rectangle(
"fill", 216, 80,

View File

@@ -64,7 +64,7 @@ function control_type.button:new(data)
r=data.r or 80, -- size
shape=data.shape or 'circle',
key=data.key or 'X',
iconSize=data.iconSize or 80,
iconSize=data.iconSize or 60,
alpha=data.alpha or 0.75,
quad=virtual_quad[data.key]
},self)
@@ -148,6 +148,7 @@ local touches={}
local global_toggle=false
VCTRL={}
VCTRL.focus=nil -- Focusing buttons
VCTRL.hasChanged = false
---@class VCTRL.data
---@field type 'button'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -1,7 +1,8 @@
-- SIMPLE-BUTTON.lua
-- A simple module that aims to help you quickly create buttons
-- It is can be used as a base class to help you quickly creating button
-- This module has type notations so IntelliSense should give you some suggestions
-- SIMPLE-BUTTON.lua<br>
-- A simple module that aims to help you quickly create buttons<br>
-- It is can be used as a base class to help you quickly creating button<br>
-- This module has type notations so IntelliSense should give you some suggestions<br>
local BUTTON = {}
-- MIT License
@@ -174,9 +175,6 @@ function button:release(x, y, touchID)
end
end
local BUTTON = {}
---@param D BUTTON.button|BUTTON.newData
---@param safe? boolean @ Creating widget? If not then ignore accept missing important parameters
---@return nil

View File

@@ -1,6 +1,12 @@
-- Fonts
FONT_tromi = love.graphics.newFont('res/fonts/monofonto rg.otf', 28)
FONT_big = love.graphics.newFont('res/fonts/monofonto rg.otf', 56)
FONT_tromi = love.graphics.newFont('res/fonts/Iosevka-Bold.ttf' , 28)
FONT_big = love.graphics.newFont('res/fonts/Iosevka-Heavy.ttf', 56)
FONT_bold = love.graphics.newFont('res/fonts/Iosevka-Heavy.ttf', 28)
-- Icons
FONT_tromi:setFallbacks(love.graphics.newFont('res/fonts/techmino_proportional.otf', 28))
FONT_bold :setFallbacks(love.graphics.newFont('res/fonts/techmino_proportional.otf', 28))
FONT_big :setFallbacks(love.graphics.newFont('res/fonts/techmino_proportional.otf', 56))
CHAR = require("char")
local font_height = FONT_tromi:getHeight() * 0.5
local font_big_height = FONT_big:getHeight() * 0.5
@@ -15,7 +21,7 @@ BUTTON = require "libs.simple-button"
BUTTON.setDefaultOption{
draw = function(self)
local need_big_font = (self.font == FONT_big)
love.graphics.setColor(self.backgroundColor)
love.graphics.rectangle('fill', self.x, self.y, self.w, self.h, self.r)
@@ -31,22 +37,19 @@ BUTTON.setDefaultOption{
local lineAmount
do
local _, t
if need_big_font then
_, t = FONT_big:getWrap(text, (self.w - 5) * 2)
else
_, t = FONT_tromi:getWrap(text, (self.w - 5) * 2)
end
local _, t = self.font:getWrap(text, (self.w - 5) * 2)
lineAmount = #t
end
local _font_height = need_big_font and font_big_height or font_height
local textHeight = _font_height * (lineAmount * 0.5)
local textPos = self.y + (self.h * 0.5) - textHeight
if need_big_font then
if self.font == FONT_big then
drawBigText(text, self.x + 2.5, textPos, self.w - 5, self.textOrientation, self.textColor)
elseif self.font == FONT_bold then
drawBoldText(text, self.x + 2.5, textPos, self.w - 5, self.textOrientation, self.textColor)
else
drawText(text, self.x + 2.5, textPos, self.w - 5, self.textOrientation, self.textColor)
end

View File

@@ -52,9 +52,11 @@ function love.load()
require "game.vctrl" -- VCTRL
function SCENE.update()
SCENE.update = function() end
SCENE = SETTINGS.firstTime and InputConfigScene(true) or TitleScene()
end
function SCENE.render()
SCENE.render = function() end
love.graphics.draw(LOADING_IMAGE_FILE,0,0,0,0.5)
end

View File

@@ -35,6 +35,7 @@ function SCENE:onInputPress(e) end
function SCENE:onInputRelease(e) end
GameScene = require "scene.game"
TrainingScene = require "scene.training"
NameEntryScene = require "scene.name_entry"
KeyConfigScene = require "scene.key_config"
@@ -44,7 +45,7 @@ TouchConfigPreviewScene = require "scene.touch_config_preview"
InputConfigScene = require "scene.input_config"
ReplaySelectScene = require "scene.replay"
TrainingScene = require "scene.training"
ReplayTestScene = require"scene.replay_test"
FullscreenScene = require "scene.fullscreen"
MusicToggleScene = require "scene.music_toggle"

BIN
res/fonts/Iosevka-Bold.ttf Normal file

Binary file not shown.

BIN
res/fonts/Iosevka-Heavy.ttf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -45,6 +45,8 @@ local buttonList = {
local menuKey -- MENU key used to go main menu XD
function GameScene:new(player_name, replay_file, replay_grade)
VCTRL[9].show = false
menuKey = BUTTON.new{
text = "MENU",
x = 265, y = 0, w = 60, h = 25,
@@ -130,7 +132,7 @@ end
function GameScene:onInputPress(e)
if e.type == "mouse" or (e.type == "touch" and not VCTRL.press(e.x, e.y, e.id)) then
BUTTON.press(buttonList, e.x, e.y, e.id)
if self.game.input_playback then BUTTON.press(buttonList, e.x, e.y, e.id) end
menuKey:press(e.x, e.y, e.id)
elseif (self.game.game_over or self.game.completed) and (e.input == "menu_decide" or e.input == "menu_back" or e.input == "rotate_right") and self.game.game_over_frames > 50 then
SCENE = TitleScene()
@@ -171,7 +173,7 @@ end
function GameScene:onInputRelease(e)
if e.type == "mouse" or (e.type == "touch" and not VCTRL.release(e.id)) then
BUTTON.release(buttonList, e.x, e.y, e.id)
if self.game.input_playback then BUTTON.release(buttonList, e.x, e.y, e.id) end
menuKey:release(e.x, e.y, e.id)
elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then
self.inputs[e.input] = false

View File

@@ -47,7 +47,7 @@ local function updateButtonList(self)
if not SETTINGS.firstTime then
menu_screens[4] = TitleScene
buttonList[4] = BUTTON.new{
text = "", font = FONT_big,
text = CHAR.icon.home, font = FONT_big,
x = 75, y = 280, w = 40, h = 40,
codeWhenReleased = function()
if self.menu_state ~= 4 then
@@ -70,7 +70,7 @@ function ConfigScene:new(first_time)
updateButtonList(self)
secret_code_used = false
secret_code_input = {} -- When it matches 79338732 then we will automatically set the special keybind
secret_code_input = {} -- When it matches 88663366 then we will automatically set the special keybind
self.menu_state = 1
if first_time then
@@ -88,10 +88,10 @@ function ConfigScene:render()
if SETTINGS.tvMode then
drawText("TV mode is ON now! Check keybind below", 80, 40, 1000)
drawText("Which controls do you want to configure?", 80, 70, 1000)
drawText(
"2 - Up 1 - Rotate left 5 - Confirm selection\n"..
"8 - Right 3 - Rotate right 0 - Back\n"..
"4 - Left 7 - Rotate left 2\n"..
drawText(
"2 - Up 1 - Rotate left 5 - Confirm selection\n"..
"8 - Right 3 - Rotate right 0 - Back\n"..
"4 - Left 7 - Rotate left 2\n"..
"6 - Down 9 - Rotate right 2", 80, 350, 1000
)
else
@@ -128,7 +128,7 @@ function ConfigScene:onInputMove(e)
end
---@param key string
local function checkSecretCodeInput(self, key)
local function checkSecretCodeInput(key)
if secret_code_used then return end
if key:sub(1, 2) == "kp" then
table.insert(secret_code_input, key:sub(3,3))
@@ -160,7 +160,7 @@ local function checkSecretCodeInput(self, key)
SETTINGS.firstTime = false
SETTINGS.tvMode = true
secret_code_used = true
updateButtonList(self)
updateButtonList(SCENE)
elseif current_code == "........" then
SETTINGS.input.keys = {}
SETTINGS.tvMode = false
@@ -183,7 +183,7 @@ function ConfigScene:onInputPress(e)
) then
SCENE = TitleScene()
end
checkSecretCodeInput(self, e.key or "")
checkSecretCodeInput(e.key or "")
end
function ConfigScene:onInputRelease(e)
if e.type == "touch" or e.type == "mouse" then

View File

@@ -4,29 +4,41 @@ ReplaySelectScene.title = "Replay"
local replay_list
local buttonList = {
BUTTON.new{
text = "\nUP", font = FONT_big,
text = CHAR.key.up.."\nUP", font = FONT_big,
x = 425, y = 80, w = 80, h = 80,
codeWhenPressed = function() SCENE:onInputPress {input = "up"} end,
codeWhenReleased = function() SCENE:onInputRelease{input = "up"} end
},
BUTTON.new{
text = "\nDOWN", font = FONT_big,
text = CHAR.key.down.."\nDOWN", font = FONT_big,
x = 425, y = 240, w = 80, h = 80,
codeWhenPressed = function() SCENE:onInputPress {input = "down"} end,
codeWhenReleased = function() SCENE:onInputRelease{input = "down"} end
},
BUTTON.new{
text = "\nPLAY", font = FONT_big,
text = CHAR.icon.play.."\nPLAY", font = FONT_big,
x = 345, y = 160, w = 80, h = 80,
codeWhenPressed = function() SCENE:onInputPress {input = "menu_decide"} end,
codeWhenReleased = function() SCENE:onInputRelease{input = "menu_decide"} end
},
BUTTON.new{
text = "\nHOME", font = FONT_big,
text = CHAR.icon.home.."\nHOME", font = FONT_big,
x = 505, y = 160, w = 80, h = 80,
codeWhenPressed = function() SCENE:onInputPress {input = "menu_back"} end,
codeWhenReleased = function() SCENE:onInputRelease{input = "menu_back"} end
},
BUTTON.new{
text = CHAR.icon.export.."\nEXP.", font = FONT_big,
x = 345, y = 320, w = 80, h = 80,
codeWhenPressed = function() SCENE:onInputPress {input = "rotate_left"} end,
codeWhenReleased = function() SCENE:onInputRelease{input = "rotate_left"} end
},
BUTTON.new{
text = CHAR.icon.import.."\nIMP.", font = FONT_big,
x = 505, y = 320, w = 80, h = 80,
codeWhenPressed = function() SCENE:onInputPress {input = "rotate_right"} end,
codeWhenReleased = function() SCENE:onInputRelease{input = "rotate_right"} end
},
}
function ReplaySelectScene:new()
@@ -135,7 +147,7 @@ function ReplaySelectScene:onInputPress(e)
if e.type == "touch" or e.type == "mouse" then
BUTTON.press(buttonList, e.x, e.y, e.id)
elseif e.input == "menu_decide" or e.input == "rotate_left" or e.scancode == "return" then
elseif e.input == "menu_decide" or e.scancode == "return" then
if self.replays[1] == nil then SCENE = TitleScene(); return
else
local line_components = {}
@@ -146,15 +158,30 @@ function ReplaySelectScene:onInputPress(e)
local player_grade = string.gsub(line_components[2], " ", "")
SCENE = GameScene(player_name, selected_replay, player_grade)
end
-- elseif e.input == "rotate_right" then -- Will add in future, not now
-- love.system.setClipboardText(love.data.encode("string", "base64", love.filesystem.read('saves/replays/'..selected_replay)))
elseif e.input == "rotate_left" then -- Export
local plain_replay_data = love.data.encode("string", "base64", love.filesystem.read(REPLAY_DIR..selected_replay))
love.system.setClipboardText(("BEGINNING_OF_TROMI_REPLAY|%s|%s|END_OF_TROMI_REPLAY"):format(selected_replay, plain_replay_data))
elseif e.input == "rotate_right" then -- Import
local input_data = love.system.getClipboardText()
if input_data:find("BEGINNING_OF_TROMI_REPLAY|", 1, true) == 1 and input_data:find("|END_OF_TROMI_REPLAY", 1, true) == #input_data - 19 then
local data_part = input_data:sub(27, #input_data - 20)
local separator_pos = data_part:find("|", 1, true)
local replay_name = data_part:sub(1, separator_pos - 1)
local replay_path = REPLAY_DIR..replay_name
local replay_data = love.data.decode("string", "base64", data_part:sub(separator_pos + 1))
love.filesystem.write(replay_path, replay_data)
SCENE = ReplayTestScene(replay_name)
else
SCENE = ReplayTestScene()
end
elseif e.input == "up" or e.scancode == "up" then
if self.replays[1] == nil then SCENE = TitleScene(); return end
self.direction = 'up'
elseif e.input == "down" or e.scancode == "down" then
if self.replays[1] == nil then SCENE = TitleScene(); return end
self.direction = 'down'
elseif e.input == "menu_back" or e.input == "rotate_right" or e.scancode == "backspace" or e.scancode == "delete" then
elseif e.input == "menu_back" or e.scancode == "backspace" or e.scancode == "delete" then
SCENE = TitleScene()
end
end

51
scene/replay_test.lua Normal file
View File

@@ -0,0 +1,51 @@
local ReplayTestScene = SCENE:extend()
local GAME
local error_message
local valid_data
function ReplayTestScene:new(input_file)
if not input_file then
valid_data = false
return
else
valid_data = true
end
self.input_file = input_file
GAME = require("game.gamemode")
local okay, err = pcall(function()
GAME:new("TRO", input_file, "19k")
GAME:initialize(require"game.rotation")
end)
if not okay then
-- TODO
error_message = err
love.filesystem.remove(REPLAY_DIR..self.input_file)
end
end
function ReplayTestScene:render()
MainBackground()
if valid_data then
if error_message then
drawText("Replay test failed! Data corrupted!", 80, 40, 1000)
drawText("Press any key to go back. Anyway here is the error info:", 80, 70, 1000)
drawText(error_message, 80, 100, 560)
else
drawText("Replay test finished!", 80, 40, 1000)
drawText("Your replay was imported. Press any key to go back.", 80, 70, 1000)
end
else
drawText("Replay test failed! Not Tromi's replay data", 80, 40, 1000)
drawText("Press any key to go back, and check your device's clipboard again!", 80, 100, 1000)
end
end
function ReplayTestScene:onInputPress()
SCENE = ReplaySelectScene()
end
return ReplayTestScene

View File

@@ -15,6 +15,8 @@ local main_menu_screens = {
}
function TitleScene:new()
VCTRL.clearAll() -- Reset the RESTART button state
VCTRL.new(SETTINGS.input.virtual)
if SOUNDS['bgm_firsthalf']:isPlaying() or SOUNDS['bgm_secondhalf']:isPlaying() or not SETTINGS["music"] then
love.audio.stop()
end

View File

@@ -5,13 +5,11 @@ TouchConfigScene.title = "Touchscreen config"
local Grid = require 'game.grid'
local buttonList
local sliderList
local sliderList = {}
---@class VCTRL.data
local focusingButton
---@type number
local snapUnit = 1
---@type boolean
local hasChanged
---@type function
local function exitSceneFunc(saved)
@@ -29,37 +27,55 @@ buttonList = {
showToggle = BUTTON.new{
text = function()
if focusingButton then
return focusingButton.show and "[SHOW]\nHide" or "Show\n[HIDE]"
return focusingButton.show and ">SHOW<\nhide" or "show\n>HIDE<"
else
return "Show\nHide"
return "show\nhide"
end
end,
x = 275, y = 5, w = 50, h = 75,
codeWhenReleased = function ()
x = 400, y = 110, w = 60, h = 40,
codeWhenReleased = function()
if focusingButton then
focusingButton.show = not focusingButton.show
hasChanged = true
VCTRL.hasChanged = true
end
end,
update = function(self) self.textColor = focusingButton and {1, 1, 1} or {0.5, 0.5, 0.5} end
},
previewToggle = BUTTON.new{
text = "Preview\nON",
x = 570, y = 35, w = 60, h = 40,
x = 570, y = 60, w = 60, h = 40,
codeWhenReleased = function()
VCTRL.release()
BUTTON.release(buttonList)
SCENE = TouchConfigPreviewScene()
end
},
resetAll = BUTTON.new{
text = "RESET\nALL",
x = 500, y = 110, w = 60, h = 40,
codeWhenReleased = function()
local selection = love.window.showMessageBox(
"Save config?", "Are you really sure about RESETTING ALL touchscreen configuration?",
{"Yes", "No", escapebutton = 2, enterbutton = 1},
"info", true
)
if selection == 1 then
VCTRL.focus = nil; focusingButton = nil
VCTRL.hasChanged = false
VCTRL.clearAll()
VCTRL.new(SETTINGS.__default__.input.virtual)
SETTINGS.input.virtual = SETTINGS.__default__.input.virtual
end
end
},
menuScreen = BUTTON.new{
text = "MENU",
x = 570, y = 5, w = 60, h = 25,
x = 570, y = 10, w = 60, h = 40,
codeWhenReleased = function()
if hasChanged or SETTINGS.firstTime then
if VCTRL.hasChanged or SETTINGS.firstTime then
local selection = love.window.showMessageBox(
"Save config?", "Do you want to save your changes before exiting?",
{"Save", "Discard", "Keep editing", escapebutton = 2, enterbutton = 1},
{"Save", "Discard", "Keep editing", escapebutton = 3, enterbutton = 1},
"info", true
)
if selection == 1 then
@@ -80,45 +96,59 @@ buttonList = {
end
}
}
sliderList = {}
sliderList.buttonSize = newSlider(
200, 30, 120, 0, 0, 120,
function(v)
if focusingButton then
v = math.roundUnit(v, 5)
if focusingButton.r ~= v then
focusingButton.r = v
VCTRL.hasChanged = true
end
sliderList.buttonSize.value = v / 120
end
end,
{width = 40}
)
sliderList.iconSize = newSlider(
480, 30, 120, 0, 0, 100,
function(v)
if focusingButton then
v = math.roundUnit(v, 5)
if focusingButton.iconSize ~= v then
focusingButton.iconSize = v
VCTRL.hasChanged = true
end
sliderList.iconSize.value = v / 100
end
end,
{width = 40}
)
sliderList.opacity = newSlider(
155, 20+5, 120, 100, 0, 100,
200, 80, 120, 0, 0, 1,
function()
local v
if focusingButton then
v = math.roundUnit(sliderList.opacity.value, 0.01)
if focusingButton.alpha~=v then
focusingButton.alpha = v
hasChanged = true
VCTRL.hasChanged = true
end
sliderList.opacity.value = v
end
end,
{width = 30}
)
sliderList.size = newSlider(
155, 60+2.5, 120, 45, 0, 120,
function(v)
if focusingButton then
local v = math.roundUnit(v, 5)
if focusingButton.r ~= v then
focusingButton.r = v
hasChanged = true
end
sliderList.size.value = v / 120
end
end,
{width = 30}
{width = 40}
)
local gridSizeTable = {1, 2, 5, 10, 20, 50, 100}
sliderList.gridSize = newSlider(
405, 50, 100, 1, 1, #gridSizeTable - 1,
480, 80, 120, 1, 1, #gridSizeTable - 1,
function()
local v = math.roundUnit(sliderList.gridSize.value, 1 / 6)
local f = #gridSizeTable - 1
local v = math.roundUnit(sliderList.gridSize.value, 1 / f)
sliderList.gridSize.value = v
snapUnit = gridSizeTable[math.roundUnit(v * (#gridSizeTable - 1) + 1)]
snapUnit = gridSizeTable[math.roundUnit(v * f + 1)]
end,
{width = 30}
{width = 40}
); sliderList.gridSize.forceLight = true
local function sliderList_draw()
@@ -145,12 +175,12 @@ local function sliderList_update()
end
end
function TouchConfigScene:new(fromPreviewScene)
function TouchConfigScene:new()
VCTRL.toggle(true)
VCTRL.focus = nil
focusingButton = nil
hasChanged = fromPreviewScene
Grid:new(10, 20)
-- TODO
end
@@ -159,13 +189,15 @@ function TouchConfigScene:update()
if VCTRL.focus~=focusingButton then
focusingButton = VCTRL.focus
sliderList.opacity.value = focusingButton.alpha
sliderList.size.value = focusingButton.r / 120
sliderList.buttonSize.value = focusingButton.r / 120
sliderList.iconSize.value = focusingButton.iconSize / 100
end
BUTTON.update(buttonList)
sliderList_update()
end
local string_format = string.format
function TouchConfigScene:render()
MainBackground()
@@ -188,19 +220,16 @@ function TouchConfigScene:render()
end
love.graphics.setColor(0, 0, 0, 0.7)
-- Opacity and Size
love.graphics.rectangle("fill", 10, 5, 267, 75)
-- Snap to grid
love.graphics.rectangle("fill", 330, 5, 150, 75)
love.graphics.rectangle("fill", 5, 5, 560, 100)
-- Button Size
drawText(string_format("Size (buttons)\n%14.1d", focusingButton and focusingButton.r or 0), 10, 13, 100, "left")
-- Icon size
drawText(string_format("Size (icons)\n%13.1d%%", focusingButton and focusingButton.iconSize or 0), 290, 13, 100, "left")
-- Opacity
drawText("Opacity", 20, 15, 100, "left")
drawText(string.format("%3.1d%%", focusingButton and focusingButton.alpha * 100 or 0), 225, 15, 40, "left")
-- Size
drawText("Size", 20, 55, 100, "left")
drawText(string.format("%3.1dpx", focusingButton and focusingButton.r or 0), 225, 55, 40, "left")
drawText(string_format("Opacity\n%13.1d%%", focusingButton and focusingButton.alpha * 100 or 0), 10, 63, 100, "left")
-- Snap to grid
drawText(string.format("Snap to grid: %3s", snapUnit), 345, 15, 140, "left")
drawText(string_format("Snap to grid\n%14.1d", snapUnit), 290, 63, 100, "left")
for _, v in ipairs(VCTRL) do
if v ~= focusingButton then
@@ -227,10 +256,8 @@ end
---@param e SCENE_onInput
function TouchConfigScene:onInputMove(e)
if e.type == "touch" or (e.type == "mouse" and love.mouse.isDown(1)) then
if VCTRL.drag(e.dx, e.dy, e.id or 1) then hasChanged = true end
end
if e.type == "mouse" then
if VCTRL.drag(e.dx, e.dy, e.id or 1) then VCTRL.hasChanged = true end
elseif e.type == "mouse" then
BUTTON.checkHovering(buttonList, e.x, e.y)
end
end
@@ -240,7 +267,8 @@ function TouchConfigScene:onInputPress(e)
if not (
VCTRL.press(e.x, e.y, e.id and e.id or 1, true) or
BUTTON.press(buttonList, e.x, e.y, e.id) or
(e.x >= 80 and e.x <= 230 and e.y >= 10 and e.y <= 77)
(e.x >= 120 and e.x <= 280 and e.y >= 10 and e.y <= 100) or
(e.x >= 400 and e.x <= 560 and e.y >= 10 and e.y <= 100)
) then
VCTRL.focus = nil
focusingButton = nil

View File

@@ -8,7 +8,7 @@ local buttonList
buttonList = {
previewToggle = BUTTON.new{
text = "Preview\nOFF",
x = 570, y = 35, w = 60, h = 40,
x = 570, y = 60, w = 60, h = 40,
codeWhenReleased = function()
VCTRL.release()
BUTTON.release(buttonList)
@@ -16,8 +16,6 @@ buttonList = {
end
},
}
local sliderList = {}
local secret_grade_grid = {}
do
local colour_names = {'R', 'O', 'Y', 'G', 'C', 'B', 'M'}
@@ -80,13 +78,17 @@ end
function TouchConfigPreviewScene:onInputPress(e)
if e.type ~= "virtual" and e.input == 'menu_back' then SCENE = InputConfigScene() end
if e.type == "mouse" or e.type == "touch" then
BUTTON.press(buttonList, e.x, e.y, e.id)
if not BUTTON.press(buttonList, e.x, e.y, e.id) then
VCTRL.press(e.x, e.y, e.id or 1)
end
end
end
---@param e SCENE_onInput
function TouchConfigPreviewScene:onInputRelease(e)
if e.type == "mouse" or e.type == "touch" then
BUTTON.release(buttonList, e.x, e.y, e.id)
if not BUTTON.release(buttonList, e.x, e.y, e.id) then
VCTRL.release(e.id or 1)
end
end
end

View File

@@ -57,6 +57,8 @@ function TrainingScene:onInputPress(e)
SCENE = TitleScene()
elseif (e.input == "menu_back") then
SCENE = TitleScene()
elseif e.input == "restart" or e.input == "menu_decide" then
SCENE = TrainingScene()
elseif e.input and string.sub(e.input, 1, 5) ~= "menu_" then
self.inputs[e.input] = true
end

View File

@@ -25,15 +25,14 @@ local _defaultSettings = {
{type='button',x=640-145,y=355,key= 'rotate_left2',r=45,iconSize=60,alpha=0.4},
{type='button',x=640- 70,y=430,key= 'rotate_right',r=45,iconSize=60,alpha=0.4},
{type='button',x=640- 70,y=280,key='rotate_right2',r=45,iconSize=60,alpha=0.4},
{type='button',x=320- 40,y=420,key= 'menu_decide',r=35,iconSize=60,alpha=0.4},
{type='button',x=320+ 40,y=420,key= 'menu_back',r=35,iconSize=60,alpha=0.4},
{type='button',x=320, y=420,key= 'restart',r=35,iconSize=60,alpha=0.4},
}
},
tvMode = false -- 79338732
tvMode = false
}
SETTINGS = setmetatable(
{},
{__default__ = _defaultSettings},
{
__index = function(_, k)
if _settings[k] == nil then