+ qft
timer
Quarterframe Timer (Experimental)
- Noki Doki
- 0.5
- Nov 28, 2021
+ QFタイマー
+ Noki Doki, sup39(サポミク)
+ 1.0
+ Apr 08, 2022
+ drawText
Adds an in-game timer to every level, starting on the last black frame after the
loading screen and ending as soon as the 'Shine Get' animation starts
@@ -1250,382 +1253,10 @@
The timer pauses during loading times and FMVs.
:::
-
-
-
-
+
+
+
+
loader
diff --git a/changelog.md b/changelog.md
index e92b7b8..f9cdced 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,9 @@
# Changelog
## Apr 08, 2022
+### Implemented custom config of QFT
+- Appearance: location, font size, font color, background color (GMSJ01 only)
+- Freezing the timer
+
### Implemented customizable code
- Add code info in `Codes.xml` as other code,
but you should specify `` and available versions with ``
diff --git a/site/.vuepress/components/codes/codegen.js b/site/.vuepress/components/codes/codegen.js
index 54c05c6..ca5c83e 100644
--- a/site/.vuepress/components/codes/codegen.js
+++ b/site/.vuepress/components/codes/codegen.js
@@ -1,7 +1,9 @@
import InstantRestart from './InstantRestart/codegen.js';
+import qft from './qft/codegen.js';
export default {
InstantRestart,
+ qft,
};
/**
diff --git a/site/.vuepress/components/codes/qft/code/GMSE01.js b/site/.vuepress/components/codes/qft/code/GMSE01.js
new file mode 100644
index 0000000..3276a98
--- /dev/null
+++ b/site/.vuepress/components/codes/qft/code/GMSE01.js
@@ -0,0 +1,101 @@
+export const freezeCodegen = {
+ redCoin: (s) => `
+C21BE474 00000004
+80AD9FB8 8005005C
+3CA0817F 900500B8
+3800${s} 900500BC
+38A00000 00000000
+`,
+ blueCoin: (s) => `
+C21BE288 00000005
+7C030378 80A3005C
+38A50003 54A0003A
+3CA0817F 900500B8
+3800${s} 900500BC
+60000000 00000000
+`,
+};
+
+export const baseCode = `
+C22998C0 0000000B
+981A0260 3CE0817F
+880700B3 2C000000
+38000000 900700BC
+4182000C B00700B2
+900700B4 60E30110
+38800000 60E50094
+80CD9FC8 80C60048
+60E700A4 39000002
+39200000 3D80802D
+618C0848 7D8803A6
+4E800021 00000000
+C2143F18 0000001B
+3C60817F 888300B2
+810300B4 2C040000
+40A20030 808300BC
+2C040000 40A20010
+810D9FB8 8108005C
+48000010 3884FFFF
+908300BC 810300B8
+800300B4 7D080214
+3CE0000A 60E7F9B0
+7C074000 40A00010
+7CE83B78 98E300B2
+90E300B4 1D0803E9
+38000078 7D080396
+380003E8 7CE80396
+7C0701D6 7D004050
+3800003C 7CC70396
+7C0601D6 7CE03850
+38A300A4 3880000F
+80630210 4CC63182
+3D808033 618C97A4
+7D8803A6 4E800021
+3C60817F 60630110
+38800000 38A00000
+38C10BD0 38E00081
+3D80802C 618CFDA8
+7D8803A6 4E800021
+881F0046 00000000
+C229C520 00000005
+3CA0817F A00500B2
+2C000000 40820014
+800500B4 80C3005C
+7C003214 900500B4
+7C0802A6 00000000
+C229A5AC 00000005
+3CA0817F 80C500B4
+8003005C 7CC60214
+38C60004 54C6003A
+90C500B4 38C0FFFF
+B0C500B2 00000000
+C21FA380 00000005
+3D00817F 80C800B4
+8003005C 7CC60214
+38C60004 54C6003A
+90C800B4 38C0FFFF
+B0C800B2 00000000
+C2164E24 00000002
+2C030001 3C60817F
+98A300B3 00000000
+C229880C 00000002
+389C0001 3CA0817F
+988500B3 00000000
+C22991A8 00000005
+3CA0817F 38600001
+986500B3 807F005C
+38630003 5463003A
+906500B8 3860FFFF
+906500BC 00000000
+C229A36C 00000005
+3CA0817F 980500B3
+801E005C 30000004
+5400003A 900500B8
+3800FFFF 900500BC
+60000000 00000000
+077F0094 0000001D
+00000010 000001A0
+00000280 000001E0
+25753A25 3032752E
+25303375 00000000
+`;
diff --git a/site/.vuepress/components/codes/qft/code/GMSJ01.js b/site/.vuepress/components/codes/qft/code/GMSJ01.js
new file mode 100644
index 0000000..3447ee5
--- /dev/null
+++ b/site/.vuepress/components/codes/qft/code/GMSJ01.js
@@ -0,0 +1,136 @@
+export const freezeCodegen = {
+ yellowCoin: (s) => `
+C2196B54 00000004
+806d97e8 8083005c
+3c60817f 908300b8
+3880${s} 908300bc
+8805000E 00000000
+`,
+ redCoin: (s) => `
+C2196314 00000004
+80AD97E8 8005005C
+3CA0817F 900500B8
+3800${s} 900500BC
+38A00000 00000000
+`,
+ blueCoin: (s) => `
+C2196128 00000005
+7C030378 80A3005C
+38A50003 54A0003A
+3CA0817F 900500B8
+3800${s} 900500BC
+60000000 00000000
+`,
+ item: (s) => `
+C21971F8 00000004
+806d97e8 8083005c
+3c60817f 908300b8
+3880${s} 908300bc
+8001001C 00000000
+`,
+ talk: (s) => `
+C20EB6E4 00000004
+807f005c 3c80817f
+906400b8 3860${s}
+906400bc 60000000
+807f00b0 00000000
+`,
+ demo: (s) => `
+C20EB74C 00000004
+807f005c 3c80817f
+906400b8 3860${s}
+906400bc 60000000
+806DA8B0 00000000
+`,
+ cleaned: (s) => `
+C217A3C0 00000004
+806d97e8 8083005c
+3c60817f 908300b8
+3880${s} 908300bc
+80010044 00000000
+`,
+ bowser: (s) => `
+C21D3C78 00000004
+806d97e8 8083005c
+3c60817f 908300b8
+3880${s} 908300bc
+2c1d0003 00000000
+`,
+};
+
+export const baseCode = `
+C20ECE44 00000005
+981A0260 3CE0817F
+880700B3 2C000000
+38000000 900700BC
+4182000C B00700B2
+900700B4 00000000
+C2206738 0000001F
+3C60817F 6064011C
+38630094 3D808020
+398C1EA8 7D8803A6
+4E800021 3C60817F
+888300B2 810300B4
+2C040000 40820030
+808300BC 2C040000
+40820010 810D97E8
+8108005C 48000010
+3884FFFF 908300BC
+810300B8 800300B4
+7D080214 3CE0000A
+60E7F9B0 7C074000
+40800010 7CE83B78
+98E300B2 90E300B4
+3C60817F 1D0803E9
+38000078 7D080396
+9421FFF0 380003E8
+7CE80396 7C0701D6
+7D004050 91010008
+3800003C 7D270396
+7C0901D6 7D403850
+80E30118 390300A4
+80C30114 3D80817F
+80A30110 618C0238
+808300A0 7D8803A6
+80630094 4E800021
+38210010 38610E90
+3D808003 398C5228
+7D8803A6 4E800021
+881F0046 00000000
+C20EFA30 00000005
+3CA0817F A00500B2
+2C000000 40820014
+800500B4 80C3005C
+7C003214 900500B4
+7C0802A6 00000000
+C20EDB30 00000005
+3CA0817F 80C500B4
+8003005C 7CC60214
+38C60004 54C6003A
+90C500B4 38C0FFFF
+B0C500B2 00000000
+C21D1F38 00000005
+3D00817F 80C800B4
+8003005C 7CC60214
+38C60004 54C6003A
+90C800B4 38C0FFFF
+B0C800B2 00000000
+C22257CC 00000002
+2C030001 3C60817F
+98A300B3 00000000
+C20EBD78 00000002
+389C0001 3CA0817F
+988500B3 00000000
+C20EC72C 00000005
+3CA0817F 38600001
+986500B3 807F005C
+38630003 5463003A
+906500B8 3860FFFF
+906500BC 00000000
+C20ED8F0 00000005
+3CA0817F 980500B3
+801E005C 30000004
+5400003A 900500B8
+3800FFFF 900500BC
+60000000 00000000
+`;
diff --git a/site/.vuepress/components/codes/qft/code/GMSJ0A.js b/site/.vuepress/components/codes/qft/code/GMSJ0A.js
new file mode 100644
index 0000000..f1a6e43
--- /dev/null
+++ b/site/.vuepress/components/codes/qft/code/GMSJ0A.js
@@ -0,0 +1,101 @@
+export const freezeCodegen = {
+ redCoin: (s) => `
+C219E1FC 00000004
+80AD9E78 8005005C
+3CA0817F 900500B8
+3800${s} 900500BC
+38A00000 00000000
+`,
+ blueCoin: (s) => `
+C219E010 00000005
+7C030378 80A3005C
+38A50003 54A0003A
+3CA0817F 900500B8
+3800${s} 900500BC
+60000000 00000000
+`,
+};
+
+export const baseCode = `
+C2279570 0000000B
+981A0260 3CE0817F
+880700B3 2C000000
+38000000 900700BC
+4182000C B00700B2
+900700B4 60E30110
+38800000 60E50094
+80CD9E88 80C60048
+60E700A4 39000002
+39200000 3D80802B
+618C0058 7D8803A6
+4E800021 00000000
+C21252A4 0000001B
+3C60817F 888300B2
+810300B4 2C040000
+40A20030 808300BC
+2C040000 40A20010
+810D9E78 8108005C
+48000010 3884FFFF
+908300BC 810300B8
+800300B4 7D080214
+3CE0000A 60E7F9B0
+7C074000 40A00010
+7CE83B78 98E300B2
+90E300B4 1D0803E9
+38000078 7D080396
+380003E8 7CE80396
+7C0701D6 7D004050
+3800003C 7CC70396
+7C0601D6 7CE03850
+38A300A4 3880000F
+80630210 4CC63182
+3D808031 618C90A4
+7D8803A6 4E800021
+3C60817F 60630110
+38800000 38A00000
+38C10BEC 38E00081
+3D80802A 618CF5B8
+7D8803A6 4E800021
+881F0046 00000000
+C227C214 00000005
+3CA0817F A00500B2
+2C000000 40820014
+800500B4 80C3005C
+7C003214 900500B4
+7C0802A6 00000000
+C227A298 00000005
+3CA0817F 80C500B4
+8003005C 7CC60214
+38C60004 54C6003A
+90C500B4 38C0FFFF
+B0C500B2 00000000
+C21DA0FC 00000005
+3D00817F 80C800B4
+8003005C 7CC60214
+38C60004 54C6003A
+90C800B4 38C0FFFF
+B0C800B2 00000000
+C2145EBC 00000002
+2C030001 3C60817F
+98A300B3 00000000
+C22784B4 00000002
+389C0001 3CA0817F
+988500B3 00000000
+C2278E58 00000005
+3CA0817F 38600001
+986500B3 807F005C
+38630003 5463003A
+906500B8 3860FFFF
+906500BC 00000000
+C227A01C 00000005
+3CA0817F 980500B3
+801E005C 30000004
+5400003A 900500B8
+3800FFFF 900500BC
+60000000 00000000
+077F0094 0000001D
+00000010 000001A0
+00000280 000001E0
+25753A25 3032752E
+25303375 00000000
+`;
diff --git a/site/.vuepress/components/codes/qft/code/GMSP01.js b/site/.vuepress/components/codes/qft/code/GMSP01.js
new file mode 100644
index 0000000..381dd49
--- /dev/null
+++ b/site/.vuepress/components/codes/qft/code/GMSP01.js
@@ -0,0 +1,101 @@
+export const freezeCodegen = {
+ redCoin: (s) => `
+C21B632C 00000004
+80AD9EE0 8005005C
+3CA0817F 900500B8
+3800${s} 900500BC
+38A00000 00000000
+`,
+ blueCoin: (s) => `
+C21B6140 00000005
+7C030378 80A3005C
+38A50003 54A0003A
+3CA0817F 900500B8
+3800${s} 900500BC
+60000000 00000000
+`,
+};
+
+export const baseCode = `
+C2291758 0000000B
+981A0260 3CE0817F
+880700B3 2C000000
+38000000 900700BC
+4182000C B00700B2
+900700B4 60E30110
+38800000 60E50094
+80CD9EF0 80C60048
+60E700A4 39000002
+39200000 3D80802C
+618C89F0 7D8803A6
+4E800021 00000000
+C2138B54 0000001B
+3C60817F 888300B2
+810300B4 2C040000
+40A20030 808300BC
+2C040000 40A20010
+810D9EE0 8108005C
+48000010 3884FFFF
+908300BC 810300B8
+800300B4 7D080214
+3CE0000A 60E7F9B0
+7C074000 40A00010
+7CE83B78 98E300B2
+90E300B4 1D0803E9
+38000078 7D080396
+380003E8 7CE80396
+7C0701D6 7D004050
+3800003C 7CC70396
+7C0601D6 7CE03850
+38A300A4 3880000F
+80630210 4CC63182
+3D808033 618C1924
+7D8803A6 4E800021
+3C60817F 60630110
+38800000 38A00000
+38C10BE4 38E00081
+3D80802C 618C7F50
+7D8803A6 4E800021
+881F0046 00000000
+C22943FC 00000005
+3CA0817F A00500B2
+2C000000 40820014
+800500B4 80C3005C
+7C003214 900500B4
+7C0802A6 00000000
+C2292480 00000005
+3CA0817F 80C500B4
+8003005C 7CC60214
+38C60004 54C6003A
+90C500B4 38C0FFFF
+B0C500B2 00000000
+C21F2258 00000005
+3D00817F 80C800B4
+8003005C 7CC60214
+38C60004 54C6003A
+90C800B4 38C0FFFF
+B0C800B2 00000000
+C2159E9C 00000002
+2C030001 3C60817F
+98A300B3 00000000
+C22906A4 00000002
+389C0001 3CA0817F
+988500B3 00000000
+C2291040 00000005
+3CA0817F 38600001
+986500B3 807F005C
+38630003 5463003A
+906500B8 3860FFFF
+906500BC 00000000
+C2292204 00000005
+3CA0817F 980500B3
+801E005C 30000004
+5400003A 900500B8
+3800FFFF 900500BC
+60000000 00000000
+077F0094 0000001D
+00000010 000001A0
+00000280 000001E0
+25753A25 3032752E
+25303375 00000000
+`;
diff --git a/site/.vuepress/components/codes/qft/codegen.js b/site/.vuepress/components/codes/qft/codegen.js
new file mode 100644
index 0000000..ceae46e
--- /dev/null
+++ b/site/.vuepress/components/codes/qft/codegen.js
@@ -0,0 +1,85 @@
+import { parseJSON } from '../codegen.js';
+export const lskey = 'config/qft';
+
+export const defaultConfig = {
+ x: 16,
+ y: 456,
+ width: 112,
+ fontSize: 20,
+ fgRGB: 0xffffff,
+ fgA: 0xff,
+ fgRGB2: null,
+ fgA2: null,
+ bgRGB: 0x000000,
+ bgA: 0x80,
+ freeze: {
+ yellowCoin: 0,
+ redCoin: 30,
+ blueCoin: 30,
+ item: 30,
+ talk: 30,
+ demo: 30,
+ cleaned: 30,
+ bowser: 30, // onBathhubGripDestroyed
+ },
+};
+
+export function getConfig() {
+ const config = parseJSON(localStorage.getItem(lskey)) ?? {};
+ return {
+ ...defaultConfig,
+ ...config,
+ freeze: {
+ ...defaultConfig.freeze,
+ ...config.freeze,
+ },
+ };
+}
+
+const int16 = (x) => (x < 0 ? x + 0x10000 : x).toString(16).padStart(4, '0').slice(-4);
+const int32 = (x) => (x < 0 ? x + 0x100000000 : x).toString(16).padStart(8, '0').slice(-8);
+
+import * as GMSJ01 from './code/GMSJ01.js';
+import * as GMSE01 from './code/GMSE01.js';
+import * as GMSP01 from './code/GMSP01.js';
+import * as GMSJ0A from './code/GMSJ0A.js';
+export const codes = { GMSJ01, GMSE01, GMSP01, GMSJ0A };
+
+export default function codegen(version) {
+ const config = getConfig();
+ const { freezeCodegen, baseCode } = codes[version] ?? {};
+ if (baseCode == null) return '';
+
+ let code = baseCode;
+
+ // freeze code
+ Object.entries(config.freeze).forEach(
+ ([key, frame]) => (code += frame > 0 ? freezeCodegen[key]?.(int16(frame)) ?? '' : ''),
+ );
+
+ // ui (GMSJ01 only)
+ if (['GMSJ01'].includes(version)) {
+ /* bounds */
+ const { x, y, fontSize, width } = config;
+ const scale = fontSize / 20;
+ code += '077F0094 0000001D';
+ code += [
+ x, // x1
+ y - fontSize - 2, // y1
+ x + width * scale, // x2
+ y, // y2
+ ]
+ .map(int32)
+ .join('');
+ code += '25753a253032752e2530337500000000'; // fmt
+ /* fontSize, fgColor, bgColor */
+ code += '077F0110 00000010';
+ const bgColor = (config.bgRGB & 0xffffff) * 256 + (config.bgA & 0xff);
+ const fgColor = (config.fgRGB & 0xffffff) * 256 + (config.fgA & 0xff);
+ const fgColor2 =
+ ((config.fgRGB2 ?? config.fgRGB) & 0xffffff) * 256 + ((config.fgA2 ?? config.fgA) & 0xff);
+ code += [fontSize, fgColor, fgColor2, bgColor].map(int32).join('');
+ }
+
+ return code.replace(/\s/g, '');
+}
diff --git a/site/.vuepress/components/codes/qft/config.vue b/site/.vuepress/components/codes/qft/config.vue
new file mode 100644
index 0000000..5e58a3e
--- /dev/null
+++ b/site/.vuepress/components/codes/qft/config.vue
@@ -0,0 +1,154 @@
+
+
+
+ {{l.h3}}
+
+ {{l.location}}(, )
+
+
+ {{l.fontSize}}
+
+
+ {{fgRGB2==null ? l.fgColor : l.fgColor1}}
+ {{l.alpha}}/255={{(fgA/2.55).toFixed(1)}}%
+ {{l.fgColorGrad}}
+
+
+ {{l.fgColor2}}
+ {{l.alpha}}/255={{(fgA2/2.55).toFixed(1)}}%
+
+
+ {{l.bgColor}}
+ {{l.alpha}}/255={{(bgA/2.55).toFixed(1)}}%
+
+ {{l.preview}}
+
+ {{l.previewNote}}
+
+
+ {{l.freeze.h3}}
+
+
+ {{s}}
+
+
+
+ {{l.freeze.rows[key]}}
+
+ {{(freeze[key]*1001/30000).toFixed(2)}}
+
+
+
+
+
+
+
+
+
+
diff --git a/site/.vuepress/components/codes/qft/labels.json b/site/.vuepress/components/codes/qft/labels.json
new file mode 100644
index 0000000..8751cae
--- /dev/null
+++ b/site/.vuepress/components/codes/qft/labels.json
@@ -0,0 +1,56 @@
+{
+ "ja-JP": {
+ "h3": "見た目",
+ "location": "位置:",
+ "fontSize": "文字サイズ:",
+ "fgColor": "文字色:",
+ "fgColorGrad": "グラデーション",
+ "fgColor1": "文字色(上):",
+ "fgColor2": "文字色(下):",
+ "bgColor": "背景色:",
+ "alpha": "不透明度=",
+ "preview": "プレビュー",
+ "previewNote": "※ x座標の範囲は0~600、y座標の範囲は16~464です\n※ ゲーム内のフォントはプレビューのより幅が広いです",
+ "freeze": {
+ "h3": "一時停止",
+ "th": ["タイミング", "停止フレーム数", "停止秒数"],
+ "rows": {
+ "yellowCoin": "黄コインを取った時",
+ "redCoin": "赤コインを取った時",
+ "blueCoin": "青コインを取った時",
+ "item": "アイテム(ノズル等)を取った時",
+ "talk": "会話開始時",
+ "demo": "カットシーン開始時",
+ "cleaned": "NPCを洗った時",
+ "bowser": "クッパ戦の足場を破壊した時"
+ }
+ }
+ },
+ "en-US": {
+ "h3": "Appearance",
+ "location": "Location: ",
+ "fontSize": "Font size: ",
+ "fgColor": "Font color: ",
+ "fgColorGrad": "Gradient",
+ "fgColor1": "Font color(Top): ",
+ "fgColor2": "Font color(Bottom): ",
+ "bgColor": "Background color: ",
+ "alpha": "Alpha=",
+ "preview": "Preview",
+ "previewNote": "※ x ranges from 0 to 600, and y ranges from 16 to 464.\n※ The font is wider in the game compared to the preview.",
+ "freeze": {
+ "h3": "Freezing the timer",
+ "th": ["Timing", "Duration(frame)", "Duration(sec)"],
+ "rows": {
+ "yellowCoin": "When collected a yellow coin",
+ "redCoin": "When collected a red coin",
+ "blueCoin": "When collected a blue coin",
+ "item": "When collected an item (e.g. nozzle)",
+ "talk": "When dialogue started",
+ "demo": "When cutscene started",
+ "cleaned": "When NPC is cleaned",
+ "bowser": "When destroyed platform in Bowser fight"
+ }
+ }
+ }
+}
diff --git a/site/.vuepress/components/codes/ui.js b/site/.vuepress/components/codes/ui.js
index 5c0c8a3..6594308 100644
--- a/site/.vuepress/components/codes/ui.js
+++ b/site/.vuepress/components/codes/ui.js
@@ -1,5 +1,7 @@
import InstantRestart from './InstantRestart/config.vue';
+import qft from './qft/config.vue';
export default {
InstantRestart,
+ qft,
};
diff --git a/site/.vuepress/public/img/qft/preview-base.jpg b/site/.vuepress/public/img/qft/preview-base.jpg
new file mode 100644
index 0000000..f33ed53
Binary files /dev/null and b/site/.vuepress/public/img/qft/preview-base.jpg differ