add QFT freeze options
- When Mario holds, throws, puts down an object - When Mario triple jumps, spin jumps, ledge grabs, wall kicks, bounces, rope jumps
This commit is contained in:
parent
0888e03db5
commit
9b767a4cbb
9 changed files with 145 additions and 15 deletions
|
@ -1,4 +1,10 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
## Jan 10, 2023
|
||||||
|
### Updated 'Quarterframe Timer'
|
||||||
|
Added the following options to freeze QFT:
|
||||||
|
- When Mario holds, throws, puts down an object
|
||||||
|
- When Mario triple jumps, spin jumps, ledge grabs, wall kicks, bounces, rope jumps
|
||||||
|
|
||||||
## Jan 7, 2023
|
## Jan 7, 2023
|
||||||
### Updated 'Quarterframe Timer'
|
### Updated 'Quarterframe Timer'
|
||||||
Reworked the existing freezes and added the option to freeze when mounting Yoshi
|
Reworked the existing freezes and added the option to freeze when mounting Yoshi
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
export const r13off = -0x6048;
|
export const r13off = -0x6048;
|
||||||
|
export const onChangeStatusAddr = 0x802541c8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {{[key: string]: number}}
|
* @type {{[key: string]: number|{addr: number, orig: number}}}
|
||||||
*/
|
*/
|
||||||
export const freezeCodeHooks = {
|
export const freezeCodeHooks = {
|
||||||
yellowCoin: 0x801bee10,
|
yellowCoin: 0x801bee10,
|
||||||
|
@ -13,4 +14,6 @@ export const freezeCodeHooks = {
|
||||||
cleaned: 0x80215c6c,
|
cleaned: 0x80215c6c,
|
||||||
bowser: 0x801fb7ac,
|
bowser: 0x801fb7ac,
|
||||||
yoshi: 0x802704d4,
|
yoshi: 0x802704d4,
|
||||||
|
take: { addr: 0x8023f9a8, orig: 0x801f0384 },
|
||||||
|
drop: { addr: 0x802437d4, orig: 0x38000000 },
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
export const r13off = -0x6818;
|
export const r13off = -0x6818;
|
||||||
|
export const onChangeStatusAddr = 0x801335b8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {{[key: string]: number}}
|
* @type {{[key: string]: number|{addr: number, orig: number}}}
|
||||||
*/
|
*/
|
||||||
export const freezeCodeHooks = {
|
export const freezeCodeHooks = {
|
||||||
yellowCoin: 0x80196cb0,
|
yellowCoin: 0x80196cb0,
|
||||||
|
@ -13,4 +14,6 @@ export const freezeCodeHooks = {
|
||||||
cleaned: 0x8017a3d4,
|
cleaned: 0x8017a3d4,
|
||||||
bowser: 0x801d3380,
|
bowser: 0x801d3380,
|
||||||
yoshi: 0x8014f830,
|
yoshi: 0x8014f830,
|
||||||
|
take: { addr: 0x8011eae4, orig: 0x801f0384 },
|
||||||
|
drop: { addr: 0x80122964, orig: 0x38000000 },
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
export const r13off = -0x6188;
|
export const r13off = -0x6188;
|
||||||
|
export const onChangeStatusAddr = 0x80233f18;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {{[key: string]: number}}
|
* @type {{[key: string]: number|{addr: number, orig: number}}}
|
||||||
*/
|
*/
|
||||||
export const freezeCodeHooks = {
|
export const freezeCodeHooks = {
|
||||||
yellowCoin: 0x8019eb98,
|
yellowCoin: 0x8019eb98,
|
||||||
|
@ -13,4 +14,6 @@ export const freezeCodeHooks = {
|
||||||
cleaned: 0x801f5b0c,
|
cleaned: 0x801f5b0c,
|
||||||
bowser: 0x801db550,
|
bowser: 0x801db550,
|
||||||
yoshi: 0x80250224,
|
yoshi: 0x80250224,
|
||||||
|
take: { addr: 0x8021f6f0, orig: 0x801f0384 },
|
||||||
|
drop: { addr: 0x8022351c, orig: 0x38000000 },
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
export const r13off = -0x6120;
|
export const r13off = -0x6120;
|
||||||
|
export const onChangeStatusAddr = 0x8024bf54;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {{[key: string]: number}}}
|
* @type {{[key: string]: number|{addr: number, orig: number}}}
|
||||||
*/
|
*/
|
||||||
export const freezeCodeHooks = {
|
export const freezeCodeHooks = {
|
||||||
yellowCoin: 0x801b6cc8,
|
yellowCoin: 0x801b6cc8,
|
||||||
|
@ -13,4 +14,6 @@ export const freezeCodeHooks = {
|
||||||
cleaned: 0x8020db50,
|
cleaned: 0x8020db50,
|
||||||
bowser: 0x801f3690,
|
bowser: 0x801f3690,
|
||||||
yoshi: 0x80268260,
|
yoshi: 0x80268260,
|
||||||
|
take: { addr: 0x80237734, orig: 0x801f0384 },
|
||||||
|
drop: { addr: 0x8023b560, orig: 0x38000000 },
|
||||||
};
|
};
|
||||||
|
|
9
site/.vuepress/components/codes/qft/code/status.js
Normal file
9
site/.vuepress/components/codes/qft/code/status.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export default {
|
||||||
|
put: [0x80000387],
|
||||||
|
tripleJump: [0x882],
|
||||||
|
spinJump: [0x895, 0x896],
|
||||||
|
ledgeGrab: [0x3800034b],
|
||||||
|
wallKick: [0x2000886],
|
||||||
|
bounce: [0x884],
|
||||||
|
ropeJump: [0x892, 0x893],
|
||||||
|
};
|
|
@ -23,6 +23,15 @@ export const defaultConfig = {
|
||||||
cleaned: true,
|
cleaned: true,
|
||||||
bowser: true, // onBathhubGripDestroyed
|
bowser: true, // onBathhubGripDestroyed
|
||||||
yoshi: true,
|
yoshi: true,
|
||||||
|
take: true,
|
||||||
|
drop: true,
|
||||||
|
put: true,
|
||||||
|
tripleJump: true,
|
||||||
|
spinJump: true,
|
||||||
|
ledgeGrab: true,
|
||||||
|
wallKick: true,
|
||||||
|
ropeJump: true,
|
||||||
|
bounce: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,6 +57,9 @@ import * as GMSP01 from './code/GMSP01.js';
|
||||||
import * as GMSJ0A from './code/GMSJ0A.js';
|
import * as GMSJ0A from './code/GMSJ0A.js';
|
||||||
export const codes = { GMSJ01, GMSE01, GMSP01, GMSJ0A };
|
export const codes = { GMSJ01, GMSE01, GMSP01, GMSJ0A };
|
||||||
|
|
||||||
|
import statusDB from './code/status.js';
|
||||||
|
export const statusKeys = Object.keys(statusDB);
|
||||||
|
|
||||||
/****
|
/****
|
||||||
## save freeze frame, load and save QF
|
## save freeze frame, load and save QF
|
||||||
## this function destroys r11 and r12
|
## this function destroys r11 and r12
|
||||||
|
@ -71,18 +83,24 @@ export default function codegen(version, baseCode) {
|
||||||
if (!baseCode) return '';
|
if (!baseCode) return '';
|
||||||
|
|
||||||
const config = getConfig();
|
const config = getConfig();
|
||||||
const { freezeCodeHooks, r13off } = codes[version] ?? {};
|
const { freezeCodeHooks, r13off, onChangeStatusAddr } = codes[version] ?? {};
|
||||||
|
|
||||||
let code = baseCode;
|
let code = baseCode;
|
||||||
const { freezeDuration: frame } = config;
|
const { freezeDuration: frame } = config;
|
||||||
|
|
||||||
// freezing code
|
// freezing code
|
||||||
const enabledFreezes = [];
|
const enabledFreezes = [];
|
||||||
|
const statuses = [];
|
||||||
if (frame > 0) {
|
if (frame > 0) {
|
||||||
for (const [key, enabled] of Object.entries(config.freeze)) {
|
for (const [key, enabled] of Object.entries(config.freeze)) {
|
||||||
const addr = freezeCodeHooks[key];
|
if (!enabled) continue;
|
||||||
if (enabled && addr) {
|
// add status
|
||||||
|
statuses.push(...(statusDB[key] ?? []));
|
||||||
|
// add hook
|
||||||
|
const hook = freezeCodeHooks[key];
|
||||||
|
if (hook) {
|
||||||
if (key === 'blueCoin') {
|
if (key === 'blueCoin') {
|
||||||
|
const addr = hook;
|
||||||
// special: needs to adjust QF -> use separate C2 instead
|
// special: needs to adjust QF -> use separate C2 instead
|
||||||
code += [
|
code += [
|
||||||
0xc2000000 + (addr & 0x1ffffff),
|
0xc2000000 + (addr & 0x1ffffff),
|
||||||
|
@ -100,15 +118,32 @@ export default function codegen(version, baseCode) {
|
||||||
]
|
]
|
||||||
.map(int2gecko)
|
.map(int2gecko)
|
||||||
.join('');
|
.join('');
|
||||||
} else {
|
} else if (typeof hook === 'number') {
|
||||||
// handle regular freezing code later
|
// handle regular freezing code later
|
||||||
|
const addr = hook;
|
||||||
enabledFreezes.push(addr);
|
enabledFreezes.push(addr);
|
||||||
|
} else {
|
||||||
|
// {addr: number, orig: number}
|
||||||
|
// separate C2 code to handle orig
|
||||||
|
const { addr, orig } = hook;
|
||||||
|
code += [
|
||||||
|
0xc2000000 + (addr & 0x1ffffff),
|
||||||
|
0x00000003,
|
||||||
|
0x3d800000 + (freezeCodeAddr >>> 16), // lis r12, freezeCodeAddr@h
|
||||||
|
0x618c0000 + (freezeCodeAddr & 0xffff), // ori r12, r12, freezeCodeAddr@l
|
||||||
|
0x7d8803a6, // mtlr r12
|
||||||
|
0x4e800021, // blrl
|
||||||
|
orig,
|
||||||
|
0x00000000,
|
||||||
|
]
|
||||||
|
.map(int2gecko)
|
||||||
|
.join('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handle regular freezing code
|
// handle regular freezing code
|
||||||
if (enabledFreezes.length <= 1) {
|
if (enabledFreezes.length <= 1 && statuses.length === 0) {
|
||||||
// use C2 directly
|
// use C2 directly
|
||||||
code += enabledFreezes
|
code += enabledFreezes
|
||||||
.flatMap((addr) => [
|
.flatMap((addr) => [
|
||||||
|
@ -149,6 +184,54 @@ export default function codegen(version, baseCode) {
|
||||||
code += [...hooks, ...freezer].map(int2gecko).join('');
|
code += [...hooks, ...freezer].map(int2gecko).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// onChangeStatus hook
|
||||||
|
if (statuses.length) {
|
||||||
|
const c = [
|
||||||
|
// check each status
|
||||||
|
...statuses.flatMap((x, i) => {
|
||||||
|
const cr = i > 0 ? 0x800000 : 0; // i>0 ? cr1 : cr0
|
||||||
|
const c =
|
||||||
|
x < 0x10000
|
||||||
|
? [
|
||||||
|
0x281d0000 + cr + x, // cmplwi crX, r29, $x
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
0x3c000000 + (x >>> 16), // lis r0, $x@h
|
||||||
|
0x60000000 + (x & 0xffff), // ori r0, r0, $x@l
|
||||||
|
0x7c1d0040 + cr, // cmplw crX, r29, r0
|
||||||
|
];
|
||||||
|
if (i > 0) {
|
||||||
|
// cror 4*cr0+eq, 4*cr0+eq, 4*cr1+eq
|
||||||
|
c.push(0x4c423382);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}),
|
||||||
|
// freeze
|
||||||
|
0x3d800000 + (freezeCodeAddr >>> 16), // lis r12, freezeCodeAddr@h
|
||||||
|
0x618c0000 + (freezeCodeAddr & 0xffff), // ori r12, r12, freezeCodeAddr@l
|
||||||
|
0x7d8803a6, // mtlr r12
|
||||||
|
0x4d820021, // beqlrl
|
||||||
|
// orig
|
||||||
|
0x38000000, // li r0, 0
|
||||||
|
];
|
||||||
|
|
||||||
|
// pad nop
|
||||||
|
if (c.length % 2 === 0) {
|
||||||
|
c.push(0x60000000);
|
||||||
|
}
|
||||||
|
// end of C2
|
||||||
|
c.push(0x00000000);
|
||||||
|
|
||||||
|
// apply code
|
||||||
|
code += [
|
||||||
|
0xc2000000 + (onChangeStatusAddr & 0x1ffffff),
|
||||||
|
c.length >> 1, // line count
|
||||||
|
...c,
|
||||||
|
]
|
||||||
|
.map(int2gecko)
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
/* bounds */
|
/* bounds */
|
||||||
const { x, y, fontSize, width } = config;
|
const { x, y, fontSize, width } = config;
|
||||||
|
|
|
@ -35,14 +35,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getConfig, lskey, codes } from './codegen.js';
|
import { getConfig, lskey, codes, statusKeys } from './codegen.js';
|
||||||
import { rgbI2S, rgbS2I, rgbaI2S } from '../utils';
|
import { rgbI2S, rgbS2I, rgbaI2S } from '../utils';
|
||||||
import labels from './labels.json';
|
import labels from './labels.json';
|
||||||
import TextConfig from '../TextConfig.vue';
|
import TextConfig from '../TextConfig.vue';
|
||||||
|
|
||||||
function updateConfig() {
|
function updateConfig() {
|
||||||
const { x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration } =
|
const { x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration } = this;
|
||||||
this;
|
|
||||||
const config = {
|
const config = {
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
|
@ -104,7 +103,10 @@ export default {
|
||||||
freeze,
|
freeze,
|
||||||
freezeDuration,
|
freezeDuration,
|
||||||
// const
|
// const
|
||||||
freezeKeys: Object.keys(codes[this.version]?.freezeCodeHooks ?? {}),
|
freezeKeys: [
|
||||||
|
...Object.keys(codes[this.version]?.freezeCodeHooks ?? {}),
|
||||||
|
...statusKeys,
|
||||||
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
@ -25,7 +25,16 @@
|
||||||
"demo": "カットシーン開始時",
|
"demo": "カットシーン開始時",
|
||||||
"cleaned": "NPCを洗った時",
|
"cleaned": "NPCを洗った時",
|
||||||
"bowser": "クッパ戦の足場を破壊した時",
|
"bowser": "クッパ戦の足場を破壊した時",
|
||||||
"yoshi": "ヨッシーに乗った時"
|
"yoshi": "ヨッシーに乗った時",
|
||||||
|
"take": "オブジェクトを持った時",
|
||||||
|
"drop": "オブジェクトを投げた時",
|
||||||
|
"put": "オブジェクトを置いた時",
|
||||||
|
"tripleJump": "三段ジャンプした時",
|
||||||
|
"spinJump": "スピンジャンプした時",
|
||||||
|
"ledgeGrab": "崖掴まりした時",
|
||||||
|
"wallKick": "壁キックした時",
|
||||||
|
"ropeJump": "ロープ、大車輪でジャンプした時",
|
||||||
|
"bounce": "アメンボやジャンプ台で跳ねた時"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -55,7 +64,16 @@
|
||||||
"demo": "When a cutscene starts",
|
"demo": "When a cutscene starts",
|
||||||
"cleaned": "When an NPC is cleaned",
|
"cleaned": "When an NPC is cleaned",
|
||||||
"bowser": "When a platform is destroyed in the Bowser fight",
|
"bowser": "When a platform is destroyed in the Bowser fight",
|
||||||
"yoshi": "When Yoshi is mounted"
|
"yoshi": "When Yoshi is mounted",
|
||||||
|
"take": "When Mario holds an object",
|
||||||
|
"drop": "When Mario throws an object",
|
||||||
|
"put": "When Mario puts down an object",
|
||||||
|
"tripleJump": "When Mario triple jumps",
|
||||||
|
"spinJump": "When Mario spin jumps",
|
||||||
|
"ledgeGrab": "When Mario ledge grabs",
|
||||||
|
"wallKick": "When Mario wall kicks",
|
||||||
|
"ropeJump": "When Mario jumps from a rope",
|
||||||
|
"bounce": "When Mario bounces (e.g. on a roof)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue