91b878f347
- Added 6 Quaternius weapon models (Pistol, Revolver, Rifle, Shotgun, P90, SniperRifle) - Created weapon resource files with balanced stats - Wired Pistol + Rifle into player.tscn replacing Kenney blasters - Fixed WASD input (matched input map action names) - Fixed Escape key toggles mouse capture - Added Audio autoload singleton - Fixed broken sound asset paths across all scripts - Fixed all scene ext_resource text paths (nested under assets/kenney-fps/) - Fixed all .import source_file paths - Deleted stale .import files (will be regenerated by editor on open)
124 lines
5.1 KiB
JavaScript
124 lines
5.1 KiB
JavaScript
import { readFileSync, writeFileSync } from 'fs';
|
|
/**
|
|
* Matches an empty `[autoload]` section (the header followed by only blank
|
|
* lines, up to the next section header or end-of-file). Used by cleanup paths
|
|
* to drop the section after the last entry is removed.
|
|
*/
|
|
export const EMPTY_AUTOLOAD_SECTION_REGEX = /\[autoload\]\s*(?=\n\[|\n*$)/g;
|
|
/**
|
|
* Mirrors the parser's `\w+` assumption (parseAutoloads / removeAutoloadEntry).
|
|
* Enforced on write paths to prevent a name with newlines or INI section
|
|
* delimiters from corrupting project.godot.
|
|
*/
|
|
export const VALID_AUTOLOAD_NAME_REGEX = /^\w+$/;
|
|
function assertValidName(name) {
|
|
if (!VALID_AUTOLOAD_NAME_REGEX.test(name)) {
|
|
throw new Error(`Invalid autoload name '${name}': must contain only word characters (letters, digits, underscore)`);
|
|
}
|
|
}
|
|
export function normalizeAutoloadPath(p) {
|
|
return p.startsWith('res://') ? p : `res://${p}`;
|
|
}
|
|
export function parseAutoloads(projectFilePath, existingContent) {
|
|
const content = existingContent ?? readFileSync(projectFilePath, 'utf8');
|
|
const autoloads = [];
|
|
let inAutoloadSection = false;
|
|
for (const line of content.split('\n')) {
|
|
const trimmed = line.trim();
|
|
if (trimmed.startsWith('[')) {
|
|
inAutoloadSection = trimmed === '[autoload]';
|
|
continue;
|
|
}
|
|
if (!inAutoloadSection || trimmed === '' || trimmed.startsWith(';') || trimmed.startsWith('#'))
|
|
continue;
|
|
// The surrounding `"?` are intentional: Godot always writes quotes, but
|
|
// hand-edited project.godot files sometimes omit them. Tolerating both
|
|
// shapes means a missing quote pair doesn't silently drop the entry.
|
|
const match = trimmed.match(/^(\w+)="?(\*?)([^"]*?)"?$/);
|
|
if (match) {
|
|
autoloads.push({ name: match[1], singleton: match[2] === '*', path: match[3] });
|
|
}
|
|
}
|
|
return autoloads;
|
|
}
|
|
export function addAutoloadEntry(projectFilePath, name, path, singleton, existingContent) {
|
|
assertValidName(name);
|
|
const content = existingContent ?? readFileSync(projectFilePath, 'utf8');
|
|
const lines = content.split('\n');
|
|
const entry = `${name}="${singleton ? '*' : ''}${normalizeAutoloadPath(path)}"`;
|
|
const sectionIdx = lines.findIndex((l) => l.trim() === '[autoload]');
|
|
if (sectionIdx === -1) {
|
|
writeFileSync(projectFilePath, content.trimEnd() + '\n\n[autoload]\n' + entry + '\n', 'utf8');
|
|
return;
|
|
}
|
|
let insertIdx = sectionIdx + 1;
|
|
while (insertIdx < lines.length && !lines[insertIdx].trim().startsWith('[')) {
|
|
insertIdx++;
|
|
}
|
|
lines.splice(insertIdx, 0, entry);
|
|
writeFileSync(projectFilePath, lines.join('\n'), 'utf8');
|
|
}
|
|
/**
|
|
* Remove the named autoload entry. Also drops the `[autoload]` section header
|
|
* if the removed entry was the last one in it. Returns true when the file was
|
|
* mutated.
|
|
*/
|
|
export function removeAutoloadEntry(projectFilePath, name) {
|
|
const content = readFileSync(projectFilePath, 'utf8');
|
|
const lines = content.split('\n');
|
|
let inAutoloadSection = false;
|
|
let removed = false;
|
|
const filtered = lines.filter((line) => {
|
|
const trimmed = line.trim();
|
|
if (trimmed.startsWith('[')) {
|
|
inAutoloadSection = trimmed === '[autoload]';
|
|
return true;
|
|
}
|
|
if (inAutoloadSection) {
|
|
const match = trimmed.match(/^(\w+)=/);
|
|
if (match && match[1] === name) {
|
|
removed = true;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
if (!removed)
|
|
return false;
|
|
let newContent = filtered.join('\n');
|
|
newContent = newContent.replace(EMPTY_AUTOLOAD_SECTION_REGEX, '');
|
|
newContent = newContent.trimEnd() + '\n';
|
|
writeFileSync(projectFilePath, newContent, 'utf8');
|
|
return true;
|
|
}
|
|
export function updateAutoloadEntry(projectFilePath, name, newPath, singleton) {
|
|
assertValidName(name);
|
|
const content = readFileSync(projectFilePath, 'utf8');
|
|
const lines = content.split('\n');
|
|
let inAutoloadSection = false;
|
|
let updated = false;
|
|
const newLines = lines.map((line) => {
|
|
const trimmed = line.trim();
|
|
if (trimmed.startsWith('[')) {
|
|
inAutoloadSection = trimmed === '[autoload]';
|
|
return line;
|
|
}
|
|
if (inAutoloadSection) {
|
|
// The surrounding `"?` are intentional: Godot always writes quotes, but
|
|
// hand-edited project.godot files sometimes omit them. Tolerating both
|
|
// shapes means a missing quote pair doesn't silently drop the entry.
|
|
const match = trimmed.match(/^(\w+)="?(\*?)([^"]*?)"?$/);
|
|
if (match && match[1] === name) {
|
|
const effectiveSingleton = singleton !== undefined ? singleton : match[2] === '*';
|
|
const effectivePath = newPath !== undefined ? normalizeAutoloadPath(newPath) : match[3];
|
|
updated = true;
|
|
return `${name}="${effectiveSingleton ? '*' : ''}${effectivePath}"`;
|
|
}
|
|
}
|
|
return line;
|
|
});
|
|
if (updated)
|
|
writeFileSync(projectFilePath, newLines.join('\n'), 'utf8');
|
|
return updated;
|
|
}
|
|
//# sourceMappingURL=autoload-ini.js.map
|