diff --git a/package.json b/package.json index e0553558..fd4824cb 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@vanilla-extract/recipes": "^0.5.2", "adm-zip": "^0.5.16", "archiver": "^7.0.1", + "archiver-utils": "^5.0.2", "auto-launch": "^5.0.6", "axios": "^1.7.7", "better-sqlite3": "^11.3.0", diff --git a/src/main/services/achievements/check-unlocked-achievements.ts b/src/main/services/achievements/check-unlocked-achievements.ts index 1fa2c3f5..608ff5bd 100644 --- a/src/main/services/achievements/check-unlocked-achievements.ts +++ b/src/main/services/achievements/check-unlocked-achievements.ts @@ -6,7 +6,7 @@ export const checkUnlockedAchievements = ( unlockedAchievements: any ): UnlockedAchievement[] => { if (type === Cracker.onlineFix) return onlineFixMerge(unlockedAchievements); - if (type === Cracker.goldberg) + if (type === Cracker.goldberg || type === Cracker.goldberg2) return goldbergUnlockedAchievements(unlockedAchievements); if (type == Cracker.generic) return genericMerge(unlockedAchievements); return defaultMerge(unlockedAchievements); diff --git a/src/main/services/achievements/find-steam-game-achivement-files.ts b/src/main/services/achievements/find-steam-game-achivement-files.ts index 1f4a0a86..980f0ae8 100644 --- a/src/main/services/achievements/find-steam-game-achivement-files.ts +++ b/src/main/services/achievements/find-steam-game-achivement-files.ts @@ -9,6 +9,15 @@ import { Game } from "@main/entity"; const publicDir = path.join("C:", "Users", "Public", "Documents"); const appData = app.getPath("appData"); +const crackers = [ + Cracker.codex, + Cracker.goldberg, + Cracker.goldberg2, + Cracker.rune, + Cracker.onlineFix, + Cracker.generic, +]; + const addGame = ( achievementFiles: Map, achievementPath: string, @@ -39,14 +48,6 @@ const getObjectIdsInFolder = (path: string) => { }; export const findSteamGameAchievementFiles = (game: Game) => { - const crackers = [ - Cracker.codex, - Cracker.goldberg, - Cracker.rune, - Cracker.onlineFix, - Cracker.generic, - ]; - const achievementFiles: AchievementFile[] = []; for (const cracker of crackers) { let achievementPath: string; @@ -58,9 +59,9 @@ export const findSteamGameAchievementFiles = (game: Game) => { } else if (cracker === Cracker.goldberg) { achievementPath = path.join(appData, "Goldberg SteamEmu Saves"); fileLocation = ["achievements.json"]; - } else if (cracker === Cracker.generic) { - achievementPath = path.join(publicDir, Cracker.generic); - fileLocation = ["user_stats.ini"]; + } else if (cracker === Cracker.goldberg2) { + achievementPath = path.join(appData, "GSE Saves"); + fileLocation = ["achievements.json"]; } else { achievementPath = path.join(publicDir, "Steam", cracker); fileLocation = ["achievements.ini"]; @@ -102,13 +103,6 @@ export const findAchievementFileInExecutableDirectory = ( export const findAllSteamGameAchievementFiles = () => { const gameAchievementFiles = new Map(); - const crackers = [ - Cracker.codex, - Cracker.goldberg, - Cracker.rune, - Cracker.onlineFix, - ]; - for (const cracker of crackers) { let achievementPath: string; let fileLocation: string[]; @@ -116,6 +110,9 @@ export const findAllSteamGameAchievementFiles = () => { if (cracker === Cracker.onlineFix) { achievementPath = path.join(publicDir, Cracker.onlineFix); fileLocation = ["Stats", "Achievements.ini"]; + } else if (cracker === Cracker.goldberg2) { + achievementPath = path.join(appData, "GSE Saves"); + fileLocation = ["achievements.json"]; } else if (cracker === Cracker.goldberg) { achievementPath = path.join(appData, "Goldberg SteamEmu Saves"); fileLocation = ["achievements.json"]; diff --git a/src/main/services/achievements/merge-achievements.ts b/src/main/services/achievements/merge-achievements.ts index 31ffc767..dd4bd465 100644 --- a/src/main/services/achievements/merge-achievements.ts +++ b/src/main/services/achievements/merge-achievements.ts @@ -52,7 +52,9 @@ export const mergeAchievements = async ( const newAchievements = achievements .filter((achievement) => { return !unlockedAchievements.some((localAchievement) => { - return localAchievement.name === achievement.name.toUpperCase(); + return ( + localAchievement.name.toUpperCase() === achievement.name.toUpperCase() + ); }); }) .map((achievement) => { @@ -64,10 +66,16 @@ export const mergeAchievements = async ( if (newAchievements.length && publishNotification) { const achievementsInfo = newAchievements + .sort((a, b) => { + return a.unlockTime - b.unlockTime; + }) .map((achievement) => { return JSON.parse(localGameAchievement?.achievements || "[]").find( (steamAchievement) => { - return achievement.name === steamAchievement.name; + return ( + achievement.name.toUpperCase() === + steamAchievement.name.toUpperCase() + ); } ); }) @@ -85,12 +93,6 @@ export const mergeAchievements = async ( shop, achievementsInfo ); - - WindowManager.notificationWindow?.setBounds({ y: 50 }); - - setTimeout(() => { - WindowManager.notificationWindow?.setBounds({ y: -9999 }); - }, 4000); } const mergedLocalAchievements = unlockedAchievements.concat(newAchievements); diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index fa63dbea..60ca45b1 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -151,19 +151,21 @@ export class WindowManager { maximizable: false, autoHideMenuBar: true, minimizable: false, - focusable: true, + focusable: false, skipTaskbar: true, frame: false, width: 240, height: 60, x: 25, - y: -9999, + y: 25, webPreferences: { preload: path.join(__dirname, "../preload/index.mjs"), sandbox: false, }, }); + this.notificationWindow.setIgnoreMouseEvents(true); + this.notificationWindow.webContents.openDevTools(); this.notificationWindow.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true, }); diff --git a/src/renderer/index.html b/src/renderer/index.html index e2eea62c..b53595ff 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -9,7 +9,7 @@ content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: local: *; media-src 'self' local: data: *;" /> - +
diff --git a/src/renderer/src/app.css.ts b/src/renderer/src/app.css.ts index 4e0cf7a0..40e15835 100644 --- a/src/renderer/src/app.css.ts +++ b/src/renderer/src/app.css.ts @@ -35,7 +35,6 @@ globalStyle("body", { userSelect: "none", fontFamily: "Noto Sans, sans-serif", fontSize: vars.size.body, - background: vars.color.background, color: vars.color.body, margin: "0", }); diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index 3478273b..bcb8fe80 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -28,7 +28,7 @@ import { import { store } from "./store"; import resources from "@locales"; -import { Achievemnt } from "./pages/achievement/achievement"; +import { Achievement } from "./pages/achievement/achievement"; import "./workers"; import { RepacksContextProvider } from "./context"; @@ -70,7 +70,7 @@ ReactDOM.createRoot(document.getElementById("root")!).render( - + diff --git a/src/renderer/src/pages/achievement/achievement.tsx b/src/renderer/src/pages/achievement/achievement.tsx index 7bc625d3..27559e38 100644 --- a/src/renderer/src/pages/achievement/achievement.tsx +++ b/src/renderer/src/pages/achievement/achievement.tsx @@ -1,14 +1,18 @@ -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import achievementSound from "@renderer/assets/audio/achievement.wav"; import { useTranslation } from "react-i18next"; +import { vars } from "@renderer/theme.css"; -export function Achievemnt() { +interface AchievementInfo { + displayName: string; + iconUrl: string; +} + +export function Achievement() { const { t } = useTranslation("achievement"); - const [achievementInfo, setAchievementInfo] = useState<{ - displayName: string; - icon: string; - } | null>(null); + const [achievements, setAchievements] = useState([]); + const achievementAnimation = useRef(-1); const audio = useMemo(() => { const audio = new Audio(achievementSound); @@ -23,11 +27,7 @@ export function Achievemnt() { if (!achievements) return; if (achievements.length) { - const achievement = achievements[0]; - setAchievementInfo({ - displayName: achievement.displayName, - icon: achievement.iconUrl, - }); + setAchievements((ach) => ach.concat(achievements)); } audio.play(); @@ -39,7 +39,26 @@ export function Achievemnt() { }; }, [audio]); - if (!achievementInfo) return

Nada

; + const hasAchievementsPending = achievements.length > 0; + + useEffect(() => { + if (hasAchievementsPending) { + let zero = performance.now(); + achievementAnimation.current = requestAnimationFrame( + function animateLock(time) { + if (time - zero > 3000) { + zero = performance.now(); + setAchievements((ach) => ach.slice(1)); + } + achievementAnimation.current = requestAnimationFrame(animateLock); + } + ); + } else { + cancelAnimationFrame(achievementAnimation.current); + } + }, [hasAchievementsPending]); + + if (!hasAchievementsPending) return null; return (
{achievementInfo.displayName}

{t("achievement_unlocked")}

-

{achievementInfo.displayName}

+

{achievements[0].displayName}

); diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 79f0e6bd..a92ea029 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -29,5 +29,6 @@ export enum Cracker { rune = "RUNE", onlineFix = "OnlineFix", goldberg = "Goldberg", + goldberg2 = "Goldberg2", generic = "Generic", } diff --git a/yarn.lock b/yarn.lock index 63a6a728..c4320526 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5902,6 +5902,15 @@ iterator.prototype@^1.1.2: reflect.getprototypeof "^1.0.4" set-function-name "^2.0.1" +jackspeak@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" @@ -6484,7 +6493,7 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.3, minimatch@^9.0.4: +minimatch@^9.0.1, minimatch@^9.0.3, minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== @@ -6552,7 +6561,7 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4, minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== @@ -7006,7 +7015,7 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.11.1, path-scurry@^1.6.1: +path-scurry@^1.11.0, path-scurry@^1.11.1, path-scurry@^1.6.1: version "1.11.1" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==