Implement customizable code; add Instant Restart
This commit is contained in:
parent
ea10541da0
commit
06f7ad9864
11 changed files with 233 additions and 3 deletions
30
Codes.xml
30
Codes.xml
|
@ -4040,4 +4040,34 @@
|
|||
00000000 59800004
|
||||
</source>
|
||||
</code>
|
||||
<code>
|
||||
<id>InstantRestart</id>
|
||||
<category>qol</category>
|
||||
<presets>recommended</presets>
|
||||
<title lang="en-US">Instant Restart</title>
|
||||
<title lang="ja-JP">ポーズせずにやり直し</title>
|
||||
<author>sup39(サポミク)</author>
|
||||
<version>0.1.3</version>
|
||||
<date>Jan 07, 2022</date>
|
||||
<description lang="en-US">
|
||||
When you pressed the buttons configured in [#Button Config](#config) simultaneously,
|
||||
you can restart the current area without selecting "Exit Area" in pause menu.
|
||||
Note that the restart function behaves differently than pressing Y or Z with "Level Select".
|
||||
This code only supports restarting 1 area only.
|
||||
For example, you can restart outside a secret stage or inside a secret stage individually,
|
||||
but you can NOT restart a combination of outside+inside a secret stage.
|
||||
|
||||
::: warning
|
||||
You can NOT restart after destroying the last platform in Bowser fight at the moment.
|
||||
:::
|
||||
</description>
|
||||
<description lang="ja-JP">
|
||||
[#ボタン設定](#config)で設定したボタンを同時に押すと、ポーズメニューから「コースから出る」を選択せずに所在のエリアをやり直すことができます。ただし、Level SelectのYとZのやり直し機能と異なり、エリアごとのやり直ししかできないので注意してください。例えば、ヒミツ外部のみ、ヒミツ内部のみといった一つのエリアのやり直しはできますが、ヒミツ外部+ヒミツ内部といった組み合わせのやり直しはできません。
|
||||
|
||||
::: warning
|
||||
現時点ではクッパ戦で最後の足場を破壊するとやり直しできません。
|
||||
:::
|
||||
</description>
|
||||
<source version="GMSJ01"></source>
|
||||
</code>
|
||||
</codes>
|
||||
|
|
23
changelog.md
23
changelog.md
|
@ -1,4 +1,27 @@
|
|||
# Changelog
|
||||
## Apr 08, 2022
|
||||
### Implemented customizable code
|
||||
- Add code info in `Codes.xml` as other code,
|
||||
but you should specify `<id>` and available versions with `<source version="XXX"></source>`
|
||||
(the code in `<source>` will be ignored).
|
||||
- Create directory `site/.vuepress/components/codes/YOUR_CODE_NAME`,
|
||||
and create the following two files (file names are arbitrary):
|
||||
+ `codegen.js`: **export default** a `codegen(version: string): string` function,
|
||||
where `version` is the game version (e.g. `GMSJ01`),
|
||||
and the return value is the gecko code string.
|
||||
The config of the code can be read from localStorage
|
||||
(with key = `config/YOUR_CODE_ID` as convention) directly.
|
||||
+ `config.vue`: The vue component of the config UI,
|
||||
which is shown below the `<description>` given in `Codes.xml`.
|
||||
When the config is changed, store the new config into localStorage
|
||||
(with key = `config/YOUR_CODE_ID` as convention).
|
||||
- Register `codegen.js` in `site/.vuepress/components/codes/codegen.js`
|
||||
and `config.vue` in `site/.vuepress/components/codes/ui.js`.
|
||||
Note that the name used in export must match the `<id>` of the code.
|
||||
|
||||
### Added 'Instant Restart' (GMSJ01 only)
|
||||
Restarts the area without pausing.
|
||||
|
||||
## Mar 25, 2022
|
||||
### Implemented dependencies system
|
||||
Add `<dependencies>` tag in `<code>` (e.g. `<dependencies>dep1,dep2,dep3</dependencies>`)
|
||||
|
|
|
@ -107,8 +107,7 @@ const validateXML = (xmlString) => {
|
|||
// Each source has a valid length
|
||||
for (let j = 0; j < codeSources.length; j++) {
|
||||
if (
|
||||
codeSources[j].textContent.replace(/[\s\n\r\t]+/gm, '').length % 16 != 0 ||
|
||||
codeSources[j].textContent.replace(/[\s\n\r\t]+/gm, '').length < 16
|
||||
codeSources[j].textContent.replace(/[\s\n\r\t]+/gm, '').length % 16 != 0
|
||||
)
|
||||
throw new Error(
|
||||
`Invalid source length for code '${codeTitle.textContent}' and version ${
|
||||
|
|
|
@ -11,21 +11,27 @@
|
|||
<span v-else>{{ getLabel('codeinfo.author') }} {{ translatedCode.author }}</span>
|
||||
</div>
|
||||
<p class="description" v-html="translatedCode.description"></p>
|
||||
<component v-if="configUI" :is="configUI" :version="version"></component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { translate, translateCode } from '../i18n/localeHelper';
|
||||
import configUIs from './codes/ui.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
anchor: { type: Boolean },
|
||||
code: { type: Object },
|
||||
version: { type: String },
|
||||
},
|
||||
computed: {
|
||||
translatedCode: function () {
|
||||
return translateCode(this.code, this.$lang);
|
||||
},
|
||||
configUI: function () {
|
||||
return configUIs[this.code.id];
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
|
|
|
@ -15,6 +15,9 @@ import gameVersions from '../data/gameVersions.json';
|
|||
// Util
|
||||
import { translateCode } from '../i18n/localeHelper';
|
||||
|
||||
// customizable code
|
||||
import codegens from './codes/codegen.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
codes: { type: Array },
|
||||
|
@ -59,6 +62,15 @@ export default {
|
|||
|
||||
const fileName = gameVersions.find((v) => v.identifier === this.versionIdentifier).version;
|
||||
|
||||
// apply customizable codes
|
||||
for (const code of c) {
|
||||
const codegen = codegens[code.id];
|
||||
if (codegen) {
|
||||
code.source = codegen(this.versionIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
// generate file
|
||||
switch (this.format) {
|
||||
case 'gct':
|
||||
this.generateGCT(c, fileName);
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
<div v-if="codes && codes.length > 0" class="help">
|
||||
<h3>{{ getLabel('headers.help') }}</h3>
|
||||
<CodeInfo v-if="!!inspectingCode" :code="inspectingCode" />
|
||||
<CodeInfo v-if="!!inspectingCode" :code="inspectingCode" :version="selectedVersion" />
|
||||
<div v-else-if="showStageLoaderHelp">
|
||||
<h3>{{ getLabel('headers.stageloader') }}</h3>
|
||||
<div>
|
||||
|
|
52
site/.vuepress/components/codes/InstantRestart/codegen.js
Normal file
52
site/.vuepress/components/codes/InstantRestart/codegen.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { parseJSON } from '../codegen.js';
|
||||
export const lskey = 'config/InstantRestart';
|
||||
export const buttonValues = {
|
||||
START: 0x1000,
|
||||
Y: 0x0800,
|
||||
X: 0x0400,
|
||||
B: 0x0200,
|
||||
A: 0x0100,
|
||||
L: 0x0040,
|
||||
R: 0x0020,
|
||||
Z: 0x0010,
|
||||
DU: 0x0008,
|
||||
DD: 0x0004,
|
||||
DR: 0x0002,
|
||||
DL: 0x0001,
|
||||
};
|
||||
|
||||
export const defaultConfig = {
|
||||
button: buttonValues.Y | buttonValues.DL,
|
||||
};
|
||||
export function getConfig() {
|
||||
return {
|
||||
...defaultConfig,
|
||||
...(parseJSON(localStorage.getItem(lskey)) ?? {}),
|
||||
};
|
||||
}
|
||||
export default function codegen(version) {
|
||||
const { button } = getConfig();
|
||||
let code;
|
||||
switch (version) {
|
||||
case 'GMSJ01':
|
||||
code = `
|
||||
c20eafa0 00000009
|
||||
3c608040 a0a30d50
|
||||
2805${button.toString(16).padStart(4, '0')} 40a20030
|
||||
3c60817f 38a00001
|
||||
98a300b3 98a30100
|
||||
3c60803e 80a3600e
|
||||
90a36012 3c60800e
|
||||
6063b3f8 7c6803a6
|
||||
4e800020 2c000002
|
||||
60000000 00000000
|
||||
`;
|
||||
if (button & buttonValues.Z) {
|
||||
code += '\n040eb024 60000000';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
return code.replace(/\s/g, '');
|
||||
}
|
68
site/.vuepress/components/codes/InstantRestart/config.vue
Normal file
68
site/.vuepress/components/codes/InstantRestart/config.vue
Normal file
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<div id="config">
|
||||
<h3>{{l('h3Config')}}</h3>
|
||||
<div>
|
||||
<span>{{l('lblButton')}}</span>
|
||||
<div v-for="info in buttonInfos" class="inline" :key="info.value">
|
||||
<input type="checkbox" :checked="button & info.value"
|
||||
@change="toggleButton($event, info.value)"><span>{{info.text}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>{{l('lblDPad')}}</span>
|
||||
<div v-for="info in dpadButtonInfos" class="inline" :key="info.value">
|
||||
<input type="checkbox" :checked="button & info.value"
|
||||
@change="toggleButton($event, info.value)"><span>{{info.text}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="button & buttonValues.Z" class="custom-block danger">
|
||||
<p>{{l('pCautionZ')}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getConfig, lskey, buttonValues} from './codegen.js';
|
||||
import labels from './labels.json';
|
||||
import {getLabels} from '../codegen.js';
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
updateConfig() {
|
||||
localStorage.setItem(lskey, JSON.stringify({button: this.button}));
|
||||
},
|
||||
toggleButton(event, value) {
|
||||
this.button = event.target.checked ?
|
||||
this.button | value : // ON
|
||||
this.button & ~value; // OFF
|
||||
this.updateConfig();
|
||||
},
|
||||
l(id) {
|
||||
return labels[id][this.$lang] ?? labels[id]['en-US'];
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const {button} = getConfig();
|
||||
return {
|
||||
button,
|
||||
// const
|
||||
buttonInfos: [
|
||||
'A', 'B', 'X', 'Y', 'L', 'R', 'Z',
|
||||
].map(text => ({text, value: buttonValues[text]})),
|
||||
dpadButtonInfos: [
|
||||
{text: '←', value: buttonValues.DL},
|
||||
{text: '↓', value: buttonValues.DD},
|
||||
{text: '↑', value: buttonValues.DU},
|
||||
{text: '→', value: buttonValues.DR},
|
||||
],
|
||||
buttonValues,
|
||||
};
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.inline {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
18
site/.vuepress/components/codes/InstantRestart/labels.json
Normal file
18
site/.vuepress/components/codes/InstantRestart/labels.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"h3Config": {
|
||||
"ja-JP": "ボタン設定",
|
||||
"en-US": "Button Config"
|
||||
},
|
||||
"lblDPad": {
|
||||
"ja-JP": "【十字キー】",
|
||||
"en-US": "D-Pad: "
|
||||
},
|
||||
"lblButton": {
|
||||
"ja-JP": "【ボタン】",
|
||||
"en-US": "Button: "
|
||||
},
|
||||
"pCautionZ": {
|
||||
"ja-JP": "Zメニューが無効化されるので注意してください",
|
||||
"en-US": "Note that Z menu will be disabled."
|
||||
}
|
||||
}
|
17
site/.vuepress/components/codes/codegen.js
Normal file
17
site/.vuepress/components/codes/codegen.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import InstantRestart from './InstantRestart/codegen.js';
|
||||
|
||||
export default {
|
||||
InstantRestart,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string|null} s -- raw input string
|
||||
*/
|
||||
export function parseJSON(s) {
|
||||
if (s == null) return null;
|
||||
try {
|
||||
return JSON.parse(s);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
5
site/.vuepress/components/codes/ui.js
Normal file
5
site/.vuepress/components/codes/ui.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import InstantRestart from './InstantRestart/config.vue';
|
||||
|
||||
export default {
|
||||
InstantRestart,
|
||||
};
|
Loading…
Reference in a new issue