Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extensions/games/game-fallout4vr/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "game-fallout4vr",
"version": "1.0.2",
"version": "1.0.3",
"description": "Support for the VR variant of Fallout 4",
"scripts": {
"_assets": "copyfiles -u 1 -f ./assets/* dist",
Expand Down
128 changes: 125 additions & 3 deletions extensions/games/game-fallout4vr/src/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
const Promise = require("bluebird");
const path = require("path");
const { getFileVersion } = require("exe-version");
const { util } = require("@nexusmods/vortex-api");
const { selectors, util } = require("@nexusmods/vortex-api");
const winapi = require("winapi-bindings");

const GAME_ID = "fallout4vr";
const ESL_ENABLER_LIB = "Daytripper4.dll";
const ESL_NOTIF_ID = "fallout4vr-esl-enabler-notif";

/*
Ignore the Meshes\AnimTextData\AnimationOffsets\PersistantSubgraphInfoAndOffsetData.txt file as a conflict.
It's present in a lot of weapon mods but doesn't matter if it's overwritten.
Expand Down Expand Up @@ -34,7 +38,7 @@ function getGameVersion(gamePath, exePath) {
return fileVersion + "-VR";
}

let tools = [
const tools = [
{
id: "FO4VREdit",
name: "FO4VREdit",
Expand All @@ -51,9 +55,82 @@ let tools = [
},
];

function isESLSupported(api) {
const state = api.getState();
const profileId = selectors.lastActiveProfileForGame(state, GAME_ID);
const discovery = selectors.discoveryByGame(state, GAME_ID);
if (discovery?.store === "xbox") {
return false;
}
const modState = util.getSafe(state, ["persistent", "profiles", profileId, "modState"], {});
const isEnabled = (modId) => util.getSafe(modState, [modId, "enabled"], false);
const mods = util.getSafe(state, ["persistent", "mods", GAME_ID], {});
const hasESLEnabler = Object.keys(mods).some(
(modId) => isEnabled(modId) && mods[modId]?.attributes?.eslEnabler === true,
);
if (hasESLEnabler) {
api.dismissNotification(ESL_NOTIF_ID);
}
return hasESLEnabler;
}

function testEslEnabler(files, gameId) {
const isFallout4VR = gameId === GAME_ID;
const isESLEnabler = files.some((file) => file.toLowerCase().endsWith(ESL_ENABLER_LIB));
return Promise.resolve({
supported: isFallout4VR && isESLEnabler,
requiredFiles: [],
});
}

function installEslEnabler(files, destinationPath) {
const filtered = files.filter((file) => path.extname(file) !== "");
const instructions = filtered.map((file) => {
const segments = file.split(path.sep);
segments.splice(0, 1, "Data");
return {
type: "copy",
source: file,
destination: segments.join(path.sep),
};
});

// Remove this once the mod type conflict issue is resolved
instructions.push({ type: "setmodtype", value: "dinput" });
instructions.push({ type: "attribute", key: "eslEnabler", value: true });

return Promise.resolve({ instructions });
}

function prepare(api, discovery) {
if (isESLSupported(api)) {
return Promise.resolve();
}

api.sendNotification({
id: ESL_NOTIF_ID,
type: "info",
title: "ESL Support",
message:
"Fallout 4 VR requires a mod to enable ESL support. Mod must be installed through Vortex for ESL support to work.",
actions: [
{
title: "Download",
action: () =>
util.opn("https://www.nexusmods.com/fallout4/mods/91141?tab=files").catch(() => {}),
},
],
});
}

const sortAndResolve = (api) => {
api.events.emit("autosort-plugins", false);
return Promise.resolve();
};

function main(context) {
context.registerGame({
id: "fallout4vr",
id: GAME_ID,
name: "Fallout 4 VR",
mergeMods: true,
queryPath: findGame,
Expand All @@ -62,18 +139,63 @@ function main(context) {
logo: "gameart.jpg",
executable: () => "Fallout4VR.exe",
getGameVersion,
setup: (discovery) => prepare(context.api, discovery),
requiredFiles: ["Fallout4VR.exe"],
environment: {
SteamAPPId: "611660",
},
details: {
steamAppId: 611660,
compatibleDownloads: ["fallout4"],
supportsESL: () => isESLSupported(context.api),
ignoreConflicts: IGNORED_FILES,
nexusPageId: "fallout4",
},
});

context.registerInstaller("fallout4vr-esl-enabler", 10, testEslEnabler, installEslEnabler);

context.once(() => {
context.api.events.on("gamemode-activated", (gameId) => {
if (gameId !== GAME_ID) {
context.api.dismissNotification(ESL_NOTIF_ID);
}
});
context.api.onAsync("did-deploy", (profileId, newDeployment) => {
const state = context.api.getState();
const profile = selectors.profileById(state, profileId);
if (profile?.gameId !== GAME_ID) {
return Promise.resolve();
}
const discovery = selectors.discoveryByGame(state, GAME_ID);
if (!discovery?.path || discovery?.store === "xbox") {
// Fallout 4 VR is currently not on Xbox, but it may be one day!
return Promise.resolve();
}

const deployedFiles = newDeployment[""];
const modESLEnabler = deployedFiles.find((file) =>
file.relPath.toLowerCase().endsWith(ESL_ENABLER_LIB.toLowerCase()),
);
if (modESLEnabler === undefined) {
return sortAndResolve(context.api);
}

const mods = util.getSafe(state, ["persistent", "mods", GAME_ID], {});
const mod = Object.values(mods).find((mod) => mod.installationPath === modESLEnabler.source);
if (mod === undefined || mod.attributes.eslEnabler === true) {
return sortAndResolve(context.api);
}

const modAttributes = {
...mod.attributes,
eslEnabler: true,
};
context.api.store.dispatch(actions.setModAttributes(GAME_ID, mod.id, modAttributes));
return sortAndResolve(context.api);
});
});

return true;
}

Expand Down
Loading