Compare commits

..

21 Commits

Author SHA1 Message Date
bc12f855ce Merge pull request 'tristan-wip' (#2) from tristan-wip into main
Reviewed-on: #2
2025-07-19 19:58:57 +00:00
Trit0
09fcc8f76d fix a couple of stuff 2025-07-19 19:58:57 +00:00
Trit0
0ab4b1f832 control helper and cute tags 2025-07-19 19:58:57 +00:00
club
af42d6c61c hahahahahahah 2025-07-19 19:58:57 +00:00
Trit0
889a00fc49 not always decompress 2025-07-19 19:58:57 +00:00
Trit0
789e0c1e13 better ui, uses fake data, wont be able to start games 2025-07-19 19:58:57 +00:00
club
2872bd6220 all two controllers and no extract every time 2025-07-19 19:58:57 +00:00
Trit0
5c7c429c70 try to support both controllers 2025-07-19 19:58:57 +00:00
club
6ca7501e8b it only support one controller but thats already good 2025-07-19 19:58:57 +00:00
Trit0
c20554c4f6 test 2025-07-19 19:58:57 +00:00
Trit0
8bf08218c0 image! 2025-07-19 19:58:57 +00:00
Trit0
fd33b6df73 test 2 2025-07-19 19:58:57 +00:00
Trit0
900210eeb7 test 2025-07-19 19:58:57 +00:00
Trit0
76997a60a1 chui tanner 2025-07-19 19:58:57 +00:00
Trit0
34864e96ac chui tanner 2025-07-19 19:58:57 +00:00
Trit0
6f9f80a190 images perchance? 3 2025-07-19 19:58:57 +00:00
Trit0
10174b1232 images perchance? 2025-07-19 19:58:57 +00:00
Trit0
39a35e4eaa stupid mistake 2025-07-19 19:58:57 +00:00
Trit0
938e8e3195 start game perchance? 2025-07-19 19:58:57 +00:00
Trit0
73e68bf2bb wip 4 2025-07-19 19:58:57 +00:00
Trit0
7ba175204d wip 3 2025-07-19 19:58:57 +00:00
11 changed files with 32 additions and 259 deletions

21
app.go
View File

@ -98,28 +98,13 @@ func (a *App) StartGame(id string) {
fmt.Println() fmt.Println()
} }
func (a *App) LoadGames(mode string) []models.Metadata { func (a *App) LoadGames() []models.Metadata {
games = provider.GetConjureGameInfo() games = provider.GetConjureGameInfo()
if mode == "remote-showcase" {
games = provider.Filter(games, func(game models.Metadata) bool {
return game.Game == "Soul Shaper" || game.Game == "One Pixel Remaning" || game.Game == "Pong"
})
}
return games return games
} }
func (a *App) LoadGamesNewModel(mode string) []models.Game { func (a *App) LoadGamesNewModel() []models.Game {
mgames := provider.GetConjureGameInfoNew() return []models.Game{}
if mode == "remote-showcase" {
mgames = provider.Filter(mgames, func(game models.Game) bool {
return game.Title == "Soul Shaper" || game.Title == "One Pixel Remaning" || game.Title == "Pong"
})
}
return mgames
} }
func (a *App) Log(message string) { func (a *App) Log(message string) {

View File

@ -30,8 +30,8 @@
</div> </div>
</div> </div>
<OptionsModal v-if="optionsOpen" @close="store.closeModal()"/> <OptionsModal v-if="optionsOpen" @close="optionsOpen = false"/>
<QrModal v-if="qrLink" :link="qrLink" @close="store.closeModal()"/> <QrModal v-if="qrLink" :link="qrLink" @close="qrLink = ''"/>
<LoadingModal v-if="gameIsStarting"/> <LoadingModal v-if="gameIsStarting"/>
</div> </div>
</template> </template>

View File

@ -9,8 +9,6 @@
<select v-model="dataSource" class="mt-1 block w-full"> <select v-model="dataSource" class="mt-1 block w-full">
<option value="local">Local (Mock)</option> <option value="local">Local (Mock)</option>
<option value="remote">Remote (API)</option> <option value="remote">Remote (API)</option>
<option value="remote-showcase">Showcase</option>
<option value="new-model">New Model</option>
</select> </select>
</label> </label>
@ -30,34 +28,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref } from 'vue';
import { useAppStore } from "../stores/app-store";
import { log } from "../services/logger-service";
const emit = defineEmits(['close']); const emit = defineEmits(['close']);
const store = useAppStore();
const dataSource = ref(localStorage.getItem('dataSource') || 'local'); const dataSource = ref(localStorage.getItem('dataSource') || 'local');
const volume = ref(+localStorage.getItem('volume') || 50); const volume = ref(+localStorage.getItem('volume') || 50);
async function save() { function save() {
const oldSource = localStorage.getItem('dataSource');
log(dataSource.value);
localStorage.setItem('dataSource', dataSource.value); localStorage.setItem('dataSource', dataSource.value);
localStorage.setItem('volume', volume.value.toString()); localStorage.setItem('volume', volume.value.toString());
try {
await store.loadGames();
}
catch (error) {
console.error(error);
}
//
// if (oldSource !== dataSource.value) {
// log("Reloading games")
// await store.loadGames();
// }
emit('close'); emit('close');
} }
</script> </script>

View File

@ -1,6 +1,5 @@
import { KeyContext } from "./key-context"; import { KeyContext } from "./key-context";
import { InputManager } from "../input-manager"; import { InputManager } from "../input-manager";
import { mod } from "../../stores/app-store";
export class GamePreviewKeyContext extends KeyContext { export class GamePreviewKeyContext extends KeyContext {
readonly name: string = "GamePreviewKeyContext"; readonly name: string = "GamePreviewKeyContext";
@ -59,7 +58,7 @@ export class GamePreviewKeyContext extends KeyContext {
.filter(el => (el as any).offsetParent !== null); // filter out hidden elements .filter(el => (el as any).offsetParent !== null); // filter out hidden elements
const currentIndex = focusableElements.indexOf(document.activeElement); const currentIndex = focusableElements.indexOf(document.activeElement);
const nextIndex = mod((currentIndex - 1), focusableElements.length); const nextIndex = (currentIndex - 1) % focusableElements.length;
(focusableElements[nextIndex] as any).focus(); (focusableElements[nextIndex] as any).focus();
} }

View File

@ -1,7 +1,5 @@
import { InputManager } from "../input-manager"; import { InputManager } from "../input-manager";
import { ModalKeyContext } from "./modal-key-context"; import { ModalKeyContext } from "./modal-key-context";
import { log } from "../../services/logger-service";
import { mod } from "../../stores/app-store";
export class OptionsKeyContext extends ModalKeyContext { export class OptionsKeyContext extends ModalKeyContext {
public name: string = "OptionsKeyContext"; public name: string = "OptionsKeyContext";
@ -10,110 +8,7 @@ export class OptionsKeyContext extends ModalKeyContext {
super.onEscape(); super.onEscape();
this.store.optionsOpen = false; this.store.optionsOpen = false;
this.store.qrLink = ""; this.store.qrLink = "";
(document.activeElement as any).blur();
InputManager.switchContext('carousel'); InputManager.switchContext('carousel');
} }
protected onKeyUp() {
super.onKeyUp();
this.focusLastElement()
}
protected onKeyDown() {
super.onKeyDown();
this.focusNextElement();
}
private focusNextElement() {
const focusableSelectors = [
'a[href]',
'button:not([disabled])',
'input:not([disabled]):not([type="hidden"])',
'select:not([disabled])',
'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"])'
];
const focusableElements = Array.from(document.querySelectorAll(focusableSelectors.join(',')))
.filter(el => (el as any).offsetParent !== null); // filter out hidden elements
const currentIndex = focusableElements.indexOf(document.activeElement);
const nextIndex = (currentIndex + 1) % focusableElements.length;
(focusableElements[nextIndex] as any).focus();
}
protected onEnter() {
super.onEnter();
const activeElement = document.activeElement;
console.log(activeElement);
(document.activeElement as any).click();
}
private focusLastElement() {
const focusableSelectors = [
'a[href]',
'button:not([disabled])',
'input:not([disabled]):not([type="hidden"])',
'select:not([disabled])',
'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"])'
];
const focusableElements = Array.from(document.querySelectorAll(focusableSelectors.join(',')))
.filter(el => (el as any).offsetParent !== null); // filter out hidden elements
const currentIndex = focusableElements.indexOf(document.activeElement);
const nextIndex = mod((currentIndex - 1), focusableElements.length);
(focusableElements[nextIndex] as any).focus();
}
protected onKeyRight() {
super.onKeyRight();
const activeElement = document.activeElement;
if (activeElement.tagName.toLowerCase() === 'select') {
const mySelect = document.activeElement as HTMLSelectElement;
const currentIndex = mySelect.selectedIndex;
mySelect.selectedIndex = (currentIndex + 1) % mySelect.options.length;
}
else if (activeElement && activeElement.tagName.toLowerCase() === 'input' && (activeElement as HTMLInputElement).type === 'range') {
// Get the current value, convert to number, add 10, and set the new value
const range = (activeElement as HTMLInputElement);
let currentValue = parseInt(range.value);
range.value = (currentValue + 10).toString();
}
else {
this.focusNextElement();
}
}
protected onKeyLeft() {
super.onKeyLeft();
const activeElement = document.activeElement;
if (activeElement.tagName.toLowerCase() === 'select') {
const mySelect = document.activeElement as HTMLSelectElement;
const currentIndex = mySelect.selectedIndex;
mySelect.selectedIndex = mod((currentIndex - 1), mySelect.options.length);
}
else if (activeElement && activeElement.tagName.toLowerCase() === 'input' && (activeElement as HTMLInputElement).type === 'range') {
// Get the current value, convert to number, add 10, and set the new value
const range = (activeElement as HTMLInputElement);
let currentValue = parseInt(range.value);
range.value = (currentValue - 10).toString();
}
else {
this.focusLastElement();
}
}
setAvailableActions() {
this.store.currentAvailableActions = {
"order": "controller,keyboard",
"1,Enter": "Click",
"Power,Escape": "Back"
};
}
} }

View File

@ -1,10 +1,10 @@
import { LoadGames, LoadGamesNewModel } from "../../wailsjs/go/main/App"; import {LoadGames} from "../../wailsjs/go/main/App";
import {models} from "../../wailsjs/go/models"; import {models} from "../../wailsjs/go/models";
import Metadata = models.Metadata; import Metadata = models.Metadata;
import Game = models.Game; import Game = models.Game;
import Developer = models.Developer; import Developer = models.Developer;
const mockGames: Game[] = [ const localGames: Game[] = [
new Game({ new Game({
id: "ddf1ab0c-d86e-442f-8fd8-cfe8a0dc0a52", id: "ddf1ab0c-d86e-442f-8fd8-cfe8a0dc0a52",
title: "Soul Shaper", title: "Soul Shaper",
@ -98,17 +98,13 @@ const mockGames: Game[] = [
export async function fetchGames(): Promise<Game[]> { export async function fetchGames(): Promise<Game[]> {
const source = localStorage.getItem('dataSource') || 'local'; const source = localStorage.getItem('dataSource') || 'local';
if (source === 'local') return mockGames; if (source === 'local') return localGames;
if (source === "new-model") { const games = await LoadGames();
return (await LoadGamesNewModel(source)) ?? [];
}
const games = (await LoadGames(source)) ?? [];
for (const game of games) { for (const game of games) {
console.log(game) console.log(game)
} }
return [...games.map(convertToNewFormat)]; return [...games.map(convertToNewFormat), ...localGames];
} }
function convertToNewFormat(metadata: Metadata): Game { function convertToNewFormat(metadata: Metadata): Game {

View File

@ -34,10 +34,8 @@ export const useAppStore = defineStore('app', {
console.log(this.games); console.log(this.games);
this.tags = [...new Set(this.games.flatMap(game => game.genres.split(",")))]; this.tags = [...new Set(this.games.flatMap(game => game.genres.split(",")))];
if (this.tags.length > 0) { if (this.tags.length > 0) {
this.selectTag(this.tags[0]); this.selectTag(this.tags[0])
} }
this.selectGame(0);
}, },
moveGameRight() { moveGameRight() {
this.selectGame(this.selectedGameIndex + 1); this.selectGame(this.selectedGameIndex + 1);
@ -71,11 +69,6 @@ export const useAppStore = defineStore('app', {
this.qrLink = link; this.qrLink = link;
InputManager.switchContext("options"); InputManager.switchContext("options");
}, },
closeModal() {
this.qrLink = "";
this.optionsOpen = false;
InputManager.switchContext("carousel");
},
async startSelectedGame() { async startSelectedGame() {
if (this.selectedGame && !this.gameIsStarting) { if (this.selectedGame && !this.gameIsStarting) {
this.gameIsStarting = true; this.gameIsStarting = true;
@ -89,6 +82,6 @@ export const useAppStore = defineStore('app', {
} }
}); });
export function mod(n, m) { function mod(n, m) {
return ((n % m) + m) % m; return ((n % m) + m) % m;
} }

View File

@ -3,9 +3,9 @@
import {models} from '../models'; import {models} from '../models';
import {provider} from '../models'; import {provider} from '../models';
export function LoadGames(arg1:string):Promise<Array<models.Metadata>>; export function LoadGames():Promise<Array<models.Metadata>>;
export function LoadGamesNewModel(arg1:string):Promise<Array<models.Game>>; export function LoadGamesNewModel():Promise<Array<models.Game>>;
export function LoadImage(arg1:string,arg2:string):Promise<provider.FileBlob>; export function LoadImage(arg1:string,arg2:string):Promise<provider.FileBlob>;

View File

@ -2,12 +2,12 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export function LoadGames(arg1) { export function LoadGames() {
return window['go']['main']['App']['LoadGames'](arg1); return window['go']['main']['App']['LoadGames']();
} }
export function LoadGamesNewModel(arg1) { export function LoadGamesNewModel() {
return window['go']['main']['App']['LoadGamesNewModel'](arg1); return window['go']['main']['App']['LoadGamesNewModel']();
} }
export function LoadImage(arg1, arg2) { export function LoadImage(arg1, arg2) {

View File

@ -31,7 +31,6 @@ func GetDefaultConjureOsDirectory() string {
case "windows": case "windows":
return filepath.Join(cacheDir, conjureDirectoryName, conjureOsDirectoryName) return filepath.Join(cacheDir, conjureDirectoryName, conjureOsDirectoryName)
case "darwin": case "darwin":
return filepath.Join(configDir, conjureDirectoryName, conjureOsDirectoryName)
case "linux": case "linux":
return filepath.Join(configDir, conjureDirectoryName, conjureOsDirectoryName) return filepath.Join(configDir, conjureDirectoryName, conjureOsDirectoryName)
default: default:

View File

@ -182,14 +182,16 @@ func ExtractGame(game models.Metadata) string {
func GetConjureGameInfo() []models.Metadata { func GetConjureGameInfo() []models.Metadata {
gamePath := config.GetDefaultConjureGamesDirectory() gamePath := config.GetDefaultConjureGamesDirectory()
fmt.Println("Loading games from path " + gamePath + "....") fmt.Println("Loading games....")
fmt.Println(gamePath)
entries, err := os.ReadDir(gamePath)
if err != nil {
log.Fatal(err)
}
var games []models.Metadata var games []models.Metadata
entries, err := os.ReadDir(gamePath)
if err != nil {
return games
}
for _, e := range entries { for _, e := range entries {
if e.IsDir() { if e.IsDir() {
continue continue
@ -217,7 +219,7 @@ func GetConjureGameInfo() []models.Metadata {
fmt.Println("Contents of metadata.txt:") fmt.Println("Contents of metadata.txt:")
metadata, err := io.ReadAll(rc) metadata, err := io.ReadAll(rc)
check(err) check(err)
game := parseGameInfoOld([]byte(escapeBackslashes(string(metadata)))) game := parseGameInfo([]byte(escapeBackslashes(string(metadata))))
fmt.Println(game.ThumbnailPath) fmt.Println(game.ThumbnailPath)
games = append(games, game) games = append(games, game)
} }
@ -226,82 +228,13 @@ func GetConjureGameInfo() []models.Metadata {
} }
if len(games) > 0 { if len(games) > 0 {
fmt.Println("Found Conjure Games: ", len(games)) fmt.Println("Found Conjure Games:", len(games))
} else { } else {
fmt.Println("No Conjure games found") fmt.Println("No Conjure games Found")
} }
return games return games
} }
func GetConjureGameInfoNew() []models.Game {
gamePath := config.GetDefaultConjureGamesDirectory()
fmt.Println("Loading games from path " + gamePath + "....")
var games []models.Game
entries, err := os.ReadDir(gamePath)
if err != nil {
return games
}
for _, e := range entries {
if e.IsDir() {
continue
} else if filepath.Ext(e.Name()) == ".conj" {
conjPath := filepath.Join(gamePath, e.Name())
conjBase := strings.TrimSuffix(conjPath, ".conj")
// Check if the destination folder already exists
if _, err := os.Stat(conjBase); os.IsNotExist(err) {
err = extractZipToSiblingFolder(conjPath)
check(err)
}
// Now read metadata from the extracted directory
entries, err := os.ReadDir(conjBase)
check(err)
fmt.Println("Contents of", conjPath)
for _, f := range entries {
if f.Name() == "metadata.txt" {
rc, err := os.Open(filepath.Join(conjBase, f.Name()))
check(err)
defer rc.Close()
fmt.Println("Contents of metadata.txt:")
metadata, err := io.ReadAll(rc)
check(err)
game, err := parseGameInfo([]byte(escapeBackslashes(string(metadata))))
if err != nil {
continue
}
fmt.Println(game.ThumbnailPath)
games = append(games, game)
}
}
}
}
if len(games) > 0 {
fmt.Println("Found Conjure Games: ", len(games))
} else {
fmt.Println("No Conjure games found")
}
return games
}
func Filter[T any](s []T, predicate func(T) bool) []T {
result := make([]T, 0, len(s)) // Pre-allocate for efficiency
for _, v := range s {
if predicate(v) {
result = append(result, v)
}
}
return result
}
// Function to escape backslashes in the YAML string // Function to escape backslashes in the YAML string
func escapeBackslashes(input string) string { func escapeBackslashes(input string) string {
// Replace every single backslash with double backslashes // Replace every single backslash with double backslashes
@ -320,19 +253,13 @@ func printIndentedPath(path string) {
} }
} }
func parseGameInfoOld(data []byte) models.Metadata { func parseGameInfo(data []byte) models.Metadata {
game := models.Metadata{} game := models.Metadata{}
err := yaml.Unmarshal(data, &game) err := yaml.Unmarshal(data, &game)
check(err) check(err)
return game return game
} }
func parseGameInfo(data []byte) (models.Game, error) {
game := models.Game{}
err := yaml.Unmarshal(data, &game)
return game, err
}
func check(e error) { func check(e error) {
if e != nil { if e != nil {
panic(e) panic(e)