Compare commits

...

58 Commits

Author SHA1 Message Date
MrZ_26
d38ff06262 修万圣节日期判定 2024-11-01 10:57:44 +08:00
MrZ_26
45a4b10d11 修关闭节日主题时特定条件无法启动 2024-11-01 03:26:37 +08:00
MrZ_26
fe12f397cc 修复皮肤设置的初始值相关 2024-11-01 00:40:27 +08:00
MrZ_26
801f67b194 Revert "fixed default skin not showing up"
This reverts commit 1cf3d101aa.
2024-11-01 00:37:35 +08:00
MrZ_26
e331c8f446 .gitignore删除一个不用的文件夹 2024-11-01 00:35:51 +08:00
MrZ_26
51897584a7 调整一个tip说法 2024-11-01 00:35:51 +08:00
Fab052
1cf3d101aa fixed default skin not showing up 2024-10-31 16:54:58 +01:00
Fab052
1830e849d8 better 3D effect (#1166) 2024-10-30 00:14:28 +08:00
Fab052
e3f246aa00 removed missing skin from main.lua (#1165) 2024-10-30 00:14:06 +08:00
Fab052
1eb679cf24 Reorganized the skins + Added "Arcade" and "Cardboard" skins (#1164) 2024-10-29 08:24:36 +08:00
Fuwuwuwu
1963dc9fb9 Fix the issue of AI passing Hold parameters in custom mode (#1163)
* 自定义游戏传递hold开关的问题

表现:即使holdCount为0,CC依旧会开启hold,导致无法预期运行,场地逐渐混乱
原因:传递了错误的类型(boolean->number)

* 非正常hold模式时,AI不启用hold

相关问题#1106
2024-10-29 08:24:10 +08:00
SweetSea
396293c8af Custom image from clipboard (#1157)
* Update how text renders in button and key

* Add a placeholder

* Add actual code and language entry

* Sort buttons

* Alter to keep original behaviour

* I HAVE OCD

* Add back legacy behaviour
2024-10-26 23:44:31 +08:00
Imple Lee
fbf6e910a3 Fix a wierd bug (#1161)
Steps to reproduce:
1. reset everything in custom game
2. restart the game
3. go to the custom game scene
4. go to the custom sequence scene and set a new sequence
5. leave the scene and go to the custom game scene
6. begin to clear

Unintended behavior:
The sequence is still the default old sequence (7-bag)

Intended behavior:
The sequence is the newly set sequence

Rationale:
The bug was introduced by #1139.
It deleted a line of `apply_locals()` which I wrongly believed that the custom modes will execute initialize() anyway, but it turned out initialization is only executed during the start of the whole game.
The patch is simple: just add back the deleted line
2024-10-18 00:45:37 +08:00
MrZ_26
9e4e861c32 继续加赞助名单 2024-10-14 23:51:46 +08:00
MrZ_26
d0b99a16c9 修单机录像播不出 2024-10-14 23:36:31 +08:00
MrZ_26
347e81c11c 更新赞助名单 2024-10-14 23:15:40 +08:00
MrZ_26
6b2a376dfe legals文件添加discord-rpc.dll的说明 2024-10-13 00:46:11 +08:00
MrZ_26
51e0ab7c48 赞助名单更新 2024-10-02 10:31:51 +08:00
MrZ_26
87fd26ab89 赞助名单更新 2024-09-29 20:14:36 +08:00
MrZ_26
0b1cee99bd 尝试添加一个paypal赞助按钮 2024-09-29 19:14:54 +08:00
MrZ_26
4768df6867 修discordRPC加载失败会崩溃 2024-09-27 03:50:12 +08:00
MrZ_26
423d502aa4 添加discordRPC支持 2024-09-26 02:29:24 +08:00
MrZ_26
a74e9033b3 添加500star贺图到仓库readme 2024-09-13 19:08:20 +08:00
SweetSea
dc6b7de15f Fix multiplayer chat (#1154)
* Fix multiplayer chat

* Update wrapping text param.

* Update net_game.lua
2024-09-11 20:31:41 +08:00
Imple Lee
74f67d0216 fix macos portable ci (#1152)
* use love 11.5 for macos portable

* use xcode 15.3
2024-09-04 17:03:16 +08:00
MrZ_26
d47f073d53 补irs打断的设置文本
补更新历史
2024-09-03 21:21:22 +08:00
MrZ_26
7407911914 Merge Electra's enhanced IRS 2024-09-03 21:12:32 +08:00
MrZ_26
9672a4fe57 修之前删goto的时候导致hisPool算法爆炸(也不知道怎么爆的,不管了) 2024-09-03 20:57:05 +08:00
Imple Lee
6c6ff26586 fix auto test: use appimage love instead of tar gz (#1141)
* use appimage love instead of tar gz

* install libfuse2 to run appimage

* replace deprecated `GabrielBB/xvfb-action`
2024-08-30 05:34:39 +08:00
Imple Lee
ca6f701084 Fix auto test (#1140)
* remove unnecessary space

* restart automatic test
2024-08-26 12:13:19 +08:00
Imple Lee
5793b7ca38 fix custom games (#1139)
Now you can `play custom_` without going through the `customGame` scene
2024-08-26 11:20:48 +08:00
MrZ_26
dee6ba95f2 修播放录像时初始化流程没重置暂停次数 close #1131 2024-08-20 01:28:39 +08:00
呵呵です
67aef1dbe3 更新tip里的40l世界纪录 close #1136 (#1137) 2024-08-20 01:26:37 +08:00
MrZ_26
90f41a20a3 整理ws模块代码 2024-08-13 21:54:11 +08:00
MrZ_26
5f5dd48ee8 修直接写在模式的env里的特殊事件不会被加载 2024-08-12 12:04:29 +08:00
MrZ_26
a8e0574f44 规则包加载流程优化 2024-08-11 13:18:36 +08:00
MrZ_26
40f148b6b3 更新赞助名单 2024-08-11 13:12:34 +08:00
MrZ_26
0eb37666f8 更新历史和版本号 2024-08-10 19:03:13 +08:00
MrZ_26
b73a653332 Merge branch 'eventsystem2' 2024-08-10 18:55:05 +08:00
MrZ_26
49f1b747b2 尝试修复新的事件系统允许自由参数数量导致网络卡顿情况且录像流最后的事件是攻击时frameRun有机会超过stream内数据导致玩家操作冻结(wtf so complicated, thank you Electra!) 2024-08-10 18:51:41 +08:00
Electra
2c75f0bc9c Simple solution? 2024-08-10 04:47:35 -04:00
MrZ_26
97e17edfae 继续修新的事件系统问题 2024-08-10 16:05:25 +08:00
MrZ_26
f7e4e47466 再修新的事件系统问题 2024-08-10 15:09:27 +08:00
MrZ_26
8779abef9a 修新的事件系统问题 2024-08-10 14:42:18 +08:00
MrZ_26
4d1caa7fe0 重构玩家交互事件系统,尝试支持可通过网络传递的自定义事件 2024-08-10 12:55:58 +08:00
MrZ_26
78f3c31db1 联网房间内的准备按钮不再会试图将自己设置为Gamer状态(已经是了) 2024-08-09 05:23:38 +08:00
MrZ_26
3c852f17a0 补翻译说明 2024-08-09 01:12:49 +08:00
MrZ_26
8737a00b44 补越南语翻译 2024-08-09 01:08:25 +08:00
MrZ_26
fff2c49f2e 补越南语翻译 2024-08-09 00:03:13 +08:00
MrZ_26
35c19a4d50 spinren加一些注释的代码 2024-08-08 23:43:01 +08:00
MrZ_26
137e707c63 删除ffa按钮
tech league改名galaxim,调整一些联网菜单文本
2024-08-08 23:42:41 +08:00
MrZ_26
d2e9439e38 更新版本号 2024-08-08 23:06:38 +08:00
MrZ_26
39cd7e4c1a 调整服务器url 2024-08-08 23:06:07 +08:00
MrZ_26
57f2b9541d 修几个词条不能通过拼音搜索 2024-08-08 16:14:53 +08:00
MrZ_26
9d4065a05a 修复创建联网房间时有好几个选项并没有生效 2024-08-08 16:08:51 +08:00
MrZ_26
424a3b3bee 公告页面信息更详细一点 2024-08-07 13:52:46 +08:00
MrZ_26
de3e1fcdc7 实现公告场景的功能 2024-08-07 13:25:03 +08:00
MrZ_26
59f390de93 尝试启用公告功能 2024-08-07 12:54:06 +08:00
134 changed files with 1170 additions and 394 deletions

16
.github/500stars/README.md vendored Normal file
View File

@@ -0,0 +1,16 @@
# Techmino - 500-star Banner
Created by NOT_A_ROBOT
13 September, 2024
**Don't forget to attribute me when using this.**
The image already includes sufficient attribution, so if you just don't crop that out, you shouldn't need to explicitly mention them.
## Attribution
Created by NOT_A_ROBOT
GitHub logo (on Z-character's screen) by GitHub
Background (space stars) originally by MrZ, ported to JS by NOT_A_ROBOT for rendering
Block skin (featured in the background) by Scf, slightly modified to make it darker
Z-character drawn by 葉枭, designed by MrZ
Techmino by MrZ and many contributors
Techmino is fun! https://github.com/26F-Studio/Techmino

BIN
.github/500stars/exported.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Binary file not shown.

BIN
.github/donate/donate.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -130,7 +130,7 @@ jobs:
prerelease: ${{ startsWith(github.ref, 'refs/tags/pre') }}
auto-test:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
needs: build-core
steps:
- uses: actions/checkout@v3
@@ -141,6 +141,25 @@ jobs:
with:
font-path: ./parts/fonts/proportional.otf
language-folder: ./parts/language
- name: Download core love package
uses: actions/download-artifact@v3
with:
name: ${{ env.CORE_LOVE_ARTIFACT_NAME }}
- name: Download love
shell: bash
run: |
curl -OL --retry 5 https://github.com/love2d/love/releases/download/11.4/love-11.4-x86_64.AppImage
chmod +x love-11.4-x86_64.AppImage
- name: Prepare PulseAudio and AppImage
shell: bash
run: |
sudo apt-get update
sudo apt-get install pulseaudio pulseaudio-utils pavucontrol alsa-oss alsa-utils libfuse2 -y
- name: Run automated test
uses: coactions/setup-xvfb@v1
with:
run: |
./love-11.4-x86_64.AppImage ${{ env.CORE_LOVE_PACKAGE_PATH }} --test
build-android:
runs-on: ubuntu-latest
@@ -331,6 +350,12 @@ jobs:
shell: bash
run: |
rm ./ColdClear/universal/libcold_clear.a
- name: Use Xcode 15.3
# Xcode 15.4 segfaults
# see https://forums.developer.apple.com/forums/thread/757398
uses: mobiledevops/xcode-select-version-action@v1
with:
xcode-select-version: 15.3
- name: Build macOS packages
id: build-packages
uses: love-actions/love-actions-macos-portable@v1
@@ -339,6 +364,7 @@ jobs:
bundle-id: ${{ steps.process-app-name.outputs.bundle-id }}
copyright: "Copyright © 2019-2023 26F-Studio. Some Rights Reserved."
icon-path: ./.github/build/macOS/${{ env.BUILD_TYPE }}/icon.icns
love-ref: "11.5"
love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }}
libs-path: ./ColdClear/universal/
product-name: ${{ steps.process-app-name.outputs.product-name }}
@@ -469,8 +495,8 @@ jobs:
icon-path: ./.github/build/windows/${{ env.BUILD_TYPE }}/icon.ico
rc-path: ./.github/build/windows/${{ env.BUILD_TYPE }}/template.rc
love-package: ${{ env.CORE_LOVE_PACKAGE_PATH }}
extra-assets-x86: ./ColdClear/x86/CCloader.dll ./ColdClear/x86/cold_clear.dll
extra-assets-x64: ./ColdClear/x64/CCloader.dll ./ColdClear/x64/cold_clear.dll
extra-assets-x86: ./ColdClear/x86/CCloader.dll ./ColdClear/x86/cold_clear.dll ./.github/build/extraLibs/Windows_x64/discord-rpc.dll
extra-assets-x64: ./ColdClear/x64/CCloader.dll ./ColdClear/x64/cold_clear.dll ./.github/build/extraLibs/Windows_x86/discord-rpc.dll
product-name: ${{ steps.process-app-name.outputs.product-name }}
app-id: ${{ secrets.WINDOWS_APP_ID }}
project-website: https://www.studio26f.org/

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
.vscode
libAndroid
*.ini
.DS_Store
Thumbs.db

View File

@@ -8,8 +8,6 @@ local path=''
local type=type
local timer=love.timer.getTime
local TRD=love.thread.newThread("\n")
local TRD_isRunning=TRD.isRunning
local WS={}
local wsList=setmetatable({},{
@@ -151,7 +149,7 @@ function WS.update(dt)
local time=timer()
for name,ws in next,wsList do
if ws.real and ws.status~='dead' then
if TRD_isRunning(ws.thread) then
if ws.thread:isRunning() then
if ws.triggerCHN:getCount()==0 then
ws.triggerCHN:push(0)
end

View File

@@ -15,6 +15,7 @@ do-- Connect
SOCK:settimeout(timeout)
local res,err=SOCK:connect(host,port)
-- print('C0',res,err)
assert(res,err)
-- WebSocket handshake
@@ -31,6 +32,7 @@ do-- Connect
-- First line of HTTP
res,err=SOCK:receive('*l')
-- print('C',res,err)
assert(res,err)
local code,ctLen
code=res:find(' ')
@@ -39,6 +41,7 @@ do-- Connect
-- Get body length from headers and remove headers
repeat
res,err=SOCK:receive('*l')
-- print('H',res,err)
assert(res,err)
if not ctLen and res:find('content-length') then
ctLen=tonumber(res:match('%d+')) or 0
@@ -53,6 +56,7 @@ do-- Connect
-- Content(?)
if ctLen then
res,err=SOCK:receive(ctLen)
-- print('R',res,err)
if code~='101' then
res=JSON.decode(assert(res,err))
error((code or "XXX")..":"..(res and res.reason or "Server Error"))
@@ -140,10 +144,10 @@ local readThread=coroutine.wrap(function()
assert(res,err)
length=shl(byte(res,1),8)+byte(res,2)
elseif length==127 then
local lenData
lenData,err=_receive(SOCK,8)
-- 'res' is 'lenData' here
res,err=_receive(SOCK,8)
assert(res,err)
local _,_,_,_,_5,_6,_7,_8=byte(lenData,1,8)
local _,_,_,_,_5,_6,_7,_8=byte(res,1,8)
length=shl(_5,24)+shl(_6,16)+shl(_7,8)+_8
end
res,err=_receive(SOCK,length)
@@ -162,12 +166,14 @@ local readThread=coroutine.wrap(function()
lBuffer=lBuffer..res
if fin then
CHN_push(readCHN,lBuffer)
-- print('M',lBuffer)
lBuffer=""
end
else
CHN_push(readCHN,op)
if fin then
CHN_push(readCHN,res)
-- print('S',res)
lBuffer=""
else
lBuffer=res

View File

@@ -15,6 +15,7 @@ local timer=love.timer.getTime
local next=next
local floor,ceil=math.floor,math.ceil
local max,min=math.max,math.min
local match=string.match
local sub,ins,rem=string.sub,table.insert,table.remove
local xOy=SCR.xOy
local FONT=FONT
@@ -142,13 +143,21 @@ local button={
type='button',
mustHaveText=true,
ATV=0,-- Activating time(0~8)
textAlreadyWrapped=false,-- Text already wrapped? (Managed by :setObject, can be override, this will be true if obj has a '\n')
}
function button:reset()
self.ATV=0
end
function button:setObject(obj)
if type(obj)=='string' or type(obj)=='number' then
self.obj=gc.newText(FONT.get(self.font,self.fType),obj)
if match(obj,"\n") then
self.textAlreadyWrapped=true
self.obj=gc.newText(FONT.get(self.font,self.fType))
self.obj:addf(obj,self.w-self.edge*2,(self.align=='L' and 'left') or (self.align=='R' and 'right') or 'center')
else
self.textAlreadyWrapped=false
self.obj=gc.newText(FONT.get(self.font,self.fType),obj)
end
elseif obj then
self.obj=obj
end
@@ -194,16 +203,7 @@ function button:draw()
local ox,oy=obj:getWidth()*.5,obj:getHeight()*.5
local y0=y+h*.5
gc_setColor(1,1,1,.2+ATV*.05)
if self.align=='M' then
local x0=x+w*.5
local kx=obj:type()=='Text' and min(w/ox/2,1) or 1
gc_draw(obj,x0-1,y0-1,nil,kx,1,ox,oy)
gc_draw(obj,x0-1,y0+1,nil,kx,1,ox,oy)
gc_draw(obj,x0+1,y0-1,nil,kx,1,ox,oy)
gc_draw(obj,x0+1,y0+1,nil,kx,1,ox,oy)
gc_setColor(r*.55,g*.55,b*.55)
gc_draw(obj,x0,y0,nil,kx,1,ox,oy)
elseif self.align=='L' then
if self.align=='L' or self.textAlreadyWrapped then
local edge=self.edge
gc_draw(obj,x+edge-1,y0-1-oy)
gc_draw(obj,x+edge-1,y0+1-oy)
@@ -219,6 +219,15 @@ function button:draw()
gc_draw(obj,x0+1,y0+1-oy)
gc_setColor(r*.55,g*.55,b*.55)
gc_draw(obj,x0,y0-oy)
else--if self.align=='M' then
local x0=x+w*.5
local kx=obj:type()=='Text' and min(w/ox/2,1) or 1
gc_draw(obj,x0-1,y0-1,nil,kx,1,ox,oy)
gc_draw(obj,x0-1,y0+1,nil,kx,1,ox,oy)
gc_draw(obj,x0+1,y0-1,nil,kx,1,ox,oy)
gc_draw(obj,x0+1,y0+1,nil,kx,1,ox,oy)
gc_setColor(r*.55,g*.55,b*.55)
gc_draw(obj,x0,y0,nil,kx,1,ox,oy)
end
end
function button:getInfo()
@@ -290,13 +299,21 @@ local key={
type='key',
mustHaveText=true,
ATV=0,-- Activating time(0~4)
textAlreadyWrapped=false,---See button.setObject (line 146)
}
function key:reset()
self.ATV=0
end
function key:setObject(obj)
if type(obj)=='string' or type(obj)=='number' then
self.obj=gc.newText(FONT.get(self.font,self.fType),obj)
if match(obj,"\n") then
self.textAlreadyWrapped=true
self.obj=gc.newText(FONT.get(self.font,self.fType))
self.obj:addf(obj,self.w-self.edge*2,(self.align=='L' and 'left') or (self.align=='R' and 'right') or 'center')
else
self.textAlreadyWrapped=false
self.obj=gc.newText(FONT.get(self.font,self.fType),obj)
end
elseif obj then
self.obj=obj
end
@@ -354,14 +371,15 @@ function key:draw()
-- Drawable
local obj=self.obj
local ox,oy=obj:getWidth()*.5,obj:getHeight()*.5
gc_setColor(r,g,b)
if align=='M' then
local kx=obj:type()=='Text' and min(w/ox/2,1) or 1
gc_draw(obj,x+w*.5,y+h*.5,nil,kx,1,ox,oy)
elseif align=='L' then
gc_draw(obj,x+self.edge,y-oy+h*.5)
if align=='L' or self.textAlreadyWrapped then
gc_draw(obj,x+self.edge,y+h*.5-oy)
elseif align=='R' then
gc_draw(obj,x+w-self.edge-ox*2,y-oy+h*.5)
else--if align=='M' then
local kx=obj:type()=='Text' and min(w/ox/2,1) or 1
gc_draw(obj,x+w*.5,y+h*.5,nil,kx,1,ox,oy)
end
end
function key:getInfo()
@@ -1382,10 +1400,13 @@ function WIDGET.setLang(widgetText)
t=W.name or "##"
W.color=COLOR.dV
end
if type(t)=='string' and W.font then
t=gc.newText(FONT.get(W.font),t)
if type(W.setObject)=='function' then
W:setObject(t)
elseif type(t)=='string' and W.font then
W.obj=gc.newText(FONT.get(W.font or 30),t)
else
W.obj=t
end
W.obj=t
end
end
end

View File

@@ -12,6 +12,8 @@ Lua is free software distributed under the terms of the MIT license. Copyright
json.lua is copyrighted by rxi. © 2022 rxi.
discord-rpc.dll is copyrighted by Discord, Inc. © 2017 Discord, Inc.
IBM Plex is copyrighted by the International Business Machines Corporation. IBM and IBM Plex are trademarks of IBM Corp, registered in many jurisdictions worldwide. IBM Plex is licensed under the SIL Open Font License, Version 1.1.

View File

@@ -294,39 +294,41 @@ IMG.init{
},
}
SKIN.load{
{name="crystal_scf", path='media/image/skin/crystal_scf.png'},
{name="matte_mrz", path='media/image/skin/matte_mrz.png'},
{name="shiny_chno", path='media/image/skin/shiny_chno.png'},
{name="contrast_mrz", path='media/image/skin/contrast_mrz.png'},
{name="polkadots_scf", path='media/image/skin/polkadots_scf.png'},
{name="toy_scf", path='media/image/skin/toy_scf.png'},
{name="smooth_mrz", path='media/image/skin/smooth_mrz.png'},
{name="simple_scf", path='media/image/skin/simple_scf.png'},
{name="glass_scf", path='media/image/skin/glass_scf.png'},
{name="penta_scf", path='media/image/skin/penta_scf.png'},
{name="bubble_scf", path='media/image/skin/bubble_scf.png'},
{name="minoes_scf", path='media/image/skin/minoes_scf.png'},
{name="pure_mrz", path='media/image/skin/pure_mrz.png'},
{name="bright_scf", path='media/image/skin/bright_scf.png'},
{name="glow_mrz", path='media/image/skin/glow_mrz.png'},
{name="plastic_mrz", path='media/image/skin/plastic_mrz.png'},
{name="paper_mrz", path='media/image/skin/paper_mrz.png'},
{name="yinyang_scf", path='media/image/skin/yinyang_scf.png'},
{name="cartooncup_earety", path='media/image/skin/cartooncup_earety.png'},
{name="jelly_miya", path='media/image/skin/jelly_miya.png'},
{name="guidetris_xmiao_lusisi",path='media/image/skin/guidetris_xmiao_lusisi.png'},
{name="brick_notypey", path='media/image/skin/brick_notypey.png'},
{name="gem_notypey", path='media/image/skin/gem_notypey.png'},
{name="classic", path='media/image/skin/classic_unknown.png'},
{name="ball_shaw", path='media/image/skin/ball_shaw.png'},
{name="retro_notypey", path='media/image/skin/retro_notypey.png'},
{name="pixel_chno", path='media/image/skin/pixel_chno.png'},
{name="pastel_chno", path='media/image/skin/pastel_chno.png'},
{name="letters_chno", path='media/image/skin/letters_chno.png'},
{name="kanji_chno", path='media/image/skin/kanji_chno.png'},
{name="textbone_mrz", path='media/image/skin/textbone_mrz.png'},
{name="coloredbone_mrz", path='media/image/skin/coloredbone_mrz.png'},
{name="wtf", path='media/image/skin/wtf_mrz.png'},
{name="Arcade (Asriel)", path='media/image/skin/asriel/arcade.png'},
{name="Cardboard (Asriel, slimenergy)", path='media/image/skin/asriel/cardboard.png'},
{name="Crystal (Scf)", path='media/image/skin/scf/crystal.png'},
{name="Matte (MrZ)", path='media/image/skin/mrz/matte.png'},
{name="Shiny (CHNO)", path='media/image/skin/chno/shiny.png'},
{name="Contrast (MrZ)", path='media/image/skin/mrz/contrast.png'},
{name="Polkadots (Scf)", path='media/image/skin/scf/polkadots.png'},
{name="Toy (Scf)", path='media/image/skin/scf/toy.png'},
{name="Smooth (MrZ)", path='media/image/skin/mrz/smooth.png'},
{name="Simple (Scf)", path='media/image/skin/scf/simple.png'},
{name="Glass (Scf)", path='media/image/skin/scf/glass.png'},
{name="Penta (Scf)", path='media/image/skin/scf/penta.png'},
{name="Bubble (Scf)", path='media/image/skin/scf/bubble.png'},
{name="Minoes (Scf)", path='media/image/skin/scf/minoes.png'},
{name="pure (MrZ)", path='media/image/skin/mrz/pure.png'},
{name="bright (Scf)", path='media/image/skin/scf/bright.png'},
{name="Glow (MrZ)", path='media/image/skin/mrz/glow.png'},
{name="Plastic (MrZ)", path='media/image/skin/mrz/plastic.png'},
{name="paper (MrZ)", path='media/image/skin/mrz/paper.png'},
{name="Yinyang (Scf)", path='media/image/skin/scf/yinyang.png'},
{name="Cartooncup (Earety)", path='media/image/skin/earety/cartooncup.png'},
{name="Jelly (Miya)", path='media/image/skin/miya/jelly.png'},
{name="guidetris (xmiao, lusisi)",path='media/image/skin/guidetris_xmiao_lusisi.png'},
{name="brick (Notypey)", path='media/image/skin/notypey/brick.png'},
{name="Gem (Notypey)", path='media/image/skin/notypey/gem.png'},
{name="Classic", path='media/image/skin/unknown/classic.png'},
{name="Ball (Shaw)", path='media/image/skin/shaw/ball.png'},
{name="Retro (Notypey)", path='media/image/skin/notypey/retro.png'},
{name="Pixel (CHNO)", path='media/image/skin/chno/pixel.png'},
{name="Pastel (CHNO)", path='media/image/skin/chno/pastel.png'},
{name="Letters (CHNO)", path='media/image/skin/chno/letters.png'},
{name="Kanji (CHNO)", path='media/image/skin/chno/kanji.png'},
{name="Textbone (MrZ)", path='media/image/skin/mrz/textbone.png'},
{name="Coloredbone (MrZ)", path='media/image/skin/mrz/coloredbone.png'},
{name="WTF (MrZ)", path='media/image/skin/mrz/wtf.png'},
}
-- Initialize sound libs
@@ -425,7 +427,8 @@ do
for _,v in next,SETTING.skin do if v<1 or v>17 then v=17 end end
if not RSlist[SETTING.RS] then SETTING.RS='TRS' end
if SETTING.ghostType=='greyCell' then SETTING.ghostType='grayCell' end
if type(SETTING.skinSet)=='number' then SETTING.skinSet='crystal_scf' end
if type(SETTING.skinSet)=='number' then SETTING.skinSet='Crystal (Scf)' end
if string.find(SETTING.skinSet,"_") then SETTING.skinSet='Crystal (Scf)' end
if not TABLE.find({8,10,13,17,22,29,37,47,62,80,100},SETTING.frameMul) then SETTING.frameMul=100 end
if SETTING.cv then SETTING.vocPack,SETTING.cv=SETTING.cv end
if type(SETTING.bg)~='string' then SETTING.bg='on' end
@@ -592,17 +595,21 @@ end
table.sort(REPLAY,function(a,b) return a.fileName>b.fileName end)
AUTHURL="https://www.studio26f.org/oauth?product=techmino"
AUTHHOST="www.studio26f.org"
WS.switchHost('www.studio26f.org','80','/techmino/ws/v1')
HTTP.setHost("www.studio26f.org")
AUTHHOST="www.studio26f.org:8080"
WS.switchHost('www.studio26f.org','8081','/techmino/ws/v1')
HTTP.setHost("www.studio26f.org:8081")
HTTP.setThreadCount(1)
-- Discord RPC
DiscordRPC=require'parts.discordRPC'
DiscordRPC.update()
table.insert(_LOADTIMELIST_,("Load Resources: %.3fs"):format(TIME()-_LOADTIME_))
for i=1,#_LOADTIMELIST_ do LOG(_LOADTIMELIST_[i]) end
-- Launch testing task if launch param received
if TABLE.find(arg,'-- test') then
if TABLE.find(arg,'--test') then
TASK.new(function()
while not LOADED do coroutine.yield() end

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 613 B

After

Width:  |  Height:  |  Size: 613 B

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 91 B

After

Width:  |  Height:  |  Size: 91 B

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 837 B

After

Width:  |  Height:  |  Size: 837 B

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -256,54 +256,34 @@ end
large value will be encoded as 1xxxxxxx(high)-1xxxxxxx-...-0xxxxxxx(low)
Example (decoded):
6,1, 20,-1, 0,2, 26,-2, 872,4, ...
26,1, 42,-1, ...
This means:
Press key1 at 6f
Release key1 at 26f (6+20)
Press key2 at the same time (26+0)
Release key 2 after 26 frame (26+26)
Press key 4 after 872 frame (52+872)
Press key1 at 26f
Release key1 at 42f
...
]]
function DATA.dumpRecording(list,ptr)
local out=""
local buffer=""
if not ptr then ptr=1 end
local prevFrm=list[ptr-2] or 0
while list[ptr] do
-- Flush buffer
if #buffer>10 then
if #buffer>26 then
out=out..buffer
buffer=""
end
-- Encode time
local t=list[ptr]-prevFrm
prevFrm=list[ptr]
buffer=buffer.._encode(t)
-- Encode event
buffer=buffer.._encode(list[ptr+1])
-- Step
ptr=ptr+2
buffer=buffer.._encode(list[ptr])
ptr=ptr+1
end
return out..buffer,ptr
end
function DATA.pumpRecording(str,L)
local len=#str
local p=1
local data
local curFrm=L[#L-1] or 0
while p<=len do
local code,event
-- Read delta time
code,p=_decode(str,p)
curFrm=curFrm+code
ins(L,curFrm)
event,p=_decode(str,p)
ins(L,event)
data,p=_decode(str,p)
ins(L,data)
end
end
do-- function DATA.saveReplay()

327
parts/discordRPC.lua Normal file
View File

@@ -0,0 +1,327 @@
local appId='1288557386700951554'
local ffi=require"ffi"
local RPC_C
if SYSTEM=='Windows' then
local suc
suc,RPC_C=pcall(ffi.load,"discord-rpc")
if not (suc and RPC_C) then
print("Failed to load Discord-RPC lib",RPC_C)
MES.new('error',"Failed to load Discord-RPC lib")
RPC_C=nil
end
end
local RPC
if RPC_C then
RPC={}
ffi.cdef[[
typedef struct DiscordRichPresence {
const char* state; /* max 128 bytes */
const char* details; /* max 128 bytes */
int64_t startTimestamp;
int64_t endTimestamp;
const char* largeImageKey; /* max 32 bytes */
const char* largeImageText; /* max 128 bytes */
const char* smallImageKey; /* max 32 bytes */
const char* smallImageText; /* max 128 bytes */
const char* partyId; /* max 128 bytes */
int partySize;
int partyMax;
const char* matchSecret; /* max 128 bytes */
const char* joinSecret; /* max 128 bytes */
const char* spectateSecret; /* max 128 bytes */
int8_t instance;
} DiscordRichPresence;
typedef struct DiscordUser {
const char* userId;
const char* username;
const char* discriminator;
const char* avatar;
} DiscordUser;
typedef void (*readyPtr)(const DiscordUser* request);
typedef void (*disconnectedPtr)(int errorCode, const char* message);
typedef void (*erroredPtr)(int errorCode, const char* message);
typedef void (*joinGamePtr)(const char* joinSecret);
typedef void (*spectateGamePtr)(const char* spectateSecret);
typedef void (*joinRequestPtr)(const DiscordUser* request);
typedef struct DiscordEventHandlers {
readyPtr ready;
disconnectedPtr disconnected;
erroredPtr errored;
joinGamePtr joinGame;
spectateGamePtr spectateGame;
joinRequestPtr joinRequest;
} DiscordEventHandlers;
void Discord_Initialize(const char* applicationId,
DiscordEventHandlers* handlers,
int autoRegister,
const char* optionalSteamId);
void Discord_Shutdown(void);
void Discord_RunCallbacks(void);
void Discord_UpdatePresence(const DiscordRichPresence* presence);
void Discord_ClearPresence(void);
void Discord_Respond(const char* userid, int reply);
void Discord_UpdateHandlers(DiscordEventHandlers* handlers);
]]
local function unpackDiscordUser(request)
return ffi.string(request.userId),ffi.string(request.username),
ffi.string(request.discriminator),ffi.string(request.avatar)
end
-- callback proxies
-- note: callbacks are not JIT compiled (= SLOW), try to avoid doing performance critical tasks in them
-- luajit.org/ext_ffi_semantics.html
local ready_proxy=ffi.cast("readyPtr",function(request)
if RPC.ready then
RPC.ready(unpackDiscordUser(request))
end
end)
local disconnected_proxy=ffi.cast("disconnectedPtr",function(errorCode,message)
if RPC.disconnected then
RPC.disconnected(errorCode,ffi.string(message))
end
end)
local errored_proxy=ffi.cast("erroredPtr",function(errorCode,message)
if RPC.errored then
RPC.errored(errorCode,ffi.string(message))
end
end)
local joinGame_proxy=ffi.cast("joinGamePtr",function(joinSecret)
if RPC.joinGame then
RPC.joinGame(ffi.string(joinSecret))
end
end)
local spectateGame_proxy=ffi.cast("spectateGamePtr",function(spectateSecret)
if RPC.spectateGame then
RPC.spectateGame(ffi.string(spectateSecret))
end
end)
local joinRequest_proxy=ffi.cast("joinRequestPtr",function(request)
if RPC.joinRequest then
RPC.joinRequest(unpackDiscordUser(request))
end
end)
-- helpers
local function checkArg(arg,argType,argName,func,maybeNil)
assert(type(arg)==argType or (maybeNil and arg==nil),
string.format("Argument \"%s\" to function \"%s\" has to be of type \"%s\"",
argName,func,argType))
end
local function checkStrArg(arg,maxLen,argName,func,maybeNil)
if maxLen then
assert(type(arg)=="string" and arg:len()<=maxLen or (maybeNil and arg==nil),
string.format("Argument \"%s\" of function \"%s\" has to be of type string with maximum length %d",
argName,func,maxLen))
else
checkArg(arg,"string",argName,func,true)
end
end
local function checkIntArg(arg,maxBits,argName,func,maybeNil)
maxBits=math.min(maxBits or 32,52) -- lua number (double) can only store integers < 2^53
local maxVal=2^(maxBits-1) -- assuming signed integers, which, for now, are the only ones in use
assert(type(arg)=="number" and math.floor(arg)==arg
and arg<maxVal and arg>=-maxVal
or (maybeNil and arg==nil),
string.format("Argument \"%s\" of function \"%s\" has to be a whole number <= %d",
argName,func,maxVal))
end
-- function wrappers
function RPC.initialize(applicationId,autoRegister,optionalSteamId)
local func="discordRPC.Initialize"
checkStrArg(applicationId,nil,"applicationId",func)
checkArg(autoRegister,"boolean","autoRegister",func)
if optionalSteamId~=nil then
checkStrArg(optionalSteamId,nil,"optionalSteamId",func)
end
local eventHandlers=ffi.new("struct DiscordEventHandlers")
eventHandlers.ready=ready_proxy
eventHandlers.disconnected=disconnected_proxy
eventHandlers.errored=errored_proxy
eventHandlers.joinGame=joinGame_proxy
eventHandlers.spectateGame=spectateGame_proxy
eventHandlers.joinRequest=joinRequest_proxy
RPC_C.Discord_Initialize(applicationId,eventHandlers,
autoRegister and 1 or 0,optionalSteamId)
end
function RPC.shutdown()
RPC_C.Discord_Shutdown()
end
function RPC.runCallbacks()
RPC_C.Discord_RunCallbacks()
end
-- http://luajit.org/ext_ffi_semantics.html#callback :
-- It is not allowed, to let an FFI call into a C function (runCallbacks)
-- get JIT-compiled, which in turn calls a callback, calling into Lua again (e.g. discordRPC.ready).
-- Usually this attempt is caught by the interpreter first and the C function
-- is blacklisted for compilation.
-- solution:
-- "Then you'll need to manually turn off JIT-compilation with jit.off() for
-- the surrounding Lua function that invokes such a message polling function."
jit.off(RPC.runCallbacks)
function RPC.updatePresence(presence)
local func="discordRPC.updatePresence"
checkArg(presence,"table","presence",func)
-- -1 for string length because of 0-termination
checkStrArg(presence.state,127,"presence.state",func,true)
checkStrArg(presence.details,127,"presence.details",func,true)
checkIntArg(presence.startTimestamp,64,"presence.startTimestamp",func,true)
checkIntArg(presence.endTimestamp,64,"presence.endTimestamp",func,true)
checkStrArg(presence.largeImageKey,31,"presence.largeImageKey",func,true)
checkStrArg(presence.largeImageText,127,"presence.largeImageText",func,true)
checkStrArg(presence.smallImageKey,31,"presence.smallImageKey",func,true)
checkStrArg(presence.smallImageText,127,"presence.smallImageText",func,true)
checkStrArg(presence.partyId,127,"presence.partyId",func,true)
checkIntArg(presence.partySize,32,"presence.partySize",func,true)
checkIntArg(presence.partyMax,32,"presence.partyMax",func,true)
checkStrArg(presence.matchSecret,127,"presence.matchSecret",func,true)
checkStrArg(presence.joinSecret,127,"presence.joinSecret",func,true)
checkStrArg(presence.spectateSecret,127,"presence.spectateSecret",func,true)
checkIntArg(presence.instance,8,"presence.instance",func,true)
local cpresence=ffi.new("struct DiscordRichPresence")
cpresence.state=presence.state
cpresence.details=presence.details
cpresence.startTimestamp=presence.startTimestamp or 0
cpresence.endTimestamp=presence.endTimestamp or 0
cpresence.largeImageKey=presence.largeImageKey
cpresence.largeImageText=presence.largeImageText
cpresence.smallImageKey=presence.smallImageKey
cpresence.smallImageText=presence.smallImageText
cpresence.partyId=presence.partyId
cpresence.partySize=presence.partySize or 0
cpresence.partyMax=presence.partyMax or 0
cpresence.matchSecret=presence.matchSecret
cpresence.joinSecret=presence.joinSecret
cpresence.spectateSecret=presence.spectateSecret
cpresence.instance=presence.instance or 0
RPC_C.Discord_UpdatePresence(cpresence)
end
function RPC.clearPresence()
RPC_C.Discord_ClearPresence()
end
local replyMap={
no=0,
yes=1,
ignore=2,
}
-- maybe let reply take ints too (0, 1, 2) and add constants to the module
function RPC.respond(userId,reply)
checkStrArg(userId,nil,"userId","discordRPC.respond")
assert(replyMap[reply],"Argument 'reply' to discordRPC.respond must be 'yes'|'no'|'ignore'")
RPC_C.Discord_Respond(userId,replyMap[reply])
end
-- garbage collection callback
RPC.gcDummy=newproxy(true)
getmetatable(RPC.gcDummy).__gc=function()
RPC.shutdown()
ready_proxy:free()
disconnected_proxy:free()
errored_proxy:free()
joinGame_proxy:free()
spectateGame_proxy:free()
joinRequest_proxy:free()
end
function RPC.ready(userId,username,discriminator,avatar)
print(string.format("Discord: ready (%s,%s,%s,%s)",userId,username,discriminator,avatar))
end
function RPC.disconnected(errorCode,message)
print(string.format("Discord: disconnected (%d: %s)",errorCode,message))
end
function RPC.errored(errorCode,message)
print(string.format("Discord: error (%d: %s)",errorCode,message))
end
function RPC.joinGame(joinSecret)
print(string.format("Discord: join (%s)",joinSecret))
end
function RPC.spectateGame(spectateSecret)
print(string.format("Discord: spectate (%s)",spectateSecret))
end
function RPC.joinRequest(userId,username,discriminator,avatar)
print(string.format("Discord: join request (%s,%s,%s,%s)",userId,username,discriminator,avatar))
RPC.respond(userId,'yes')
end
RPC.initialize(appId,true)
end
local MyRPC={
C=RPC_C,
RPC=RPC,
presence={
startTimestamp=os.time(),
state="Loading...",
details="",
largeImageKey='',
largeImageText="Techmino",
smallImageKey='',
smallImageText="",
},
}
---@class DiscordRPC.presence
---@field state? string
---@field details? string
---@field startTimestamp? number
---@field endTimestamp? number
---@field largeImageKey? string
---@field largeImageText? string
---@field smallImageKey? string
---@field smallImageText? string
---@field partyId? string
---@field partySize? number
---@field partyMax? number
---@field matchSecret? string
---@field joinSecret? string
---@field spectateSecret? string
---@field instance? number
---@overload fun()
---@overload fun(state: DiscordRPC.presence)
---@param state string
---@param details string
function MyRPC.update(state,details)
if state then
for k,v in next,
type(state)=='string'
and {state=state,details=details}
or state
do
MyRPC.presence[k]=v
end
end
if RPC then RPC.updatePresence(MyRPC.presence) end
end
return MyRPC

View File

@@ -26,7 +26,7 @@ end
return {
das=16,arr=6,
sddas=6,sdarr=6,
irs=false,ims=false,
logicalIRS=false,logicalIMS=false,
drop=6,lock=6,
wait=10,fall=25,
freshLimit=0,

View File

@@ -26,7 +26,7 @@ end
return {
das=16,arr=6,
sddas=3,sdarr=3,
irs=false,ims=false,
logicalIRS=false,logicalIMS=false,
drop=3,lock=3,
wait=10,fall=25,
freshLimit=0,

View File

@@ -26,7 +26,7 @@ end
return {
das=16,arr=6,
sddas=2,sdarr=2,
irs=false,ims=false,
logicalIRS=false,logicalIMS=false,
drop=2,lock=2,
wait=10,fall=25,
freshLimit=0,

View File

@@ -7,7 +7,7 @@ end
return {
das=16,arr=6,
sddas=1,sdarr=1,
irs=false,ims=false,
logicalIRS=false,logicalIMS=false,
drop=1,lock=1,
wait=10,fall=25,
freshLimit=0,

View File

@@ -149,7 +149,7 @@ return {
keyCancel={10,11,12,14,15,16,17,18,19,20},
das=16,arr=1,
minsdarr=1,
ihs=true,irs=true,ims=false,
logicalIRS=true,logicalIHS=true,logicalIMS=false,
mesDisp=function(P)
local D=P.modeData
GC.setColor(1,1,1,1)

View File

@@ -975,7 +975,7 @@ end
do-- function dumpBasicConfig()
local gameSetting={
-- Tuning
'das','arr','dascut','dropcut','sddas','sdarr',
'das','arr','dascut','irscut','dropcut','sddas','sdarr',
'ihs','irs','ims','RS',
-- System
@@ -1021,7 +1021,7 @@ do-- function resetGameData(args)
end
local gameSetting={
-- Tuning
'das','arr','dascut','dropcut','sddas','sdarr',
'das','arr','dascut','irscut','dropcut','sddas','sdarr',
'ihs','irs','ims','RS',
-- System
@@ -1051,6 +1051,8 @@ do-- function resetGameData(args)
GAME.rank=0
GAME.warnLVL0=0
GAME.warnLVL=0
GAME.pauseCount=0
GAME.pauseTime=0
if args:find'r' then
GAME.frameStart=0
GAME.recording=false
@@ -1058,8 +1060,6 @@ do-- function resetGameData(args)
else
GAME.frameStart=args:find'n' and 0 or 180-SETTING.reTime*60
GAME.seed=seed or math.random(1046101471)
GAME.pauseTime=0
GAME.pauseCount=0
GAME.saved=false
GAME.setting=_copyGameSetting()
GAME.tasUsed=false
@@ -1236,7 +1236,7 @@ do-- function pressKey(k)
end
do-- SETXXX(k) & ROOMXXX(k)
local warnList={
'das','arr','dascut','dropcut','sddas','sdarr',
'das','arr','dascut','irscut','dropcut','sddas','sdarr',
'ihs','irs','ims','RS',
'frameMul','highCam',
'VKSwitch','VKIcon','VKTrack','VKDodge',

View File

@@ -605,7 +605,7 @@ do-- Userdata tables
SETTING={-- Settings
-- Tuning
das=10,arr=2,
dascut=0,dropcut=0,
dascut=0,irscut=6,dropcut=0,
sddas=0,sdarr=2,
ihs=true,irs=true,ims=true,
holdMode='hold',
@@ -623,7 +623,7 @@ do-- Userdata tables
maxFPS=60,
frameMul=100,
locale='zh',
skinSet='crystal_scf',
skinSet='Crystal (Scf)',
skin={
1,7,11,3,14,4,9,
1,7,2,6,10,2,13,5,9,15,4,11,3,12,2,16,8,4,

View File

@@ -865,6 +865,11 @@ FNNS and {"Support 3",
"term",
"A special delay applied to DAS when a new block is spawned. When this happens, a small delay is added before the DAS starts timing, so that a piece doesn't start moving immediately when a sideways direction key is pressed.\nOther games may have similar mechanisms, but they may work differently.",
},
{"IRS cut",
"irscut icd",
"term",
"A special delay applied to IRS when a new block is spawned. When entry delay is disabled, this will delay IRS from being applied, allowing you to release the rotation button in the period to avoid a misdrop.",
},
{"Auto-lock cut",
"autolockcut mdcut",
"term",

View File

@@ -961,6 +961,11 @@ FNNS and {"サポート3",
"term",
"*Techmino用語*通常、ミが出現する前にDAS時間以上入力をしているとミが出現した瞬間に動き出します\nDASカットはこのような現象を減らすためにDAS時間以上入力していても出現時にDASカット分減算する機能です\n他のゲームにも似たようなものがありますが恐らく異なるでしょう",
},
-- {"IRS cut",
-- "irscut icd",
-- "term",
-- "A special delay applied to IRS when a new block is spawned. When entry delay is disabled, this will delay IRS from being applied, allowing you to release the rotation button in the period to avoid a misdrop.",
-- },
{"Auto-lock cut(自動設置カット)",
"autolockcut mdcut 自動 カット",
"term",

View File

@@ -654,6 +654,11 @@ Xem mục tiếp theo để biết thêm.
"term",
"Cơ chế đặc biệt sẽ được kích hoạt khi gạch mới xuất hiện. Khi kích hoạt, cơ chế này sẽ tăng DAS lên một chút để gạch không tự di chuyển ngay khi đang có phím được giữ.\n\nCác game khác có thể có tính năng tương tự nhưng cách hoạt động có thể khác nhau.",
},
-- {"IRS cut",
-- "irscut icd",
-- "term",
-- "A special delay applied to IRS when a new block is spawned. When entry delay is disabled, this will delay IRS from being applied, allowing you to release the rotation button in the period to avoid a misdrop.",
-- },
{"Auto-lock cut",
"nhom05e2 autolockcut",
"term",

View File

@@ -618,17 +618,17 @@ FNNS and {"赞助3",
"主流方块游戏中七种块的颜色会使用同一套彩虹配色:\nZ红 S绿 J蓝 L橙 T紫 O黄 I\n\nTechmino默认也使用这一套 “标准” 配色。",
},
{"提前旋转(IRS)",
"irs initialrotatesystem",
"tiqianxuanzhuan irs initialrotatesystem",
"term",
"Initial Rotation System 提前旋转系统\n方块出现前提前按下旋转后,出现时就是转好的形状,有时可以避免死亡。",
},
{"提前暂存(IHS)",
"ihs initialholdsystem",
"tiqianzancun ihs initialholdsystem",
"term",
"Initial Hold System 提前Hold系统\n方块出现前提前按下hold后直接出现hold里的方块有时可以避免死亡。",
},
{"提前移动(IMS)",
"ims initialmovesystem",
"tiqianyidong ims initialmovesystem",
"term",
"Initial Move System 提前移动系统\n方块出现前提前按住移动后出现时会朝移动方向偏一格有时可以避免死亡Techmino限定\n需要块出现时das已充满",
},
@@ -853,6 +853,11 @@ FNNS and {"赞助3",
"term",
"Techmino中指玩家的操作焦点转移到新方块的瞬间此时减小重置DAS计时器让自动移动不会立刻生效减少 “移动键松开晚了导致下一块一出来就立即开始移动” 的情况\n其他游戏中的DAS打断机制可能和Techmino的有区别仅供参考。",
},
{"IRS打断(ICD)",
"irscut icd daduan",
"term",
"由Electra设计新方块生成时触发IRS的特殊延迟。在没有生成延迟时这会让预输入的旋转动作延迟一段时间再生效允许晚一点松开旋转键防止md。",
},
{"误硬降打断(HCD)",
"autolockcut mdcut daduan",
"term",

View File

@@ -462,14 +462,13 @@ C. Gamepad
tas="TAS (T)",
},
net_menu={
league="Tech League",
ffa="FFA",
galaxim="Galaxim",
rooms="Rooms",
resetPW="Reset password",
logout="Log out",
},
net_league={
match="Find Match",
net_galaxim={
match="Enter Sim.",
},
net_rooms={
password="Password",
@@ -588,6 +587,7 @@ C. Gamepad
bg_on="Normal BG",
bg_off="No BG",
bg_custom="Custom BG",
bg_custom_base64="Paste image as BG\n(PNG/JPG in Base64)",
defaultBG="Default BG",
resetDbg="Reset to default",
lockBG="Lock BG",
@@ -623,6 +623,7 @@ C. Gamepad
das="DAS",arr="ARR",
dascut="DAS Cut",
irscut="IRS Cut",
dropcut="Auto-lock Cut",
sddas="Soft Drop DAS",sdarr="Soft Drop ARR",
ihs="Initial Hold",
@@ -996,7 +997,7 @@ C. Gamepad
"1 next 1 hold!",
"1 next 6 hold!",
"20G is actually a brand new game rule!",
"40-line Sprint WR: 13.650s by WestL",
"40-line Sprint WR: 13.430s by WestL",
"6 next 1 hold!",
"6 next 6 hold?!",
"A choke a day keeps record away",

View File

@@ -423,14 +423,13 @@ return {
tas="TAS (T)",
},
net_menu={
league="Liga Tech",
ffa="FFA",
-- galaxim="Galaxim", -- Galaxy+Simulation
rooms="Salas",
resetPW="Restabl. Contraseña",
logout="Desconec.",
},
net_league={
match="Buscar Match",
net_galaxim={
-- match="Enter Sim.", -- (Actively) Enter (the) (digital) Simulation of (a galaxy)
},
net_rooms={
password="Contraseña",
@@ -583,6 +582,7 @@ return {
das="DAS",arr="ARR",
dascut="Intrrp. de DAS",
irscut="Intrrp. de IRS",
dropcut="Intrrp. de Autocaída",
sddas="DAS de C. Ráp.",sdarr="ARR de C. Rápida",
ihs="Resv. Inicial",

View File

@@ -398,14 +398,13 @@ return {
-- tas="TAS (T)",
},
net_menu={
-- league="Tech League",
ffa="FFA",
-- galaxim="Galaxim", -- Galaxy+Simulation
rooms="Salons",
resetPW="Réinitialiser le mot de passe",
logout="Se déconnecter",
},
net_league={
match="Find Match",
net_galaxim={
-- match="Enter Sim.", -- (Actively) Enter (the) (digital) Simulation of (a galaxy)
},
net_rooms={
password="Mot de passe",
@@ -560,6 +559,7 @@ return {
das="DAS",arr="ARR",
dascut="DAS cut",
irscut="IRS cut",
-- dropcut="Auto-lock cut",
sddas="DAS de chute rapide",sdarr="ARR de chute rapide",
ihs="Réserve Initiale",

View File

@@ -424,14 +424,13 @@ return {
tas="TAS (T)",
},
net_menu={
league="Tech League",
ffa="FFA",
-- galaxim="Galaxim", -- Galaxy+Simulation
rooms="Ruang-ruang",
-- resetPW="Reset password",
logout="Log out",
},
net_league={
match="Cari Tandingan",
net_galaxim={
-- match="Enter Sim.", -- (Actively) Enter (the) (digital) Simulation of (a galaxy)
},
net_rooms={
password="Password",
@@ -585,6 +584,7 @@ return {
das="DAS",arr="ARR",
dascut="Gangguan DAS",
irscut="Gangguan IRS",
dropcut="Gangguan Auto-kunci",
sddas="DAS Jatuh",sdarr="ARR Jatuh",
ihs="Simpan Saat Tunda",
@@ -953,7 +953,7 @@ return {
"↑↑↓↓←→←→BA",
"$include<studio.h>",
"20G sebenarnya peraturan permainan baru!",
"Rekor dunia 40L: 13.650s dari WestL",
"Rekor dunia 40L: 13.430s dari WestL",
"Sistem pencapaian segera akan datang!",
"ALL SPIN!",
"Am G F G",

View File

@@ -468,14 +468,13 @@ C. ゲームパッド
tas="TAS (T)",
},
net_menu={
league="テクリーグ",
ffa="FFA",
-- galaxim="Galaxim", -- Galaxy+Simulation
rooms="ルーム",
resetPW="パスワード再設定",
logout="ログアウト",
},
net_league={
match="対戦相手を探す",
net_galaxim={
-- match="Enter Sim.", -- (Actively) Enter (the) (digital) Simulation of (a galaxy)
},
net_rooms={
password="パスワード",
@@ -629,6 +628,7 @@ C. ゲームパッド
das="DAS",arr="ARR",
dascut="DASカット",
irscut="IRSカット",
dropcut="自動設置カット",
sddas="ソフトドロップDAS",sdarr="ソフトドロップARR",
ihs="先行ホールド",
@@ -1006,7 +1006,7 @@ getTip={refuseCopy=true,
"20PCって何?",
"26TSDって何?",
"2つの回転を使ってみよう、3つ使うとさらにいいです!",
"40-line Sprint WR: 13.650s by WestL",
"40-line Sprint WR: 13.430s by WestL",
"6next 1hold!",
"6next 6hold?!",
"低音を響かせろ!",

View File

@@ -412,13 +412,12 @@ return {
-- tas="TAS (T)",
},
net_menu={
-- league="Tech League",
ffa="FFA",
-- galaxim="Galaxim", -- Galaxy+Simulation
rooms="Salas",
-- resetPW="Reset password",
-- logout="Log out",
},
net_league={
net_galaxim={
-- match="Find Match",
},
net_rooms={
@@ -573,6 +572,7 @@ return {
das="DAS",arr="ARR",
dascut="DAS cut",
irscut="IRS cut",
-- dropcut="Auto-lock cut",
sddas="Soft Drop DAS",sdarr="Soft Drop ARR",
ihs="Segurar Inicial",
@@ -948,7 +948,7 @@ return {
"1next 1hold!",
"1next 6hold!",
"Na verdade 20G é uma regra de jogo nova.",
"40-line Sprint WR: 13.650s by WestL",
"40-line Sprint WR: 13.430s by WestL",
"6next 1hold!",
"6next 6hold?!",
"ALL SPIN!",

View File

@@ -171,13 +171,12 @@ return {
tas="#&; (T)",
},
net_menu={
league="TL",
ffa="FFA",
-- galaxim="Galaxim", -- Galaxy+Simulation
rooms="< >",
resetPW="R ***",
logout="@_@x",
},
net_league={
net_galaxim={
match="!",
},
net_rooms={
@@ -330,6 +329,7 @@ return {
das="x---x x x",arr="x x-x-x",
dascut="x x ↓___x x",
irscut="'' !''x",
dropcut="↓_ !↓↓x",
sddas="↓---↓ ↓ ↓",sdarr="↓ ↓-↓-↓",
ihs="![ ]",

View File

@@ -197,7 +197,7 @@ return {
keySettingInstruction="Nhấn một phím để gán phím đó\nescape (esc): Hủy\nbackspace: Xoá",
customBGhelp=not MOBILE and "Kéo một tấm ảnh vào đây để áp dụng ảnh nền tuỳ chỉnh" or "Chưa hỗ trợ ảnh nền cho điện thoại",
customBGhelp="Kéo một tấm ảnh vào đây để áp dụng ảnh nền tuỳ chỉnh",
customBGloadFailed="Định dạng ảnh không được hỗ trợ",
errorMsg="Techmino bị lỗi và cần phải được khởi động lại\nBạn có thể gửi error log để giúp dev sửa game nhanh hơn.",
@@ -457,14 +457,13 @@ C. Tay cầm chơi game (Gamepad):
tas="TAS (T)",
},
net_menu={
league="Tech League",
ffa="FFA",
galaxim="Galaxim",
rooms="Danh sách phòng",
resetPW="Đặt lại mật khẩu",
logout="Đăng xuất",
},
net_league={
match="Tìm trận",
net_galaxim={
match="Bước vào mô phỏng",
},
net_rooms={
password="Mật khẩu",
@@ -584,6 +583,7 @@ C. Tay cầm chơi game (Gamepad):
bg_on="Ảnh nền thường",
bg_off="Không ảnh nền",
bg_custom="Ảnh nền tự chọn",
bg_custom_base64="Dán ảnh và cài thành ảnh nền\n(PNG/JPG ở format Base64)",
defaultBG="Nền mặc định",
resetDbg='Đặt lại',
lockBG="Khóa ảnh nền",
@@ -619,6 +619,7 @@ C. Tay cầm chơi game (Gamepad):
das="DAS",arr="ARR",
dascut="DAS cut",
irscut="IRS cut",
dropcut="Auto-lock cut",
sddas="DAS thả nhẹ",sdarr="ARR thả nhẹ",
ihs="Giữ tức thì",
@@ -964,7 +965,7 @@ C. Tay cầm chơi game (Gamepad):
['tech_l']= {"Tech B2B", "RẤT KHÓ", "Cố gắng không phá B2B!"},
['tech_l_plus']= {"Tech B2B", "RẤT KHÓ+", "Chỉ được clear Spin hoặc PC"},
['tech_finesse']= {"Tech FINESSE", "", "Không được phép có lỗi di chuyển!"},
['tech_finesse_f']= {"Tech FINESSE", "KHÔNG ĐƠN/ĐÔI", "Không được phép có lỗi di chuyển hoặc kiểu Xoá hàng thường!"},
['tech_finesse_f']= {"Tech FINESSE", "PLUS", "Không được phép có lỗi di chuyển hoặc kiểu Xoá hàng thường!"},
['tsd_e']= {"TSD Challenge", "DỄ", "Chỉ được làm T-Spin Double!"},
['tsd_h']= {"TSD Challenge", "KHÓ", "Chỉ được làm T-Spin Double!"},
['tsd_u']= {"TSD Challenge", "THÁCH ĐẤU", "Chỉ được làm T-Spin Double!"},
@@ -1003,7 +1004,7 @@ C. Tay cầm chơi game (Gamepad):
"6 next 1 hold!",
"6 next 6 hold?!",
"20G thực chất là một chế độ mới đấy!",
"Kỷ lục Sprint 40 hàng: 13.650s (WestL)",
"Kỷ lục Sprint 40 hàng: 13.430s (WestL)",
"Rất gần nhưng lại rất xa",
"ALL SPIN!",
"Am G F G",

View File

@@ -453,14 +453,13 @@ return {
tas="TAS (T)",
},
net_menu={
league="Tech League",
ffa="FFA",
galaxim="Galaxim",
rooms="房间列表",
resetPW="重置密码",
logout="退出登录",
},
net_league={
match="匹配对手",
net_galaxim={
match="进入模拟",
},
net_rooms={
password="密码",
@@ -613,6 +612,7 @@ return {
das="DAS:",arr="ARR:",
dascut="DAS打断:",
irscut="IRS打断",
dropcut="误硬降打断:",
sddas="软降DAS:",sdarr="软降ARR:",
ihs="提前Hold",
@@ -984,7 +984,7 @@ return {
"1next 6hold",
"3.1415926535897932384(\\d{3})",
"3next 1hold",
"40行世界纪录: 13.650s by WestL",
"40行世界纪录: 13.430s by WestL",
"6236326236327175",
"626in1",
"6next 1hold",
@@ -1207,7 +1207,7 @@ return {
"豆知识[016]本游戏的B2B是气槽机制和传统的开关机制不一样哦",
"豆知识[017]本游戏内置了几个休(yìng)闲(hé)小游戏哦~",
"豆知识[018]本游戏在设计的时候受到了大量其他块游甚至一些音游的启发",
"豆知识[019]必须要软降才能到达的位置都会判定为极简操作",
"豆知识[019]必须要软降才能到达的位置不会被判定为极简操作",
"豆知识[020]别看攻击效率不高,其实消四还是很强的",
"豆知识[021]别问游戏名字怎么取的,问就是随便想的",
"豆知识[022]不同人打40行最合适的方式不一样s1w/63/散消/s2w……",

View File

@@ -372,14 +372,13 @@ return {
tas="TAS(); (T)",
},
net_menu={
league="M.TechLeague();",
ffa="M.FFA",
galaxim="M.Galaxim();",
rooms="M.Rooms();",
resetPW="M.ResetPW",
logout="M.Logout();",
},
net_league={
match="TL.Match();",
net_galaxim={
match="GX.Enter();",
},
net_rooms={
password="Password=",
@@ -532,6 +531,7 @@ return {
das="Set.DAS=",arr="Set.SRR=",
dascut="Set.DASCut=",
irscut="Set.IRSCut=",
dropcut="Set.DropCut=",
sddas="Set.SDDAS=",sdarr="Set.SDARR=",
ihs="Set.IHS",

View File

@@ -424,14 +424,13 @@ return {
tas="TAS (T)",
},
net_menu={
league="Tech League",
ffa="FFA",
galaxim="Galaxim",
rooms="房間列表",
resetPW="重設密碼",
logout="登出",
},
net_league={
match="匹配對手",
net_galaxim={
match="进入模拟",
},
net_rooms={
password="密碼",
@@ -584,6 +583,7 @@ return {
das="DAS",arr="ARR",
dascut="DAS打斷",
irscut="IRS打斷",
dropcut="誤硬降打斷",
sddas="軟降DAS",sdarr="軟降ARR",
ihs="提前Hold",

View File

@@ -1,3 +1,5 @@
require'parts.scenes.customGame'.initialize()
return {
env={},
load=function()
@@ -12,10 +14,11 @@ return {
PLY.newPlayer(1)
local AItype=GAME.modeEnv.opponent:sub(1,2)
local AIlevel=tonumber(GAME.modeEnv.opponent:sub(-1))
local useHold=GAME.modeEnv.holdCount>0 and GAME.modeEnv.holdMode=='hold'
if AItype=='9S' then
PLY.newAIPlayer(2,BOT.template{type='9S',speedLV=2*AIlevel,hold=GAME.modeEnv.holdCount})
PLY.newAIPlayer(2,BOT.template{type='9S',speedLV=2*AIlevel,hold=useHold})
elseif AItype=='CC' then
PLY.newAIPlayer(2,BOT.template{type='CC',speedLV=2*AIlevel-1,next=math.floor(AIlevel*.5+1),hold=GAME.modeEnv.holdCount,node=20000+5000*AIlevel})
PLY.newAIPlayer(2,BOT.template{type='CC',speedLV=2*AIlevel-1,next=math.floor(AIlevel*.5+1),hold=useHold,node=20000+5000*AIlevel})
end
for _,P in next,PLY_ALIVE do

View File

@@ -1,6 +1,8 @@
local gc_setColor,gc_draw=love.graphics.setColor,love.graphics.draw
local ply_applyField=PLY.draw.applyField
require'parts.scenes.customGame'.initialize()
return {
env={
fkey1=function(P) P.modeData.showMark=1-P.modeData.showMark end,
@@ -54,10 +56,11 @@ return {
local AItype=GAME.modeEnv.opponent:sub(1,2)
local AIlevel=tonumber(GAME.modeEnv.opponent:sub(-1))
PLY.newPlayer(1)
local useHold=GAME.modeEnv.holdCount>0 and GAME.modeEnv.holdMode=='hold'
if AItype=='9S' then
PLY.newAIPlayer(2,BOT.template{type='9S',speedLV=2*AIlevel,hold=GAME.modeEnv.holdCount})
PLY.newAIPlayer(2,BOT.template{type='9S',speedLV=2*AIlevel,hold=useHold})
elseif AItype=='CC' then
PLY.newAIPlayer(2,BOT.template{type='CC',speedLV=2*AIlevel-1,next=math.floor(AIlevel*.5+1),hold=GAME.modeEnv.holdCount,node=20000+5000*AIlevel})
PLY.newAIPlayer(2,BOT.template{type='CC',speedLV=2*AIlevel-1,next=math.floor(AIlevel*.5+1),hold=useHold,node=20000+5000*AIlevel})
end
end,
savePrivate=function()

View File

@@ -55,6 +55,12 @@ return {
P.modeData.colorSet[i]=P.holeRND:random(25)
end
P.randomizer_spinren=randomizer(P.holeRND)
-- table.insert(P.field,1,LINE.new(0))
-- P.field[1][1]=18
-- P.field[1][2]=18
-- P.field[1][9]=18
-- P.field[1][10]=18
-- table.insert(P.visTime,1,LINE.new(20))
P:pushLineList(get_lines(18,P))
P.fieldBeneath=0
end,

View File

@@ -29,8 +29,8 @@ local NET={
onlineCount="_",
textBox=WIDGET.newTextBox{name='texts',x=340,y=80,w=600,h=560},
inputBox=WIDGET.newInputBox{name='input',x=340,y=660,w=600,h=50,limit=256},
textBox=WIDGET.newTextBox{name='texts',x=20,y=110,w=980,h=500},
inputBox=WIDGET.newInputBox{name='input',x=20,y=630,w=980,h=50,limit=256},
}
function NET.freshRoomAllReady()
@@ -187,17 +187,48 @@ function NET.getAvatar(uid)
end
end)
end
function NET.getNotice(lang,count)
local noticeLang={
en='en_us',
fr='en_us', -- fr_fr
es='en_us', -- es_es
id='en_us', -- id_id
pt='en_us', -- pt_pt
symbol='en_us', -- gl_os
ja='en_us', -- ja_jp
vi='en_us', -- vi_vn
zh='zh_cn',
zh_trad='zh_tw',
zh_code='zh_cn',
}
function NET.launchNotice()
TASK.new(function()
local res=getMsg({
pool='getNotice',
path='/techmino/api/v1/notice?language='..noticeLang[SETTING.locale]..'&lastCount=1',
},6.26)
if res and res.code==200 then
local opt=res.data.contents[1]
if opt then
MES.new('info',opt.content,12.6)
else
MES.new('info',text.Techrater.NoticeManager.noticeNotFound)
end
end
end)
end
function NET.getNotice(count)
WAIT{timeout=6.26}
TASK.new(function()
local res=getMsg({
pool='getNotice',
path='/techmino/api/v1/notice?language='..(lang or 'zh_cn')..'&lastCount='..(count or 5),
path='/techmino/api/v1/notice?language='..noticeLang[SETTING.locale]..'&lastCount='..(count or 5),
},6.26)
if res and res.code==200 and type(res.data)=='string' then
local dataStr=""
SCN.go('notice',nil,dataStr)
if res and res.code==200 then
WAIT.interrupt()
SCN.go('notice',nil,noticeLang[SETTING.locale],res.data.contents)
end
end)
end
@@ -393,10 +424,19 @@ function NET.wsCallBack.room_chat(body)
if SCN.cur~='net_game' then return end
TASK.unlock('receiveMessage')
TASK.lock('receiveMessage',1)
NET.textBox:push{
COLOR.Z,_getFullName(body.data.playerId).." ",
COLOR.N,body.data.message,
}
local name=_getFullName(body.data.playerId).." "
-- P/s: we need to wrap both name and message, not just only message
local _,msgWrapped=FONT.get(NET.inputBox.font):getWrap(name..body.data.message,950)
-- We don't want to see the name repeat twice :skull:
msgWrapped[1]=string.gsub(msgWrapped[1],name,"",1)
-- Push the name in white and first line of message in blue first
NET.textBox:push{COLOR.Z,name,COLOR.N,msgWrapped[1]}
for i, line in ipairs(msgWrapped) do
if i ~= 1 then
NET.textBox:push{COLOR.N,msgWrapped[i]}
end
end
end
function NET.wsCallBack.room_create(body)
MES.new('check',text.createRoomSuccessed)

View File

@@ -29,11 +29,13 @@ return {
{font=65,name="怀沙"},
{font=65,name="星街书婉"},
{font=65,name="老板来两份薯条"},
{font=65,name="yumucy"},
{font=65,name="[**昆]"},
{font=65,name="[**浩]"},
{font=65,name="sakurw"},
{font=65,name="[**霖]"},
{font=65,name="KK"},
{font=65,name="武仰绍"},
{font=25,name="八零哥"},
{font=25,name="蕴空之灵"},
@@ -151,4 +153,9 @@ return {
{font=25,name="电蜥蜴"},
{font=25,name="浮芙fufu"},
{font=25,name="NOname_awa"},
{font=65,name="liaotianhao"},
{font=65,name="Aquamarine"},
{font=65,name="jacky"},
{font=65,name="方块丶小刘"},
{font=65,name="DX3906"},
}

View File

@@ -177,12 +177,12 @@ local function _drawField(P,showInvis)
if P.falling==0 then-- Blocks only
if ENV.upEdge then
gc_setShader(shader_lighter)
gc_translate(0,-4)
gc_translate(0,-6)
-- <drawRow>
for j=start,min(start+21,#F) do _drawRow(texture,j,V[j],F[j]) end
-- </drawRow>
gc_setShader(shader_fieldSatur)
gc_translate(0,4)
gc_translate(0,6)
else
gc_setShader(shader_fieldSatur)
end
@@ -197,7 +197,7 @@ local function _drawField(P,showInvis)
if ENV.upEdge then
gc_push('transform')
gc_setShader(shader_lighter)
gc_translate(0,-4)
gc_translate(0,-6)
-- <drawRow>
for j=start,min(start+21,#F) do
while j==P.clearingRow[h] do

View File

@@ -1,8 +1,9 @@
return {
das=10,arr=2,
dascut=0,dropcut=0,
dascut=0,irscut=6,dropcut=0,
sddas=2,sdarr=2,
ihs=true,irs=true,ims=true,
logicalIHS=true,logicalIRS=true,logicalIMS=true,
ghostType='gray',
block=true,ghost=.3,center=1,
@@ -40,7 +41,7 @@ return {
RS='TRS',
sequence='bag',
seqData={1,2,3,4,5,6,7},
skinSet='crystal_scf',
skinSet='Crystal (Scf)',
face=false,skin=false,
mission=false,
@@ -62,10 +63,16 @@ return {
mindas=0,minarr=0,minsdarr=0,
noInitSZO=false,
mesDisp={},
hook_drop={},
hook_die={},
task={},
-- Some Events are registered in player/init.lua, see "tableNeedMerge"
extraEvent={
{'attack',4},
},
extraEventHandler={
attack=function(P,P2,...)
P:beAttacked(P2,...)
end,
},
eventSet="X",
bg='none',bgm='race',

View File

@@ -236,17 +236,8 @@ local function _loadRemoteEnv(P,confStr)-- Load gameEnv
end
end
end
local function _mergeFuncTable(f,L)
if type(f)=='function' then
ins(L,f)
elseif type(f)=='table' then
for i=1,#f do
ins(L,f[i])
end
end
return L
end
local hooks = {
local tableNeedMerge={
'task',
'mesDisp',
'hook_left',
'hook_left_manual',
@@ -259,36 +250,63 @@ local hooks = {
'hook_spawn',
'hook_hold',
'hook_die',
'task'
'extraEvent',
}
for _,k in next,tableNeedMerge do gameEnv0[k]={} end
local function _mergeFuncTable(f,L)
if type(f)=='function' then
ins(L,f)
elseif type(f)=='table' then
for i=1,#f do
ins(L,f[i])
end
end
return L
end
local function _applyGameEnv(P)-- Finish gameEnv processing
local ENV=P.gameEnv
-- Apply events
for i=1,#hooks do
ENV[hooks[i]]=_mergeFuncTable(ENV[hooks[i]],{})
-- Create event tables
for i=1,#tableNeedMerge do
ENV[tableNeedMerge[i]]=_mergeFuncTable(ENV[tableNeedMerge[i]],{})
end
-- Apply eventSet
if ENV.eventSet and ENV.eventSet~="X" then
if type(ENV.eventSet)=='string' then
local eventSet=require('parts.eventsets.'..ENV.eventSet)
if eventSet then
for k,v in next,eventSet do
if TABLE.find(hooks,k) then
_mergeFuncTable(v,ENV[k])
elseif type(v)=='table' then
ENV[k]=TABLE.copy(v)
while true do
if not (ENV.eventSet and ENV.eventSet~="X") then
break
end
if type(ENV.eventSet)~='string' then
MES.new('warn',"Wrong event set type: "..type(ENV.eventSet))
break
end
local eventSet=require('parts.eventsets.'..ENV.eventSet)
if not eventSet then
MES.new('warn',"No event set called: "..ENV.eventSet)
break
end
for k,v in next,eventSet do
if k=='extraEventHandler' then
for ev,handler in next,v do
if ENV.extraEventHandler[ev] then
local prevHandler=ENV.extraEventHandler[ev]
ENV.extraEventHandler[ev]=function(...)
prevHandler(...)
handler(...)
end
else
ENV[k]=v
ENV.extraEventHandler[ev]=handler
end
end
elseif TABLE.find(tableNeedMerge,k) then
_mergeFuncTable(v,ENV[k])
elseif type(v)=='table' then
ENV[k]=TABLE.copy(v)
else
MES.new('warn',"No event set called: "..ENV.eventSet)
ENV[k]=v
end
else
MES.new('warn',"Wrong event set type: "..type(ENV.eventSet))
end
break
end
P._20G=ENV.drop==0

View File

@@ -270,27 +270,61 @@ function Player:act_rotRight()
if not self.control then return end
if self.cur then
self.ctrlCount=self.ctrlCount+1
if self.bufferedIRS then
-- Ensure IRS is spent before the rotation is processed so it doesn't throw things off.
-- This is so that if you for instance, are holding left IRS and then rotate right, it doesn't process
-- the left and right rotates in the reverse order.
self.keyPressing[3]=false
self:resolveIRS()
self.keyPressing[3]=true
end
self:spin(1)
self:_triggerEvent('hook_rotate',1)
self.keyPressing[3]=false
-- Disable held inputs if IRS is off
if not self.gameEnv.irs then
self.keyPressing[3]=false
end
end
end
function Player:act_rotLeft()
if not self.control then return end
if self.cur then
self.ctrlCount=self.ctrlCount+1
if self.bufferedIRS then
-- Ensure IRS is spent before the rotation is processed so it doesn't throw things off.
-- This is so that if you for instance, are holding left IRS and then rotate right, it doesn't process
-- the left and right rotates in the reverse order.
self.keyPressing[4]=false
self:resolveIRS()
self.keyPressing[4]=true
end
self:spin(3)
self:_triggerEvent('hook_rotate',3)
self.keyPressing[4]=false
-- Disable held inputs if IRS is off
if not self.gameEnv.irs then
self.keyPressing[4]=false
end
end
end
function Player:act_rot180()
if not self.control then return end
if self.cur then
self.ctrlCount=self.ctrlCount+2
if self.bufferedIRS then
-- Ensure IRS is spent before the rotation is processed so it doesn't throw things off.
-- This is so that if you for instance, are holding left IRS and then rotate right, it doesn't process
-- the left and right rotates in the reverse order.
self.keyPressing[5]=false
self:resolveIRS()
self.keyPressing[5]=true
end
self:spin(2)
self:_triggerEvent('hook_rotate',2)
self.keyPressing[5]=false
-- Disable held inputs if IRS is off
if not self.gameEnv.irs then
self.keyPressing[5]=false
end
end
end
function Player:act_hardDrop()
@@ -300,6 +334,10 @@ function Player:act_hardDrop()
if self.lastPiece.autoLock and self.frameRun-self.lastPiece.frame<ENV.dropcut then
SFX.play('drop_cancel',.3)
else
if self.bufferedIRS then
-- If the player drops quicker than their IRS cut delay, make sure IRS still resolves.
self:resolveIRS()
end
if self.curY>self.ghoY then
self:createDropFX()
self.curY=self.ghoY
@@ -344,7 +382,10 @@ function Player:act_hold()
if not self.control then return end
if self.cur then
if self:hold() then
self.keyPressing[8]=false
-- Disable held inputs if IHS is off
if not self.gameEnv.ihs then
self.keyPressing[8]=false
end
self:_triggerEvent('hook_hold')
end
end
@@ -701,6 +742,40 @@ function Player:_triggerEvent(eventName)
return true
end
end
function Player:extraEvent(eventName,...)
if not (self.gameEnv.extraEvent and self.gameEnv.extraEventHandler) then return end
local list=self.gameEnv.extraEvent
local eventID
for i=1,#list do
if list[i][1]==eventName then
eventID=i
break
end
end
if not eventID then
MES.new('warn',"Extra event '"..eventName.."' doesn't exist in this mode")
return
end
local SELF
-- Trigger for all non-remote players
for _,p in next,PLAYERS do
if p.type~='remote' then
if p.type=='human' then
SELF=p
end
self.gameEnv.extraEventHandler[eventName](p,self,...)
end
end
ins(GAME.rep,SELF.frameRun)
ins(GAME.rep,64+eventID)
ins(GAME.rep,self.sid)
local data={...}
for i=1,#data do
ins(GAME.rep,data[i])
end
end
function Player:getHolePos()-- Get a good garbage-line hole position
if self.garbageBeneath==0 then
@@ -833,34 +908,13 @@ function Player:ifoverlap(bk,x,y)
end
end
end
function Player:attack(R,send,time,line,fromStream)
if GAME.net then
if self.type=='human' then-- Local player attack others
ins(GAME.rep,self.frameRun)
ins(GAME.rep,
R.sid+
send*0x100+
time*0x10000+
line*0x100000000+
0x2000000000000
)
self:createBeam(R,send)
end
if fromStream and R.type=='human' then-- Local player receiving lines
ins(GAME.rep,R.frameRun)
ins(GAME.rep,
self.sid+
send*0x100+
time*0x10000+
line*0x100000000+
0x1000000000000
)
R:receive(self,send,time,line)
end
else
R:receive(self,send,time,line)
self:createBeam(R,send)
end
function Player:attack(R,send,time,line)
self:extraEvent('attack',R.sid,send,time,line)
end
function Player:beAttacked(P2,sid,send,time,line)
if self==P2 or self.sid~=sid then return end
self:receive(P2,send,time,line)
P2:createBeam(self,send)
end
function Player:receive(A,send,time,line)
self.lastRecv=A
@@ -1151,34 +1205,67 @@ function Player:resetBlock()-- Reset Block's position and execute I*S
self.curY=y
self.minY=y+sc[1]
local ENV=self.gameEnv
-- In the game settings, there are user-set control flags for irs,irs,ims
-- These control in what way the user can buffer their rotate/hold/move inputs.
-- (If enabled, they may hold these inputs from the previous piece instead of just Entry Delay)
-- And mode-set flags for logicalIRS,logicalIHS,logicalIMS
-- These control whether IRS/IHS/IMS are effective in modifying what you can do.
-- (For instance, changing your piece's spawn position in 20g, or saving you from a death).
-- If logical IRS is disabled, the player may still IRS, but it will just buffer their input,
-- not actually allowing them to survive in a way they could not without.
local pressing=self.keyPressing
-- IMS
if self.gameEnv.ims and (pressing[1] and self.movDir==-1 or pressing[2] and self.movDir==1) and self.moving>=self.gameEnv.das then
local x=self.curX+self.movDir
if not self:ifoverlap(C.bk,x,y) then
self.curX=x
-- IMS is enabled only when logicalIMS is enabled, because otherwise, it's just faster DAS.
if ENV.logicalIMS and (pressing[1] and self.movDir==-1 or pressing[2] and self.movDir==1) and self.moving>=self.gameEnv.das then
-- To avoid a top-out
if self:ifoverlap(C.bk,self.curX,self.curY) then
-- Always perform the shift, since you're topped out anyway
self.curX=self.curX+self.movDir
elseif ENV.wait>0 and ENV.ims then
-- Otherwise, only check IMS if it's enabled and you're in a mode with entry delay (20g)
local x=self.curX+self.movDir
if not self:ifoverlap(C.bk,x,y) then
self.curX=x
end
end
end
-- IRS
if self.gameEnv.irs then
if not ENV.logicalIRS then
-- If logical IRS is disabled, all IRS inputs will be buffered to prevent survival.
self.bufferedIRS=true
self.bufferedDelay=0
if ENV.wait==0 then
self.bufferedDelay=ENV.irscut
end
elseif ENV.wait==0 and ENV.irscut>0 and not self:ifoverlap(C.bk,self.curX,self.curY) then
-- If IRS cut delay is enabled and we aren't currently dying, buffer the input instead.
self.bufferedIRS=true
self.bufferedDelay=ENV.irscut
else
-- If we're currently dying or in an entry-delay mode (20g), perform the rotation right away.
if pressing[5] then
self:spin(2,true)
self:act_rot180()
elseif pressing[3] then
if pressing[4] then
self:spin(2,true)
self:act_rot180()
else
self:spin(1,true)
self:act_rotRight()
end
elseif pressing[4] then
self:spin(3,true)
self:act_rotLeft()
end
end
-- Disable held inputs if IRS is off
if not ENV.irs then
pressing[3],pressing[4],pressing[5]=false,false,false
end
-- DAS cut
if self.gameEnv.dascut>0 then
self.moving=self.moving-(self.moving>0 and 1 or -1)*self.gameEnv.dascut
if ENV.dascut>0 then
self.moving=self.moving-(self.moving>0 and 1 or -1)*ENV.dascut
end
-- Spawn SFX
@@ -1498,9 +1585,29 @@ function Player:_popNext(ifhold)-- Pop nextQueue to hand
local pressing=self.keyPressing
-- IHS
if not ifhold and pressing[8] and ENV.ihs and self.holdTime>0 then
self:hold(true)
pressing[8]=false
if not ifhold and pressing[8] and self.holdTime>0 then
if not ENV.logicalIHS then
-- If logical IHS is disabled, all IHS inputs will be buffered to prevent survival.
self.bufferedIRS=true
self.bufferedIHS=true
self.bufferedDelay=0
if ENV.wait==0 then
self.bufferedDelay=ENV.irscut
end
elseif ENV.wait==0 and ENV.irscut>0 and not self:willDieWith(self.cur) then
-- If IRS cut delay is enabled and we're not currently dying, buffer the input instead.
self.bufferedIRS=true
self.bufferedIHS=true
self.bufferedDelay=ENV.irscut
self:resetBlock()
else
-- If we're currently dying or in an entry-delay mode (20g), perform the hold immediately.
self:hold(true)
end
-- Disable held inputs if IHS is off
if not ENV.ihs then
pressing[8]=false
end
else
self:resetBlock()
end
@@ -1814,7 +1921,7 @@ do
end
end
local yomi = ""
local yomi=''
piece.spin,piece.mini=dospin,false
piece.pc,piece.hpc=false,false
@@ -1825,7 +1932,7 @@ do
cscore=(spinSCR[C.name] or spinSCR[8])[cc]
if self.b2b>800 then
self:showText(text.b3b..text.block[C.name]..text.spin..text.clear[cc],0,-30,35,'stretch')
yomi = yomi..text.b3b..text.block[C.name]..text.spin..text.clear[cc]
yomi=yomi..text.b3b..text.block[C.name]..text.spin..text.clear[cc]
atk=b2bATK[cc]+cc*.5
exblock=exblock+1
cscore=cscore*2
@@ -1835,7 +1942,7 @@ do
end
elseif self.b2b>=50 then
self:showText(text.b2b..text.block[C.name]..text.spin..text.clear[cc],0,-30,35,'spin')
yomi = yomi..text.b2b..text.block[C.name]..text.spin..text.clear[cc]
yomi=yomi..text.b2b..text.block[C.name]..text.spin..text.clear[cc]
atk=b2bATK[cc]
cscore=cscore*1.2
Stat.b2b=Stat.b2b+1
@@ -1844,13 +1951,13 @@ do
end
else
self:showText(text.block[C.name]..text.spin..text.clear[cc],0,-30,45,'spin')
yomi = yomi..text.block[C.name]..text.spin..text.clear[cc]
yomi=yomi..text.block[C.name]..text.spin..text.clear[cc]
atk=2*cc
end
sendTime=20+atk*20
if mini then
self:showText(text.mini,0,-80,35,'appear')
yomi = text.mini..' '..yomi
yomi=text.mini..' '..yomi
atk=atk*.25
sendTime=sendTime+60
cscore=cscore*.5
@@ -1871,7 +1978,7 @@ do
cscore=clearSCR[cc]
if self.b2b>800 then
self:showText(text.b3b..text.clear[cc],0,-30,50,'fly')
yomi = text.b3b..text.clear[cc]..yomi
yomi=text.b3b..text.clear[cc]..yomi
atk=4*cc-10
sendTime=100
exblock=exblock+1
@@ -1882,7 +1989,7 @@ do
end
elseif self.b2b>=50 then
self:showText(text.b2b..text.clear[cc],0,-30,50,'drive')
yomi = text.b2b..text.clear[cc]..yomi
yomi=text.b2b..text.clear[cc]..yomi
sendTime=80
atk=3*cc-7
cscore=cscore*1.3
@@ -1892,7 +1999,7 @@ do
end
else
self:showText(text.clear[cc],0,-30,70,'stretch')
yomi = text.clear[cc]..yomi
yomi=text.clear[cc]..yomi
sendTime=60
atk=2*cc-4
end
@@ -1900,7 +2007,7 @@ do
piece.special=true
else
self:showText(text.clear[cc],0,-30,35,'appear',(8-cc)*.3)
yomi = text.clear[cc]..yomi
yomi=text.clear[cc]..yomi
atk=cc-.5
sendTime=20+floor(atk*20)
cscore=cscore+clearSCR[cc]
@@ -1919,7 +2026,7 @@ do
atk=atk+1
end
self:showText(text.cmb[min(cmb,21)],0,25,15+min(cmb,15)*5,cmb<10 and 'appear' or 'flicker')
yomi = yomi..' '..text.cmb[min(cmb,21)]
yomi=yomi..' '..text.cmb[min(cmb,21)]
cscore=cscore+min(50*cmb,500)*(2*cc-1)
end
@@ -2447,6 +2554,28 @@ local function _updateFX(P,dt)
end
end
end
function Player:resolveIRS()
if self.bufferedIHS then
self:hold(true)
self.bufferedIHS=false
end
self.bufferedIRS=false
local pressing=self.keyPressing
if pressing[5] then
self:act_rot180()
elseif pressing[3] then
if pressing[4] then
self:act_rot180()
else
self:act_rotRight()
end
elseif pressing[4] then
self:act_rotLeft()
end
end
local function update_alive(P,dt)
local ENV=P.gameEnv
@@ -2505,6 +2634,18 @@ local function update_alive(P,dt)
end
end
-- Buffer IRS after IRS cut delay has elapsed.
-- The purpose of this is to allow the player to release their rotate key during the IRS cut delay,
-- which will allow them to avoid accidentally using IRS.
if P.bufferedDelay then
P.bufferedDelay=P.bufferedDelay-1
if P.bufferedDelay<=0 then
if P.bufferedIRS then
P:resolveIRS()
end
end
end
-- Moving pressed
if P.movDir~=0 then
local das,arr=ENV.das,ENV.arr
@@ -2701,44 +2842,32 @@ local function update_alive(P,dt)
end
local function update_streaming(P)
local eventTime=P.stream[P.streamProgress]
while eventTime and P.frameRun==eventTime do
while eventTime and P.frameRun==eventTime or eventTime==0 do
local event=P.stream[P.streamProgress+1]
if event==0 then-- Just wait
elseif event<=32 then-- Press key
P:pressKey(event)
elseif event<=64 then-- Release key
P:releaseKey(event-32)
elseif event>0x2000000000000 then-- Sending lines
local sid=event%0x100
local amount=floor(event/0x100)%0x100
local time=floor(event/0x10000)%0x10000
local line=floor(event/0x100000000)%0x10000
for _,p in next,PLY_ALIVE do
if p.sid==sid then
P.netAtk=P.netAtk+amount
if P.netAtk~=P.stat.send then-- He cheated or just desynchronized to death
MES.new('warn',"#"..P.uid.." desynchronized")
NET.player_finish({reason='desync'})
P:lose(true)
return
end
P:attack(p,amount,time,line,true)
P:createBeam(p,amount)
elseif event<=128 then-- Extra Event
local eventName=P.gameEnv.extraEvent[event-64][1]
local eventParamCount=P.gameEnv.extraEvent[event-64][2]
local sourceSid=P.stream[P.streamProgress+2]
local paramList={}
for i=1,eventParamCount do
ins(paramList,P.stream[P.streamProgress+2+i])
end
P.streamProgress=P.streamProgress+eventParamCount+1
local SRC
for _,p in next,PLAYERS do
if p.sid==sourceSid then
SRC=p
break
end
end
elseif event>0x1000000000000 then-- Receiving lines
local sid=event%0x100
for _,p in next,PLY_ALIVE do
if p.sid==sid then
P:receive(
p,
floor(event/0x100)%0x100,-- amount
floor(event/0x10000)%0x10000,-- time
floor(event/0x100000000)%0x10000-- line
)
break
end
if SRC then
P.gameEnv.extraEventHandler[eventName](P,SRC,unpack(paramList))
end
end
P.streamProgress=P.streamProgress+2
@@ -2801,7 +2930,7 @@ function Player:update(dt)
end
while self.trigFrame>=1 do
if self.streamProgress then
local frameDelta-- Time between now and end of stream
local dataDelta -- How much data wating to be process
if self.type=='remote' then
if self.loseTimer then
self.loseTimer=self.loseTimer-1
@@ -2810,25 +2939,26 @@ function Player:update(dt)
self:lose(true)
end
end
frameDelta=(self.stream[#self.stream-1] or 0)-self.frameRun
if frameDelta==0 then frameDelta=nil end
dataDelta=#self.stream-self.streamProgress
else
frameDelta=0
dataDelta=1
end
if frameDelta then
if dataDelta>0 then
for _=1,
self.loseTimer and min(frameDelta,
-- Speed up to finish
self.loseTimer and min(dataDelta,
self.loseTimer>16 and 2 or
self.loseTimer>6.2 and 12 or
self.loseTimer>2.6 and 260 or
2600
) or
frameDelta<26 and 1 or
frameDelta<50 and 2 or
frameDelta<80 and 3 or
frameDelta<120 and 5 or
frameDelta<160 and 7 or
frameDelta<200 and 10 or
-- Chasing faster when slower
dataDelta<26 and 1 or
dataDelta<42 and 2 or
dataDelta<62 and 3 or
dataDelta<70.23 and 5 or
dataDelta<94.2 and 7 or
dataDelta<126 and 10 or
20
do
update_streaming(self)
@@ -2877,7 +3007,7 @@ function Player:revive()
SFX.play('emit')
end
function Player:torikanEnd(requiredTime)
if self.stat.time < requiredTime then
if self.stat.time<requiredTime then
return false
end
self:_die()

View File

@@ -120,20 +120,17 @@ local seqGenerators={
-- Pick a mino from pool
local tryTime=0
local r
repeat-- ::REPEAT_pickAgain::
local pickAgain
repeat
r=_poolPick()-- Random mino-index in pool
local duplicated
for i=1,len do
if r==history[i] then
tryTime=tryTime+1
if tryTime<hisLen then
pickAgain=true
break-- goto REPEAT_pickAgain
end
duplicated=true
break
end
end
if not pickAgain then break end
until true
tryTime=tryTime+1
until not duplicated or tryTime>hisLen
-- Give mino to player & update history
if history[1]~=0 then

View File

@@ -29,6 +29,7 @@ function scene.enter()
slide=true
pathVis=true
revKB=false
DiscordRPC.update("Playing 15-Puzzle")
end
local function moveU(x,y)

View File

@@ -234,6 +234,7 @@ function scene.enter()
tapControl=false
startTime=0
reset()
DiscordRPC.update("Playing 2048")
end
function scene.mouseDown(x,y,k)

View File

@@ -40,6 +40,7 @@ function scene.enter()
startTime=0
time=0
state=0
DiscordRPC.update("Spamming keyboard")
end
function scene.keyDown(key,isRep)

View File

@@ -100,6 +100,7 @@ function scene.enter()
restart()
BGM.play('truth')
BG.set('rainbow')
DiscordRPC.update("Playing Ultimate Tic-Tac-Toe")
end
function scene.mouseMove(x,y)

View File

@@ -360,6 +360,7 @@ function scene.enter()
drawing=false
numScale=1
BGM.play('truth')
DiscordRPC.update("Playing Arithmetic")
end
function scene.keyDown(key,isRep)

View File

@@ -28,6 +28,7 @@ function scene.enter()
BG.set('none')
BGM.stop()
reg,val,sym=false,"0",false
DiscordRPC.update("Calculating something")
end
function scene.leave()
BGM.play()

View File

@@ -15,6 +15,7 @@ function scene.enter()
ex,ey=626,260
BG.set('matrix')
BGM.play('hang out')
DiscordRPC.update("Shooting cannon balls")
end
function scene.keyDown(key,isRep)

View File

@@ -1034,6 +1034,7 @@ userG.the_key=first_key
function scene.enter()
WIDGET.focus(inputBox)
BG.set('none')
DiscordRPC.update("Hacking the system")
end
function scene.wheelMoved(_,y)

View File

@@ -59,6 +59,7 @@ function scene.enter()
gc.setLineJoin('bevel')
BGM.play('push')
BG.set('none')
DiscordRPC.update("Playing Cubefield")
end
function scene.touchDown(x)

View File

@@ -38,6 +38,7 @@ function scene.enter()
state='menu'
BGM.play('hang out')
BG.set('space')
DiscordRPC.update("Playing Dropper")
end
function scene.keyDown(key,isRep)

View File

@@ -201,6 +201,7 @@ function scene.enter()
reset()
BG.set('fixColor',.26,.26,.26)
BGM.play(bgm)
DiscordRPC.update("Avoiding touching white tiles")
end
local function touch(n)

View File

@@ -249,6 +249,7 @@ function scene.enter()
invis=false
newGame()
BGM.play('truth')
DiscordRPC.update("Playing Link")
end
function scene.keyDown(key,isRep)

View File

@@ -109,6 +109,7 @@ function scene.enter()
BG.set('fixColor',.26,.62,.26)
_newGame()
selected=false
DiscordRPC.update("Playing Mahjong")
end
function scene.mouseMove(x,y)

View File

@@ -36,6 +36,7 @@ function scene.enter()
input=''
showNum='memoriZe'
BGM.play('reason')
DiscordRPC.update("Playing memoriZe")
end
function scene.keyDown(key,isRep)

View File

@@ -95,6 +95,7 @@ function scene.enter()
generateVKey()
_notHoldCS()
_showVirtualKey(MOBILE)
DiscordRPC.update("Playing music")
end
function scene.leave()

View File

@@ -40,6 +40,7 @@ function scene.enter()
end
BG.set('none')
BGM.play('dream')
DiscordRPC.update("Playing polyforge")
end
function scene.keyDown(key,isRep)

View File

@@ -35,6 +35,7 @@ function scene.enter()
vy=0,
y0=false,
}
DiscordRPC.update("Playing Pong")
end
local function start()

View File

@@ -18,6 +18,7 @@ end
function scene.enter()
reset()
BG.set('none')
DiscordRPC.update("Playing Reflect")
end
function scene.keyDown(key,isRep)

View File

@@ -25,6 +25,7 @@ function scene.enter()
mistake=0
state=0
progress=0
DiscordRPC.update("Playing Schulte Grid")
end
local function newBoard()

View File

@@ -15,6 +15,10 @@ end)
local scene={}
function scene.enter()
DiscordRPC.update("Playing a non-sense thing")
end
function scene.keyDown(key,isRep)
if isRep then return end
if key=='space' or key=='return' then

Some files were not shown because too many files have changed in this diff Show More