ed
This commit is contained in:
parent
600d79af31
commit
5d11084641
136 changed files with 12007 additions and 584 deletions
224
resources/[freizeit]/[gym]/ps-ui/web/src/App.svelte
Normal file
224
resources/[freizeit]/[gym]/ps-ui/web/src/App.svelte
Normal file
|
@ -0,0 +1,224 @@
|
|||
<script lang="ts">
|
||||
// Svelte
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
// Stores
|
||||
import { isDevMode, showComponent } from './stores/GeneralStores';
|
||||
import { showUi } from './stores/GeneralStores';
|
||||
|
||||
// Enums
|
||||
import { UIComponentsEnum } from './enums/UIComponentsEnum';
|
||||
|
||||
// PS-UI components
|
||||
import GameLauncher from './games/GameLauncher.svelte';
|
||||
import { EventHandler, handleKeyUp } from './../utils/eventHandler';
|
||||
import Image from './components/Image.svelte';
|
||||
import { notificationMock } from './../utils/mockEvent';
|
||||
import Notification from './components/Notification.svelte';
|
||||
import { GamesEnum } from './enums/GamesEnum';
|
||||
import InputComponent from './components/InputComponent.svelte';
|
||||
import StatusBarComponent from './components/StatusBarComponent.svelte';
|
||||
import DrawTextComponent from './components/DrawTextComponent.svelte';
|
||||
import InteractionMenuComponent from './components/InteractionMenuComponent.svelte';
|
||||
import CircleGame from './games/CircleGame.svelte';
|
||||
|
||||
EventHandler();
|
||||
document.onkeyup = handleKeyUp;
|
||||
|
||||
if(isDevMode) {
|
||||
notificationMock();
|
||||
const memoryGameData = {
|
||||
game: GamesEnum.Memory,
|
||||
gameName: 'Memory MiniGame',
|
||||
gameDescription: 'Lorem ipsum is placceholder text commonly used in the arachic, print and publishing industries for previewing layouts and visual mockups.',
|
||||
amountOfAnswers: 9,
|
||||
gameTime: 30, // seconds
|
||||
maxAnswersIncorrect: 0,
|
||||
displayInitialAnswersFor: 5, //seconds
|
||||
gridSize: 10, //5,6,7,8,9,10 - one of these values as number of rows and columns
|
||||
triggerEvent: 'memorygame-callback',
|
||||
};
|
||||
// setupGame({ data: memoryGameData});
|
||||
|
||||
const scramblerGameData = {
|
||||
game: GamesEnum.Scrambler,
|
||||
gameName: 'Scrambler MiniGame',
|
||||
gameDescription: 'Lorem ipsum is placeholder text commonly used in the arachic, print and publishing industries for previewing layouts and visual mockups.',
|
||||
amountOfAnswers: 4, // count of numbers to display
|
||||
gameTime: 80, // seconds
|
||||
sets: 'numeric', // numeric, alphabet, alphanumeric, greek, braille, runes
|
||||
changeBoardAfter: 3 //seconds
|
||||
};
|
||||
// setupGame({ data: scramblerGameData});
|
||||
|
||||
const numberMazeGameData = {
|
||||
game: GamesEnum.NumberMaze,
|
||||
gameName: 'Number Maze MiniGame',
|
||||
gameDescription: 'Lorem ipsum is placeholder text commonly used in the arachic, print and publishing industries for previewing layouts and visual mockups.',
|
||||
gameTime: 30, // seconds
|
||||
maxAnswersIncorrect: 2,
|
||||
triggerEvent: 'maze-callback',
|
||||
};
|
||||
// setupGame({ data: numberMazeGameData});
|
||||
|
||||
const numberPuzzleGameData = {
|
||||
game: GamesEnum.NumberPuzzle,
|
||||
gameName: 'Number Puzzle MiniGame',
|
||||
gameDescription: 'Lorem ipsum is placeholder text commonly used in the arachic, print and publishing industries for previewing layouts and visual mockups.',
|
||||
gameTime: 3, // seconds
|
||||
maxAnswersIncorrect: 2,
|
||||
amountOfAnswers: 5,
|
||||
timeForNumberDisplay: 5, // seconds
|
||||
triggerEvent: 'var-callback',
|
||||
};
|
||||
// setupGame({ data: numberPuzzleGameData});
|
||||
|
||||
//input
|
||||
const inputData = [
|
||||
{
|
||||
id: '1',
|
||||
label: 'Name',
|
||||
icon: 'fa-solid fa-pen',
|
||||
placeholder: 'Insert name',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
label: 'Password',
|
||||
icon: 'fa-solid fa-lock',
|
||||
placeholder: 'Enter password',
|
||||
type: 'password',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
label: 'Phone',
|
||||
icon: 'fa-solid fa-phone',
|
||||
placeholder: 'Enter phone number',
|
||||
type: 'phone',
|
||||
},
|
||||
];
|
||||
|
||||
// showInput(inputData);
|
||||
|
||||
const statusBarData = {
|
||||
title: 'Area Dominance',
|
||||
description: 'Write some description here',
|
||||
icon: 'fa-solid fa-circle-info',
|
||||
items: [
|
||||
{
|
||||
key: 'Gang', value: 'Ballas'
|
||||
},
|
||||
{
|
||||
key: 'Dominance', value: '20%'
|
||||
}
|
||||
],
|
||||
};
|
||||
// showStatusBar(statusBarData);
|
||||
|
||||
// // double call for status bar
|
||||
// setTimeout(() => {
|
||||
// showStatusBar(
|
||||
// {
|
||||
// title: 'Area Check',
|
||||
// description: 'Whats up',
|
||||
// icon: 'fa-solid fa-heart',
|
||||
// items: [
|
||||
// {
|
||||
// key: 'Gang', value: 'Ace'
|
||||
// }
|
||||
// ],
|
||||
// }
|
||||
// )
|
||||
// }, 5000);
|
||||
|
||||
const drawTextData = {
|
||||
icon: 'fa-solid fa-circle-info',
|
||||
keys: 'Press [E] to interact',
|
||||
color: 'yellow'
|
||||
};
|
||||
// showDrawTextMenu(drawTextData);
|
||||
|
||||
// // double call for draw text
|
||||
// setTimeout(() => {
|
||||
// showDrawTextMenu(
|
||||
// {
|
||||
// icon: 'fa-solid fa-circle-info',
|
||||
// keys: 'Press [E] to interact and check if the old one exists',
|
||||
// color: ''
|
||||
// }
|
||||
// );
|
||||
// }, 5000);
|
||||
|
||||
const interactionMenuData = [
|
||||
{
|
||||
header: 'Menu item 1',
|
||||
text: 'Some text',
|
||||
icon: 'fa-solid fa-user',
|
||||
color: '#02f1b5',
|
||||
callback: '',
|
||||
subMenu: null,
|
||||
},
|
||||
{
|
||||
header: 'Menu item 2',
|
||||
icon: 'fa-solid fa-user',
|
||||
color: '',
|
||||
callback: '',
|
||||
subMenu: [
|
||||
{
|
||||
header: 'Submenu1',
|
||||
icon: 'fa-solid fa-circle-info',
|
||||
color: '#02f1b5',
|
||||
},
|
||||
{
|
||||
header: 'Submenu2',
|
||||
icon: 'fa-solid fa-circle-info',
|
||||
color: '#02f1b5',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
// setupInteractionMenu(interactionMenuData);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $showUi }
|
||||
<!-- class="min-h-screen min-w-full" -->
|
||||
<main transition:fade={{ duration: 100 }} class="main-bg">
|
||||
{#if $showComponent === UIComponentsEnum.StatusBar}
|
||||
<StatusBarComponent />
|
||||
{/if}
|
||||
|
||||
{#if $showComponent === UIComponentsEnum.DrawText}
|
||||
<DrawTextComponent />
|
||||
{/if}
|
||||
|
||||
{#if $showComponent === UIComponentsEnum.Menu}
|
||||
<InteractionMenuComponent />
|
||||
{/if}
|
||||
|
||||
{#if $showComponent === UIComponentsEnum.Input}
|
||||
<InputComponent />
|
||||
{/if}
|
||||
|
||||
{#if $showComponent === UIComponentsEnum.Game}
|
||||
<GameLauncher />
|
||||
{/if}
|
||||
|
||||
{#if $showComponent === UIComponentsEnum.Image}
|
||||
<Image />
|
||||
{/if}
|
||||
|
||||
{#if $showComponent === UIComponentsEnum.Notification}
|
||||
<Notification />
|
||||
{/if}
|
||||
<CircleGame />
|
||||
</main>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.main-bg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
resources/[freizeit]/[gym]/ps-ui/web/src/assets/svelte.png
Normal file
BIN
resources/[freizeit]/[gym]/ps-ui/web/src/assets/svelte.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
|
@ -0,0 +1,40 @@
|
|||
<script lang="ts">
|
||||
export let color: string = '';
|
||||
</script>
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 437.3 512"
|
||||
style="enable-background:new 0 0 437.3 512;"
|
||||
xml:space="preserve"
|
||||
fill={color}
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d="M230.3,455.7c-8.3,0-16.6,0-25,0c-8.5-3.1-17.2-5.7-25.4-9.4c-21.9-9.8-42.1-22.2-58.1-40.4c-10-11.3-16.2-24.2-15.6-39.9
|
||||
c0.5-13.5,0.1-27,0.1-40.5c0-2-0.2-3.2-2.5-4.1c-24.3-9.4-38-29.4-38.1-55.6c-0.1-24.7-0.3-49.4,0.1-74.1c0.2-9.7,0.9-19.6,3.2-29
|
||||
c12.1-47.6,42.8-78.7,87.9-96.4c21.1-8.3,43.3-10.3,65.8-10c33.5,0.4,64.5,8.9,91.8,28.7c35.8,26,56,60.8,56.8,105.6
|
||||
c0.5,25.3,0.2,50.7,0,76c-0.2,25.5-14.4,45.8-37.8,54.6c-2.4,0.9-2.8,2.1-2.8,4.3c0.1,14.3-0.1,28.6,0.1,42.9
|
||||
c0.2,11.1-3.6,20.9-9.8,29.8c-10,14.4-23.5,24.8-38.1,34C266.6,442.5,249.2,450.7,230.3,455.7z M165.8,219.1
|
||||
c-4.7,0.4-9.3,0.5-14,1.1c-12.7,1.5-22,8-26.3,20.1c-4.1,11.4-2.3,22.9,2.4,33.7c3.9,8.9,13.3,13.7,22.6,10.8c9-2.8,17.7-7.2,26-12
|
||||
c8.6-5,15.2-12.4,19.6-21.6c5.8-12,2.3-22.3-9.5-28.3C180,219.5,173,218.9,165.8,219.1z M274.1,218.4c-5.8,1-11.9,1.4-17.5,3.2
|
||||
c-14.4,4.7-19.2,16.8-12.3,30.2c1.7,3.3,3.8,6.5,6.2,9.4c9.1,11.3,21.8,17.4,34.8,22.6c11.9,4.8,24.3-0.9,28.1-13.1
|
||||
c1.9-6.2,2.5-13,2.9-19.5c0.7-12.6-7.2-24.7-19.2-28.7C289.9,220.2,282,219.7,274.1,218.4z M212.6,371.3c-0.5-6.4-2.3-9-6.2-9.2
|
||||
c-3.9-0.2-5.8,2.4-6.9,8.9c-4,0-7.9,0-12.1,0c0.3-4.2-0.8-7.4-4.8-8.6c-4.1-1.2-6.5,1.3-8.5,4.6c-3.7-2-5.4-4.9-5.4-8.9
|
||||
c0-5.8,0-11.7,0-17.5c0-6.9-0.8-8.1-7.2-10.2c-9.7-3.3-19.4-6.5-29.2-9.7c-1.3-0.4-2.7-0.9-4.1-1c-3-0.2-5.1,1.3-6,4
|
||||
c-1,2.7-0.2,5.2,2.2,6.9c1.2,0.9,2.8,1.4,4.2,1.9c8.4,2.8,16.7,5.7,25.1,8.4c2,0.6,2.6,1.6,2.6,3.6c-0.1,4.7,0,9.4,0,14
|
||||
c0,7,2.8,12.6,8.2,16.9c8.1,6.6,17.7,8.5,27.7,8.5c17,0.1,34.1,0.3,51.1-0.1c6.8-0.2,13.7-1.2,20.3-3c9.4-2.7,15.9-8.8,16.8-19.2
|
||||
c0.5-5.4,0.7-10.9,0.6-16.3c0-2.6,0.7-3.9,3.3-4.7c8.7-2.7,17.2-5.7,25.8-8.8c4.8-1.7,6.3-6.8,3.1-10.2c-2.4-2.6-5.3-2.2-8.3-1.2
|
||||
c-10.3,3.5-20.6,7-31,10.4c-3.8,1.2-5.4,3.6-5.3,7.5c0.1,6,0.2,12,0,17.9c-0.1,4.8-1.2,9.3-6.5,11.3c-1.1-3.4-3.1-5.9-6.7-5.4
|
||||
c-5.1,0.6-5.4,4.8-5.7,9c-4.2,0-8.1,0-12.1,0c-0.5-6.3-2.3-8.9-6.2-9.1c-3.8-0.2-5.8,2.4-6.9,9.2
|
||||
C220.6,371.3,216.7,371.3,212.6,371.3z M218,331C218,331,218,331,218,331c3.9,0,7.8,0.2,11.7-0.1c5.6-0.3,8.7-3,9.4-8.6
|
||||
c0.4-3.4,0.3-7-0.1-10.5c-1.6-11.9-4.4-23.5-11.1-33.7c-5.3-8.2-13.3-8.9-18.6-1.1c-9,13.3-12.5,28.4-12.3,44.3
|
||||
c0.1,5.9,3.9,9.2,9.8,9.5C210.4,331.2,214.2,331,218,331z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M512 208v224c0 8.75-7.25 16-16 16H416v-128h-32v128h-64v-128h-32v128H224v-128H192v128H128v-128H96v128H16C7.25 448 0 440.8 0 432v-224C0 199.2 7.25 192 16 192H64V144C64 135.2 71.25 128 80 128H128V80C128 71.25 135.2 64 144 64h224C376.8 64 384 71.25 384 80V128h48C440.8 128 448 135.2 448 144V192h48C504.8 192 512 199.2 512 208z"/></svg>
|
After Width: | Height: | Size: 570 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.1.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M400 0C426.5 0 448 21.49 448 48V144C448 170.5 426.5 192 400 192H352V224H608C625.7 224 640 238.3 640 256C640 273.7 625.7 288 608 288H512V320H560C586.5 320 608 341.5 608 368V464C608 490.5 586.5 512 560 512H400C373.5 512 352 490.5 352 464V368C352 341.5 373.5 320 400 320H448V288H192V320H240C266.5 320 288 341.5 288 368V464C288 490.5 266.5 512 240 512H80C53.49 512 32 490.5 32 464V368C32 341.5 53.49 320 80 320H128V288H32C14.33 288 0 273.7 0 256C0 238.3 14.33 224 32 224H288V192H240C213.5 192 192 170.5 192 144V48C192 21.49 213.5 0 240 0H400zM256 64V128H384V64H256zM224 448V384H96V448H224zM416 384V448H544V384H416z"/></svg>
|
After Width: | Height: | Size: 858 B |
|
@ -0,0 +1,229 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import Icon from "./Icon.svelte";
|
||||
import { isDevMode, showComponent, showUi } from "../stores/GeneralStores";
|
||||
import { drawTextStore, hideDrawTextMenu, hideDrawTextStore } from "../stores/DrawTextStore";
|
||||
|
||||
let drawTextDataValue:any = {};
|
||||
drawTextStore.subscribe((value) => drawTextDataValue = value);
|
||||
|
||||
let hideDrawTextValue = false;
|
||||
hideDrawTextStore.subscribe(value => {
|
||||
hideDrawTextValue = value;
|
||||
});
|
||||
|
||||
let marginLeftVal = '2%';
|
||||
let interactionText = [], colorValue = '';
|
||||
|
||||
onMount(() => {
|
||||
getColorValue();
|
||||
handleInteractionText();
|
||||
|
||||
if(isDevMode) {
|
||||
setTimeout(() => {
|
||||
hideDrawTextMenu();
|
||||
}, 10000);
|
||||
}
|
||||
});
|
||||
|
||||
function getColorValue() {
|
||||
switch(drawTextDataValue.color) {
|
||||
case "primary":
|
||||
colorValue ="#0275d8";
|
||||
break;
|
||||
case "error":
|
||||
colorValue = "#d9534f";
|
||||
break;
|
||||
case "success":
|
||||
colorValue = "#5cb85c";
|
||||
break;
|
||||
case "warning":
|
||||
colorValue = "#f0ad4e";
|
||||
break;
|
||||
case "info":
|
||||
colorValue = "#5bc0de";
|
||||
break;
|
||||
case "mint":
|
||||
colorValue = "#a1f8c7";
|
||||
break;
|
||||
default:
|
||||
colorValue = "var(--color-green)";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handleInteractionText() {
|
||||
let matches = drawTextDataValue.keys.match(/\[(.*?)\]/);
|
||||
if (matches) {
|
||||
let submatch = matches[0];
|
||||
|
||||
let splitText = drawTextDataValue.keys.split(submatch);
|
||||
interactionText = [
|
||||
{
|
||||
value: splitText[0],
|
||||
color: 'var(--color-lightgrey)'
|
||||
},
|
||||
{
|
||||
value: submatch,
|
||||
color: colorValue
|
||||
},
|
||||
{
|
||||
value: splitText[1],
|
||||
color: 'var(--color-lightgrey)'
|
||||
}
|
||||
];
|
||||
} else {
|
||||
interactionText = [
|
||||
{
|
||||
value: drawTextDataValue.keys,
|
||||
color: 'var(--color-lightgrey)'
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
function closeDrawText() {
|
||||
const statusWrapperDom = document.getElementById('draw-text-wrapper');
|
||||
if(statusWrapperDom) {
|
||||
statusWrapperDom.style.animation = '2s slide-left';
|
||||
|
||||
let keyFrames = document.createElement('style');
|
||||
|
||||
keyFrames.innerHTML = `
|
||||
@keyframes slide-left {
|
||||
from {
|
||||
margin-left: `+marginLeftVal+`;
|
||||
}
|
||||
to {
|
||||
margin-left: -20%;
|
||||
}
|
||||
}
|
||||
|
||||
.draw-text-wrapper {
|
||||
-moz-animation: 2s slide-left;
|
||||
-webkit-animation: 2s slide-left;
|
||||
animation: 2s slide-left;
|
||||
}
|
||||
`;
|
||||
|
||||
statusWrapperDom.appendChild(keyFrames);
|
||||
|
||||
setTimeout(() => {
|
||||
showUi.set(false);
|
||||
showComponent.set(null);
|
||||
|
||||
drawTextStore.set({
|
||||
// title: '',
|
||||
icon: '',
|
||||
keys: '',
|
||||
color: ''
|
||||
});
|
||||
hideDrawTextStore.set(false);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if(hideDrawTextValue) {
|
||||
closeDrawText();
|
||||
}
|
||||
|
||||
if(drawTextDataValue) {
|
||||
handleInteractionText();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="draw-text-wrapper" class="draw-text-wrapper" style="margin-left:{marginLeftVal};">
|
||||
<div class="draw-text-title-wrapper">
|
||||
<div class="icon">
|
||||
<Icon icon={drawTextDataValue.icon} styleColor={colorValue} />
|
||||
</div>
|
||||
|
||||
<div class="title-info">
|
||||
<p class="title-description">
|
||||
{#each interactionText as text}
|
||||
<p style="color: {text.color};">{text.value}</p>
|
||||
{/each}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.draw-text-wrapper {
|
||||
-moz-animation: 1s slide-right;
|
||||
-webkit-animation: 1s slide-right;
|
||||
animation: 1s slide-right;
|
||||
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
|
||||
min-width: 10vw;
|
||||
width: max-content;
|
||||
|
||||
min-height: 3vw;
|
||||
height: fit-content;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
background-color: var(--color-darkblue);
|
||||
border-radius: 0.3vw;
|
||||
|
||||
padding: 0.75vw 1.5vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@keyframes slide-right {
|
||||
from {
|
||||
margin-left: -20%;
|
||||
}
|
||||
to {
|
||||
margin-left: 2%;
|
||||
}
|
||||
}
|
||||
@keyframes slide-left {
|
||||
from {
|
||||
margin-left: 2%;
|
||||
}
|
||||
to {
|
||||
margin-left: -100%;
|
||||
}
|
||||
}
|
||||
|
||||
.draw-text-wrapper > .draw-text-title-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.draw-text-wrapper > .draw-text-title-wrapper > .icon {
|
||||
width: 1.5vw;
|
||||
margin: auto 0.75vw auto 0;
|
||||
|
||||
font-size: 1.25vw;
|
||||
}
|
||||
.draw-text-wrapper > .draw-text-title-wrapper > .title-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.draw-text-wrapper > .draw-text-title-wrapper > .title-info > .title-description {
|
||||
font-size: 1vw;
|
||||
font-weight: 300;
|
||||
color: var(--color-lightgrey);
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
word-wrap: break-word;
|
||||
flex-wrap: wrap;
|
||||
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
.draw-text-wrapper > .draw-text-title-wrapper > .title-info > .title-description > p {
|
||||
margin-right: 0.25vw;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,8 @@
|
|||
<script lang="ts">
|
||||
export let icon: string;
|
||||
export let color: string = '';
|
||||
export let classes: string = '';
|
||||
export let styleColor: string = '';
|
||||
</script>
|
||||
|
||||
<i class="{color} {icon} {classes}" style="color: {styleColor};" />
|
|
@ -0,0 +1,13 @@
|
|||
<script lang="ts">
|
||||
import { imageStore } from './../stores/ImageStore';
|
||||
import { fly } from 'svelte/transition';
|
||||
</script>
|
||||
|
||||
{#if $imageStore.show}
|
||||
<div class="flex items-center justify-center min-h-screen" transition:fly="{{y: +400}}">
|
||||
<div class="flex items-center flex-col ps-bg-darkblue p-10 shadow-md shadow-gray-800 rounded-md">
|
||||
<!-- svelte-ignore a11y-img-redundant-alt -->
|
||||
<img src={$imageStore.url} alt="Image Placeholder" />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
|
@ -0,0 +1,203 @@
|
|||
<script lang="ts">
|
||||
import { hideUi, isDevMode } from "../stores/GeneralStores";
|
||||
import { inputStore } from "../stores/InputStores";
|
||||
import Icon from "./Icon.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import fetchNui from "../../utils/fetch";
|
||||
import { handleKeyUp } from "../../utils/eventHandler";
|
||||
|
||||
document.onkeyup = handleKeyUp;
|
||||
let inputData:any = $inputStore;
|
||||
|
||||
onMount(() => {
|
||||
inputData = inputData.map((val) => {
|
||||
val.value = null;
|
||||
return val;
|
||||
});
|
||||
});
|
||||
|
||||
function submit() {
|
||||
let returnData = [];
|
||||
|
||||
let inputs = document.querySelectorAll('input');
|
||||
inputs.forEach((input: HTMLInputElement, index) => {
|
||||
let returnObj = {
|
||||
id: input.id,
|
||||
value: input.value,
|
||||
// compIndex: index
|
||||
};
|
||||
|
||||
returnData.push(returnObj)
|
||||
});
|
||||
|
||||
if(!isDevMode) {
|
||||
fetchNui('input-callback', returnData);
|
||||
}
|
||||
|
||||
closeInputs();
|
||||
}
|
||||
|
||||
export function closeInputs(): void {
|
||||
let inputs = document.querySelectorAll('input');
|
||||
inputs.forEach((input: HTMLInputElement) => {
|
||||
input.value = '';
|
||||
});
|
||||
|
||||
inputData = [];
|
||||
|
||||
if(!isDevMode) {
|
||||
fetchNui('input-close', { ok: true });
|
||||
}
|
||||
|
||||
hideUi();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="input-base-wrapper">
|
||||
<div class="logo">
|
||||
<img src="./images/ps-logo.png" alt="ps-logo" />
|
||||
</div>
|
||||
|
||||
<div class="input-form">
|
||||
{#each inputData as inputValue}
|
||||
<div class="input-wrapper">
|
||||
<div class="input-data-wrapper">
|
||||
<div class="input-icon">
|
||||
<Icon icon={inputValue.icon} color="ps-text-green" classes="text-2xl" />
|
||||
</div>
|
||||
<div class="input-area">
|
||||
<p class="label">
|
||||
{inputValue.label}
|
||||
</p>
|
||||
<input id={inputValue.id} type={inputValue.type} class="value" placeholder={inputValue.placeholder} value={inputValue.value} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="horizontal-line"></div>
|
||||
<!-- <div class="input-message">
|
||||
{inputValue.placeholder}
|
||||
</div> -->
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="button-wrapper">
|
||||
<button class="submit-btn" on:click={submit}>Submit</button>
|
||||
<button class="cancel-btn" on:click={closeInputs}>Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.input-base-wrapper {
|
||||
/* centering the view */
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
width: 25vw;
|
||||
min-height: 28vw;
|
||||
height: fit-content;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
background-color: var(--color-darkblue);
|
||||
border-radius: 0.3vw;
|
||||
|
||||
padding: 0.5vw 2vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.input-base-wrapper > .input-form {
|
||||
margin-top: 1vw;
|
||||
height: 100%;
|
||||
|
||||
/* border: 0.1px solid red; */
|
||||
}
|
||||
|
||||
.input-base-wrapper > .input-form > .input-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.input-base-wrapper > .input-form > .input-wrapper > .horizontal-line {
|
||||
width: inherit;
|
||||
height: 1px;
|
||||
background-color: var(--color-lightgrey);
|
||||
}
|
||||
.input-base-wrapper > .input-form > .input-wrapper > .input-message {
|
||||
font-size: 0.8vw;
|
||||
opacity: 0.85;
|
||||
color: var(--color-lightgrey);
|
||||
margin-top: 0.2vw;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.input-base-wrapper > .input-form > .input-wrapper > .input-data-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
padding: 0.5vw 0;
|
||||
}
|
||||
.input-base-wrapper > .input-form > .input-wrapper:not(:last-child) {
|
||||
margin-bottom: 1vw;
|
||||
}
|
||||
|
||||
.input-base-wrapper > .input-form > .input-wrapper > .input-data-wrapper > .input-icon {
|
||||
margin-right: 0.75vw;
|
||||
width: 1.5vw;
|
||||
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
.input-base-wrapper > .input-form > .input-wrapper > .input-data-wrapper > .input-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
color: var(--color-lightgrey);
|
||||
}
|
||||
.input-base-wrapper > .input-form > .input-wrapper > .input-data-wrapper > .input-area > .label {
|
||||
font-size: 1vw;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
.input-base-wrapper > .input-form > .input-wrapper > .input-data-wrapper > .input-area > .value {
|
||||
font-size: 0.8vw;
|
||||
font-weight: 300 !important;
|
||||
width: 18vw;
|
||||
background-color: inherit;
|
||||
margin-top: 0.2vw;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
padding-left: 0;
|
||||
outline: none;
|
||||
border-bottom-width: 2px;
|
||||
border-bottom-color: var(--color-green);
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.input-base-wrapper > .button-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
|
||||
margin: 1.5vw auto 1.25vw auto;
|
||||
color: var(--color-black);
|
||||
}
|
||||
.input-base-wrapper > .button-wrapper > .submit-btn {
|
||||
background-color: var(--color-green);
|
||||
}
|
||||
.input-base-wrapper > .button-wrapper > .cancel-btn {
|
||||
background-color: var(--color-darkgrey);
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0.3vw;
|
||||
padding: 0.3vw 1vw;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
button:not(:last-child) {
|
||||
margin-right: 0.5vw;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,256 @@
|
|||
<script lang="ts">
|
||||
import { closeInteractionMenu, menuStore } from "../stores/MenuStores";
|
||||
import Icon from "./Icon.svelte";
|
||||
import fetchNui from "../../utils/fetch";
|
||||
import { isDevMode } from "../stores/GeneralStores";
|
||||
|
||||
let menuData:Array<any> = $menuStore;
|
||||
let selectedMenuItem = null;
|
||||
let subMenu = null;
|
||||
|
||||
let subMenuTextColorOverride = {
|
||||
id: null, color: 'black'
|
||||
};
|
||||
let menuTextColorOverride = {
|
||||
id: null, color: 'black'
|
||||
};
|
||||
|
||||
function handleMenuSelection(selectedMenu) {
|
||||
selectedMenuItem = selectedMenu;
|
||||
|
||||
if(selectedMenu) {
|
||||
|
||||
if(!selectedMenu.subMenu && !isDevMode) {
|
||||
fetchNui('MenuSelect', {data :selectedMenu});
|
||||
closeInteractionMenu();
|
||||
} else {
|
||||
subMenu = selectedMenuItem.subMenu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleSubMenuSelection(selectedSubMenu) {
|
||||
const formData = {
|
||||
data:selectedSubMenu
|
||||
};
|
||||
if(!isDevMode) {
|
||||
fetchNui('MenuSelect', formData);
|
||||
closeInteractionMenu();
|
||||
}
|
||||
}
|
||||
|
||||
function handleItemHover(itemId, index, color, action='enter', isSubMenu = false) {
|
||||
const itemDom = document.getElementById(itemId);
|
||||
if(action === 'enter') {
|
||||
if(isSubMenu) {
|
||||
itemDom.style.backgroundColor = color || 'var(--color-green)';
|
||||
subMenuTextColorOverride.id = index;
|
||||
} else {
|
||||
itemDom.style.backgroundColor = color || 'var(--color-green)';
|
||||
menuTextColorOverride.id = index;
|
||||
}
|
||||
} else {
|
||||
itemDom.style.backgroundColor = 'var(--color-darkblue)';
|
||||
subMenuTextColorOverride.id = null;
|
||||
menuTextColorOverride.id = null;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="menu-base-wrapper">
|
||||
<div class="screen-base">
|
||||
{#if !selectedMenuItem}
|
||||
<div class="header-slot" style="border: 3px solid var(--color-green);">
|
||||
<img src="./images/ps-logo.png" alt="ps-logo" />
|
||||
</div>
|
||||
|
||||
<div class="screen-body">
|
||||
{#each menuData as menu, index}
|
||||
<div id={"menu-"+index} class="each-panel"
|
||||
on:mouseenter={() => handleItemHover("menu-"+index, index, menu.color, 'enter', false)}
|
||||
on:mouseleave={() => handleItemHover("menu-"+index, index, menu.color, 'leave', false)}
|
||||
on:click={() => handleMenuSelection(menu)}>
|
||||
<div class="menu-icon">
|
||||
<Icon icon={menu.icon} styleColor={menuTextColorOverride.id === index ? menuTextColorOverride.color : menu?.color || 'var(--color-green)'} />
|
||||
</div>
|
||||
<div class="menu-details">
|
||||
<p class="header" style="color: {menuTextColorOverride.id === index ? menuTextColorOverride.color : 'var(--color-white)'};">{menu.header}</p>
|
||||
{#if menu.hasOwnProperty('text')}
|
||||
<p class="text" style="color: {menuTextColorOverride.id === index ? menuTextColorOverride.color : 'var(--color-lightgrey)'};">{menu.text}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if menu?.subMenu}
|
||||
<div class="chevron" style="color: {menuTextColorOverride.id === index ? menuTextColorOverride.color : 'var(--color-white)'};">
|
||||
<i class="fa-solid fa-chevron-right"></i>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if selectedMenuItem.hasOwnProperty('subMenu') && selectedMenuItem.subMenu}
|
||||
<div class="submenu-header-slot" on:click={() => handleMenuSelection(null)}>
|
||||
<i class="fa-solid fa-chevron-left left-chevron"></i>
|
||||
<p class="main-menu">Main Menu</p>
|
||||
</div>
|
||||
|
||||
<div class="screen-body">
|
||||
{#each subMenu as menu, index}
|
||||
<div id={"sub-menu-"+index} class="each-panel"
|
||||
on:mouseenter={() => handleItemHover("sub-menu-"+index, index, menu.color, 'enter', true)}
|
||||
on:mouseleave={() => handleItemHover("sub-menu-"+index, index, menu.color, 'leave', true)}
|
||||
on:click={() => handleSubMenuSelection(menu)}>
|
||||
<div id={"menu-icon-"+index} class="menu-icon">
|
||||
<Icon icon={menu.icon} styleColor={subMenuTextColorOverride.id === index ? subMenuTextColorOverride.color : menu.color || 'var(--color-green)'} />
|
||||
</div>
|
||||
<div class="menu-details">
|
||||
<p class="header" style="color: {subMenuTextColorOverride.id === index ? subMenuTextColorOverride.color : 'var(--color-white)'};">{menu.header}</p>
|
||||
{#if menu.hasOwnProperty('text')}
|
||||
<p class="text" style="color: {subMenuTextColorOverride.id === index ? subMenuTextColorOverride.color : 'var(--color-lightgrey)'};">{menu.text}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.menu-base-wrapper {
|
||||
/* centering the view */
|
||||
position: absolute;
|
||||
left: 70%;
|
||||
top: 40%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
width: 20vw;
|
||||
height: 30vw;
|
||||
/* min-height: 4vw;
|
||||
height: fit-content; */
|
||||
|
||||
overflow: hidden;
|
||||
/* border: 0.1px solid red; */
|
||||
}
|
||||
|
||||
.menu-base-wrapper > .screen-base {
|
||||
height: 100%;
|
||||
/* border: 0.1px solid yellow; */
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.menu-base-wrapper > .screen-base > .header-slot {
|
||||
background-color: var(--color-darkblue);
|
||||
border-radius: 0.3vw;
|
||||
|
||||
padding: 0.5vw 2vw;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
min-height: 3vw;
|
||||
height: fit-content;
|
||||
}
|
||||
.menu-base-wrapper > .screen-base > .header-slot > img {
|
||||
height: 4vw;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.menu-base-wrapper > .screen-base > .submenu-header-slot {
|
||||
min-height: 4vw;
|
||||
height: max-content;
|
||||
|
||||
background-color: var(--color-darkblue);
|
||||
border-radius: 0.3vw;
|
||||
color: var(--color-white);
|
||||
|
||||
padding: 0.3vw 1vw;
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.menu-base-wrapper > .screen-base > .submenu-header-slot > .left-chevron {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
margin-right: 0.5vw;
|
||||
|
||||
width: 1.5vw;
|
||||
|
||||
font-size: 1.1vw;
|
||||
}
|
||||
.menu-base-wrapper > .screen-base > .submenu-header-slot > .main-menu {
|
||||
font-weight: 500;
|
||||
font-size: 1.25vw;
|
||||
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.menu-base-wrapper > .screen-base > .screen-body {
|
||||
margin-top: 0.3vw;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* border: 0.1px solid blue; */
|
||||
}
|
||||
|
||||
.menu-base-wrapper > .screen-base > .screen-body > .each-panel {
|
||||
min-height: 4vw;
|
||||
height: max-content;
|
||||
|
||||
background-color: var(--color-darkblue);
|
||||
border-radius: 0.3vw;
|
||||
|
||||
padding: 0.5vw 1vw;
|
||||
cursor: pointer;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.menu-base-wrapper > .screen-base > .screen-body > .each-panel:not(:last-child) {
|
||||
margin-bottom: 0.3vw;
|
||||
}
|
||||
|
||||
.menu-base-wrapper > .screen-base > .screen-body > .each-panel > .menu-icon {
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
margin-right: 0.5vw;
|
||||
|
||||
width: 1.5vw;
|
||||
|
||||
font-size: 1vw;
|
||||
}
|
||||
|
||||
.menu-base-wrapper > .screen-base > .screen-body > .each-panel > .menu-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.menu-base-wrapper > .screen-base > .screen-body > .each-panel > .menu-details > .header {
|
||||
font-size: 0.8vw;
|
||||
white-space: nowrap;
|
||||
/* color: var(--color-white); */
|
||||
}
|
||||
|
||||
.menu-base-wrapper > .screen-base > .screen-body > .each-panel > .menu-details > .text {
|
||||
font-size: 0.6vw;
|
||||
white-space: nowrap;
|
||||
/* color: var(--color-lightgrey); */
|
||||
}
|
||||
|
||||
.menu-base-wrapper > .screen-base > .screen-body > .each-panel > .chevron {
|
||||
margin-left: 72%;
|
||||
font-size: 1vw;
|
||||
/* color: var(--color-white); */
|
||||
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,27 @@
|
|||
<script lang="ts">
|
||||
import { notifications } from './../stores/NotificationStore';
|
||||
import { NotificationIcons, NotificationTypes } from './../enums/NotificationTypesEnum';
|
||||
</script>
|
||||
|
||||
<div class="flex justify-end min-h-screen">
|
||||
<div class="flex flex-col mr-4 mt-4 w-[200px]">
|
||||
{#each $notifications as notification}
|
||||
<div
|
||||
class="{notification.type} flex items-center px-4 py-3 mb-1 text-white min-w-full rounded"
|
||||
>
|
||||
<div class="text-2xl mt-1">
|
||||
{#if notification.type === NotificationTypes.Success}
|
||||
<i class={NotificationIcons.Success} />
|
||||
{:else if notification.type === NotificationTypes.Warning}
|
||||
<i class={NotificationIcons.Warning} />
|
||||
{:else if notification.type === NotificationTypes.Error}
|
||||
<i class={NotificationIcons.Error} />
|
||||
{:else if notification.type === NotificationTypes.Info}
|
||||
<i class={NotificationIcons.Info} />
|
||||
{/if}
|
||||
</div>
|
||||
<span class="ml-3">{notification.text}</span>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,195 @@
|
|||
<script lang="ts">
|
||||
import { hideStatusBar, hideStatusBarStore, statusBarStore } from "../stores/StatusBarStores";
|
||||
import { onMount } from "svelte";
|
||||
import Icon from "./Icon.svelte";
|
||||
import type { IStatusBarItem } from "src/interfaces/IStatusBar";
|
||||
import { isDevMode, showComponent, showUi } from "../stores/GeneralStores";
|
||||
|
||||
let statusData:any = $statusBarStore;
|
||||
let statusDataItems:[IStatusBarItem] = statusData.items;
|
||||
|
||||
statusBarStore.subscribe((value) => {
|
||||
statusData = value;
|
||||
statusDataItems = statusData.items;
|
||||
});
|
||||
|
||||
let hideStatusBarValue = false;
|
||||
hideStatusBarStore.subscribe(value => {
|
||||
hideStatusBarValue = value;
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
if(isDevMode) {
|
||||
setTimeout(() => {
|
||||
hideStatusBar();
|
||||
}, 10000);
|
||||
}
|
||||
});
|
||||
|
||||
function closeStatusBar() {
|
||||
const statusWrapperDom = document.getElementById('status-bar-wrapper');
|
||||
if(statusWrapperDom) {
|
||||
statusWrapperDom.style.animation = '2s hide-statusbar';
|
||||
|
||||
let keyFrames = document.createElement('style');
|
||||
|
||||
keyFrames.innerHTML = `
|
||||
@keyframes hide-statusbar {
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.status-bar-wrapper {
|
||||
-moz-animation: 2s hide-statusbar;
|
||||
-webkit-animation: 2s hide-statusbar;
|
||||
animation: 2s hide-statusbar;
|
||||
}
|
||||
`;
|
||||
|
||||
statusWrapperDom.appendChild(keyFrames);
|
||||
|
||||
setTimeout(() => {
|
||||
showUi.set(false);
|
||||
showComponent.set(null);
|
||||
|
||||
statusBarStore.set({
|
||||
title: '',
|
||||
description: '',
|
||||
items: [],
|
||||
icon: '',
|
||||
});
|
||||
hideStatusBarStore.set(false);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if(hideStatusBarValue) {
|
||||
// if(hideStatusBarValue || !hideStatusBarValue)
|
||||
closeStatusBar();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="status-bar-wrapper" class="status-bar-wrapper">
|
||||
<div class="status-title-wrapper">
|
||||
<div class="icon">
|
||||
<Icon icon={statusData.icon} color="ps-text-green" />
|
||||
</div>
|
||||
|
||||
<div class="title-info">
|
||||
<p class="title">
|
||||
{statusData.title}
|
||||
</p>
|
||||
<p class="title-description">
|
||||
{statusData.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="items-wrapper">
|
||||
{#each statusDataItems as item}
|
||||
<div class="each-item">
|
||||
<p class="label">
|
||||
{item.key}:
|
||||
</p>
|
||||
<p class="value">
|
||||
{item.value}
|
||||
</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.status-bar-wrapper {
|
||||
-moz-animation: 2s display-status;
|
||||
-webkit-animation: 2s display-status;
|
||||
animation: 2s display-status;
|
||||
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 1%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
width: 23vw;
|
||||
min-height: 8vw;
|
||||
height: fit-content;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
background-color: var(--color-darkblue);
|
||||
border-radius: 0.3vw;
|
||||
|
||||
padding: 1vw 2vw;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@keyframes display-status {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.status-bar-wrapper > .status-title-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.status-bar-wrapper > .status-title-wrapper > .icon {
|
||||
width: 1.5vw;
|
||||
margin-right: 0.5vw;
|
||||
|
||||
font-size: 1.25vw;
|
||||
}
|
||||
.status-bar-wrapper > .status-title-wrapper > .title-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.status-bar-wrapper > .status-title-wrapper > .title-info > .title {
|
||||
font-size: 1.3vw;
|
||||
font-weight: 500;
|
||||
color: var(--color-white);
|
||||
}
|
||||
.status-bar-wrapper > .status-title-wrapper > .title-info > .title-description {
|
||||
font-size: 0.95vw;
|
||||
font-weight: 200;
|
||||
color: var(--color-lightgrey);
|
||||
|
||||
margin-top: -0.2vw;
|
||||
}
|
||||
|
||||
.status-bar-wrapper > .items-wrapper {
|
||||
margin-left: 2vw;
|
||||
margin-top: 0.5vw;
|
||||
}
|
||||
.status-bar-wrapper > .items-wrapper > .each-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
word-wrap: break-word;
|
||||
flex-wrap: wrap;
|
||||
|
||||
font-size: 0.95vw;
|
||||
}
|
||||
.status-bar-wrapper > .items-wrapper > .each-item:not(:last-child) {
|
||||
margin-bottom: 0.3vw;
|
||||
}
|
||||
|
||||
.status-bar-wrapper > .items-wrapper > .each-item > .label {
|
||||
color: var(--color-lightgrey);
|
||||
}
|
||||
.status-bar-wrapper > .items-wrapper > .each-item > .value {
|
||||
color: var(--color-green);
|
||||
margin-left: 0.3vw;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,4 @@
|
|||
export enum ConnectingGameMessageEnum {
|
||||
Connecting = 'CONNECTING TO INTERFACE',
|
||||
Connected = 'CONNECTED. GET READY.',
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export enum GamesEnum {
|
||||
Scrambler = 'Scramber',
|
||||
NumberMaze = 'NumberMaze',
|
||||
Memory = 'MemoryGame',
|
||||
NumberPuzzle = 'NumberPuzzle',
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
export enum NotificationTypes {
|
||||
Success = 'ps-notification-success',
|
||||
Error = 'ps-notification-error',
|
||||
Warning = 'ps-notification-warning',
|
||||
Info = 'ps-notification-info',
|
||||
}
|
||||
|
||||
export enum NotificationIcons {
|
||||
Success = 'fa-solid fa-circle-check',
|
||||
Error = 'fa-solid fa-circle-exclamation',
|
||||
Warning = 'fa-solid fa-triangle-exclamation',
|
||||
Info = 'fa-solid fa-circle-info',
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
export enum UIComponentsEnum {
|
||||
StatusBar = 'StatusBar',
|
||||
Menu = 'Menu',
|
||||
Input = 'Input',
|
||||
Game = 'Game',
|
||||
MemoryGame = 'MemoryGame',
|
||||
Image = 'ShowImage',
|
||||
DrawText = 'DrawText',
|
||||
Notification = 'Notify',
|
||||
None = 'hideUi',
|
||||
}
|
139
resources/[freizeit]/[gym]/ps-ui/web/src/games/CircleGame.svelte
Normal file
139
resources/[freizeit]/[gym]/ps-ui/web/src/games/CircleGame.svelte
Normal file
|
@ -0,0 +1,139 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
let canvas;
|
||||
let ctx;
|
||||
let W, H, degrees = 0, new_degrees = 0, color = "#38D5AF";
|
||||
let txtcolor = "#ffffff", bgcolor = "#2B312B", bgcolor2 = "#068f6d", bgcolor3 = "#00ff00";
|
||||
let key_to_press, g_start, g_end, animation_loop;
|
||||
let needed = 4, streak = 0, circle_started = false, time = 2;
|
||||
|
||||
function getRandomInt(min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
||||
}
|
||||
|
||||
function StartCircle() {
|
||||
ctx.clearRect(0, 0, W, H);
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = bgcolor;
|
||||
ctx.lineWidth = 20;
|
||||
ctx.arc(W / 2, H / 2, Math.min(W, H) / 2 - 10, 0, Math.PI * 2, false);
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = correct === true ? bgcolor3 : bgcolor2;
|
||||
ctx.lineWidth = 20;
|
||||
ctx.arc(W / 2, H / 2, Math.min(W, H) / 2 - 10, g_start - 90 * Math.PI / 180, g_end - 90 * Math.PI / 180, false);
|
||||
ctx.stroke();
|
||||
let radians = degrees * Math.PI / 180;
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = 40;
|
||||
ctx.arc(W / 2, H / 2, Math.min(W, H) / 2 - 20, radians - 0.1 - 90 * Math.PI / 180, radians - 90 * Math.PI / 180, false);
|
||||
ctx.stroke();
|
||||
ctx.fillStyle = txtcolor;
|
||||
ctx.font = "100px sans-serif";
|
||||
let text_width = ctx.measureText(key_to_press).width;
|
||||
ctx.fillText(key_to_press, W / 2 - text_width / 2, H / 2 + 35);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
if (typeof animation_loop !== undefined) clearInterval(animation_loop);
|
||||
g_start = getRandomInt(20, 40) / 10;
|
||||
g_end = getRandomInt(5, 10) / 10;
|
||||
g_end = g_start + g_end;
|
||||
degrees = 0;
|
||||
new_degrees = 360;
|
||||
key_to_press = '' + getRandomInt(1, 4);
|
||||
animation_loop = setInterval(animate_to, time);
|
||||
}
|
||||
|
||||
function animate_to() {
|
||||
if (degrees >= new_degrees) {
|
||||
CircleFail();
|
||||
return;
|
||||
}
|
||||
degrees += 2;
|
||||
StartCircle();
|
||||
}
|
||||
|
||||
function correct() {
|
||||
streak += 1;
|
||||
if (streak == needed) {
|
||||
clearInterval(animation_loop);
|
||||
endGame(true);
|
||||
} else {
|
||||
draw();
|
||||
}
|
||||
}
|
||||
|
||||
function CircleFail() {
|
||||
clearInterval(animation_loop);
|
||||
endGame(false);
|
||||
}
|
||||
|
||||
function startGame() {
|
||||
document.getElementById('circle').style.display = 'block';
|
||||
document.getElementById('circle').style.pointerEvents = 'auto';
|
||||
circle_started = true;
|
||||
draw();
|
||||
}
|
||||
|
||||
function endGame(status) {
|
||||
document.getElementById('circle').style.display = 'none';
|
||||
circle_started = false;
|
||||
let endResult = status ? true : false;
|
||||
fetch(`https://ps-ui/circle-result`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({endResult})
|
||||
});
|
||||
streak = 0;
|
||||
needed = 4;
|
||||
}
|
||||
|
||||
export function setupCircleGame(data: { circles?: number; time?: number }) {
|
||||
needed = data.circles ?? 4;
|
||||
time = data.time ?? 2;
|
||||
startGame();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
canvas = document.getElementById("circle");
|
||||
ctx = canvas.getContext("2d");
|
||||
W = window.innerWidth * 0.2;
|
||||
H = window.innerHeight * 0.2;
|
||||
canvas.width = W;
|
||||
canvas.height = H;
|
||||
|
||||
window.addEventListener("message", (event) => {
|
||||
if (event.data.action == "circle-start") {
|
||||
needed = event.data.circles ?? 4;
|
||||
time = event.data.time ?? 2;
|
||||
startGame();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("keydown", function (ev) {
|
||||
let key_pressed = ev.key;
|
||||
let valid_keys = ['1', '2', '3', '4'];
|
||||
if (valid_keys.includes(key_pressed) && circle_started) {
|
||||
if (key_pressed === key_to_press) {
|
||||
let d_start = (180 / Math.PI) * g_start;
|
||||
let d_end = (180 / Math.PI) * g_end;
|
||||
if (degrees < d_start || degrees > d_end) {
|
||||
CircleFail();
|
||||
} else {
|
||||
correct();
|
||||
}
|
||||
} else {
|
||||
CircleFail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="absolute inset-0 flex items-center justify-center" style="pointer-events: none; z-index: 100;">
|
||||
<canvas id="circle" class="w-auto h-auto" style="display: none;"></canvas>
|
||||
</div>
|
104
resources/[freizeit]/[gym]/ps-ui/web/src/games/GameBase.svelte
Normal file
104
resources/[freizeit]/[gym]/ps-ui/web/src/games/GameBase.svelte
Normal file
|
@ -0,0 +1,104 @@
|
|||
<script lang="ts">
|
||||
import { currentActiveGameDetails, currentGameActive } from "../stores/GameLauncherStore";
|
||||
import Skull from "../assets/svgs/Skull.svelte";
|
||||
import { GamesEnum } from "../enums/GamesEnum";
|
||||
import NewMemoryGame from "./MemoryGame.svelte";
|
||||
import SuccessFailureScreen from "./SuccessFailureScreen.svelte";
|
||||
import NewScrambler from "./Scrambler.svelte";
|
||||
import NewNumberMaze from "./NumberMaze.svelte";
|
||||
import NewNumberPuzzle from "./NumberPuzzle.svelte";
|
||||
|
||||
const skullColor: string = 'var(--color-green)';
|
||||
|
||||
let showResultScreen = false, hackSuccess = false;
|
||||
</script>
|
||||
|
||||
{#if !showResultScreen}
|
||||
<div class="games-container">
|
||||
<div class="game-wrapper ps-bg-darkblue">
|
||||
<div class="skull-logo">
|
||||
<Skull color={skullColor} />
|
||||
</div>
|
||||
|
||||
<div class="game-heading">
|
||||
<p class="ps-font-arcade">
|
||||
{$currentActiveGameDetails?.gameName}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="game-description">
|
||||
<p>{$currentActiveGameDetails?.gameDescription}</p>
|
||||
</div>
|
||||
|
||||
<div class="main-game-body">
|
||||
{#if $currentGameActive === GamesEnum.Memory}
|
||||
<NewMemoryGame on:game-ended={(event) => {showResultScreen = true; hackSuccess = event.detail.hackSuccess; }} />
|
||||
{:else if $currentGameActive === GamesEnum.Scrambler}
|
||||
<NewScrambler on:game-ended={(event) => {showResultScreen = true; hackSuccess = event.detail.hackSuccess; }} />
|
||||
{:else if $currentGameActive === GamesEnum.NumberMaze}
|
||||
<NewNumberMaze on:game-ended={(event) => {showResultScreen = true; hackSuccess = event.detail.hackSuccess; }} />
|
||||
{:else if $currentGameActive === GamesEnum.NumberPuzzle}
|
||||
<NewNumberPuzzle on:game-ended={(event) => {showResultScreen = true; hackSuccess = event.detail.hackSuccess; }} />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<SuccessFailureScreen isSuccess={hackSuccess} />
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.games-container {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
border-radius: 0.2vw;
|
||||
}
|
||||
|
||||
.games-container > .game-wrapper {
|
||||
width: 33vw;
|
||||
height: 45vw;
|
||||
border-radius: 0.3vw;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.games-container > .game-wrapper > .skull-logo {
|
||||
margin: 0 auto;
|
||||
padding-top: 1vw;
|
||||
width: 5vw;
|
||||
}
|
||||
|
||||
.games-container > .game-wrapper > .game-heading {
|
||||
font-size: 1.1vw;
|
||||
letter-spacing: 0.4vw;
|
||||
color: var(--color-lightgrey);
|
||||
|
||||
margin: 0.25vw auto;
|
||||
|
||||
width: 19vw;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.games-container > .game-wrapper > .game-description {
|
||||
font-size: 0.7vw;
|
||||
font-weight: normal;
|
||||
color: var(--color-lightgrey);
|
||||
text-align: center;
|
||||
|
||||
width: 33vw;
|
||||
margin: 1vw auto;
|
||||
}
|
||||
|
||||
.games-container > .game-wrapper > .main-game-body {
|
||||
margin: 0.75vw auto 2vw auto;
|
||||
width: 31vw;
|
||||
/* border: 0.1px solid yellow; */
|
||||
height: 30vw;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,79 @@
|
|||
<script lang="ts">
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import Skull from '../assets/svgs/Skull.svelte';
|
||||
('./../../utils/mockEvent');
|
||||
import { connectionText, showLoading } from '../stores/GameLauncherStore';
|
||||
import { ConnectingGameMessageEnum } from '../enums/GameConnectionMessages';
|
||||
import GameBase from './GameBase.svelte';
|
||||
|
||||
const skullColor: string = '#02f1b5';
|
||||
|
||||
let loadingBar: HTMLDivElement;
|
||||
let gameLoaded = false;
|
||||
|
||||
/** Asynchronously connects to a game and resolves the Promise when completed.
|
||||
* Uses a loading bar to show progress, incrementing by 1% every 30ms until it reaches 100%.
|
||||
*
|
||||
* @returns Promise that resolves when the loading bar reaches 100%.
|
||||
*/
|
||||
async function connectToGame(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
let width = 0;
|
||||
let interval = setInterval(() => {
|
||||
width++;
|
||||
loadingBar.style.width = `${width}%`;
|
||||
|
||||
if (width === 100) {
|
||||
clearInterval(interval);
|
||||
resolve();
|
||||
}
|
||||
}, 30);
|
||||
});
|
||||
}
|
||||
|
||||
// Call init() on mount
|
||||
onMount(async () => {
|
||||
// Show a loading indicator while connecting to the game server
|
||||
connectionText.set(ConnectingGameMessageEnum.Connecting);
|
||||
showLoading.set(true);
|
||||
|
||||
// Connect to the game server
|
||||
await connectToGame();
|
||||
|
||||
// Hide loading indicator when completed connecting to the game server
|
||||
connectionText.set(ConnectingGameMessageEnum.Connected);
|
||||
setTimeout(() => {
|
||||
showLoading.set(false);
|
||||
gameLoaded = true;
|
||||
}, 2000);
|
||||
});
|
||||
|
||||
onDestroy(async () => {
|
||||
gameLoaded = false
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if $showLoading}
|
||||
<div class="flex min-h-screen justify-center items-center">
|
||||
<div
|
||||
class="flex flex-col h-[400px] w-[700px] ps-bg-darkblue shadow-md shadow-black justify-center items-center"
|
||||
>
|
||||
<span class="w-40"><Skull color={skullColor} /></span>
|
||||
<p class="text-white text-3xl mt-2">{$connectionText}</p>
|
||||
|
||||
<!-- Loading wrapper -->
|
||||
<div class="flex mt-10 ps-border-green border-4 w-[80%] h-10">
|
||||
<!-- Loading bar progress -->
|
||||
<div
|
||||
bind:this={loadingBar}
|
||||
class="ps-bg-green opacity-40 will-change-auto w-0"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if !$showLoading && gameLoaded}
|
||||
<GameBase />
|
||||
{/if}
|
269
resources/[freizeit]/[gym]/ps-ui/web/src/games/MemoryGame.svelte
Normal file
269
resources/[freizeit]/[gym]/ps-ui/web/src/games/MemoryGame.svelte
Normal file
|
@ -0,0 +1,269 @@
|
|||
<script lang="ts">
|
||||
import { closeGame, currentActiveGameDetails, gameSettings } from "../stores/GameLauncherStore";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import fetchNui from "../../utils/fetch";
|
||||
import { isDevMode } from "../stores/GeneralStores";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let gridSizesAcceptable = [
|
||||
{
|
||||
numberOfRowCol: 5,
|
||||
cubeSize: '4.2vw',
|
||||
gap: '1vw'
|
||||
},
|
||||
{
|
||||
numberOfRowCol: 6,
|
||||
cubeSize: '3.7vw',
|
||||
gap: '0.8vw'
|
||||
},
|
||||
{
|
||||
numberOfRowCol: 7,
|
||||
cubeSize: '2.9vw',
|
||||
gap: '1vw'
|
||||
},
|
||||
{
|
||||
numberOfRowCol: 8,
|
||||
cubeSize: '2.6vw',
|
||||
gap: '0.9vw'
|
||||
},
|
||||
{
|
||||
numberOfRowCol: 9,
|
||||
cubeSize: '2.4vw',
|
||||
gap: '0.75vw'
|
||||
},
|
||||
{
|
||||
numberOfRowCol: 10,
|
||||
cubeSize: '2.1vw',
|
||||
gap: '0.75vw'
|
||||
},
|
||||
];
|
||||
|
||||
let gameTimeRemaining = 0;
|
||||
|
||||
let numberOfCorrectCubesToDisplay = $gameSettings.amountOfAnswers; // how many cubes to remember for game - increment number based on difficulty level
|
||||
let gameTime = $gameSettings.gameTime * 100;
|
||||
let numberOfWrongClicksAllowed = $gameSettings.maxAnswersIncorrect;
|
||||
|
||||
let correctIndices = [], displayCorrectIndicesFor = $currentActiveGameDetails.displayInitialAnswersFor * 1000; // time in seconds
|
||||
let counter, gameStarted = false, gameEnded = false;
|
||||
let hackSuccess = false;
|
||||
let numberOfCubes = $currentActiveGameDetails.gridSize * $currentActiveGameDetails.gridSize;
|
||||
let allCubes = [];
|
||||
|
||||
onMount(() => {
|
||||
//generate random indices from number of cubes
|
||||
while(correctIndices.length < numberOfCorrectCubesToDisplay){
|
||||
const r = Math.floor(Math.random() * numberOfCubes);
|
||||
if(correctIndices.indexOf(r) === -1) correctIndices.push(r);
|
||||
}
|
||||
|
||||
//generating an array to maintain each cube data by index
|
||||
for(let i = 0; i < numberOfCubes; i++) {
|
||||
const cubeData = {
|
||||
cubeIndex: i,
|
||||
isCorrectAnswer: correctIndices.includes(i),
|
||||
isClicked: false
|
||||
};
|
||||
allCubes.push(cubeData);
|
||||
allCubes = allCubes;
|
||||
}
|
||||
|
||||
let cubeWidthHeightValue = gridSizesAcceptable.filter((accept) => {
|
||||
return accept.numberOfRowCol === $currentActiveGameDetails.gridSize;
|
||||
})[0];
|
||||
|
||||
setTimeout(() => {
|
||||
//assigning cube width and height
|
||||
allCubes.forEach((cube) => {
|
||||
const gameContainer = document.getElementById('memory-game-container');
|
||||
if(gameContainer) {
|
||||
gameContainer.style.gap = cubeWidthHeightValue.gap;
|
||||
}
|
||||
|
||||
const cubeDom = document.getElementById('each-cube-'+cube.cubeIndex);
|
||||
if(cubeDom) {
|
||||
cubeDom.style.width = cubeWidthHeightValue.cubeSize;
|
||||
cubeDom.style.height = cubeWidthHeightValue.cubeSize;
|
||||
cubeDom.style.border = "2px solid var(--color-green)";
|
||||
}
|
||||
});
|
||||
}, 1500);
|
||||
|
||||
//stop showing the correct cubes and start the guessing game
|
||||
setTimeout(() => {
|
||||
gameStarted = true;
|
||||
counter = setInterval(startTimer, 10);
|
||||
}, displayCorrectIndicesFor + 1500);
|
||||
|
||||
});
|
||||
|
||||
function startTimer() {
|
||||
if (gameTime <= 0)
|
||||
{
|
||||
gameEnded = true;
|
||||
hackSuccess = isSuccessful();
|
||||
clearInterval(counter);
|
||||
return;
|
||||
}
|
||||
gameTime--;
|
||||
gameTimeRemaining = gameTime/100;
|
||||
}
|
||||
|
||||
function isSuccessful() {
|
||||
//all correct cubes clicked and wrong clicks are within threshold then success
|
||||
|
||||
//all correct cubes clicked
|
||||
let allCorrectCubesClicked = false;
|
||||
allCubes.map((item) => {
|
||||
if(item.isCorrectAnswer && item.isClicked) {
|
||||
allCorrectCubesClicked = true;
|
||||
}
|
||||
if(item.isCorrectAnswer && !item.isClicked) {
|
||||
allCorrectCubesClicked = false;
|
||||
}
|
||||
});
|
||||
|
||||
//wrong clicks within threshold
|
||||
const wrongClickedCubes = getWrongClicks();
|
||||
const wrongClicksWithinThreshold = wrongClickedCubes.length < numberOfWrongClicksAllowed;
|
||||
|
||||
return allCorrectCubesClicked && wrongClicksWithinThreshold;
|
||||
}
|
||||
|
||||
function getWrongClicks() {
|
||||
return allCubes.filter((item) => {
|
||||
return item.isClicked && !item.isCorrectAnswer;
|
||||
});
|
||||
}
|
||||
|
||||
function guessAnswer(guessedCube) {
|
||||
if(!gameEnded) {
|
||||
const cubeIndexInArray = allCubes.findIndex((item) => item.cubeIndex === guessedCube.cubeIndex);
|
||||
|
||||
let updatedCube = guessedCube;
|
||||
updatedCube.isClicked = true;
|
||||
|
||||
allCubes[cubeIndexInArray] = updatedCube;
|
||||
|
||||
const wrongClickedCubes = getWrongClicks();
|
||||
|
||||
// if wrong clicks are done, end the game
|
||||
if(wrongClickedCubes.length >= numberOfWrongClicksAllowed) {
|
||||
clearInterval(counter);
|
||||
setTimeout(() => {
|
||||
hackSuccess = false;
|
||||
gameTimeRemaining = 0;
|
||||
gameEnded = true;
|
||||
return;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
hackSuccess = isSuccessful();
|
||||
|
||||
if(hackSuccess) {
|
||||
clearInterval(counter);
|
||||
gameTimeRemaining = 0;
|
||||
gameEnded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if(gameEnded) {
|
||||
if(!isDevMode) {
|
||||
fetchNui('minigame:callback', hackSuccess);
|
||||
dispatch('game-ended', { hackSuccess });
|
||||
}
|
||||
|
||||
dispatch('closeUI', {hackSuccess})
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyEvent(event) {
|
||||
let key_pressed = event.key;
|
||||
let valid_keys = ['Escape'];
|
||||
|
||||
if(gameStarted && valid_keys.includes(key_pressed) && !gameEnded) {
|
||||
switch(key_pressed){
|
||||
case 'Escape':
|
||||
closeGame(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown|preventDefault={handleKeyEvent} />
|
||||
<div class="memory-game-base">
|
||||
<div class="time-left">
|
||||
<i class="fa-solid fa-clock ps-text-lightgrey clock-icon"></i>
|
||||
<p class="{gameTimeRemaining !== 0 ? 'game-timer-var' : 'mr-1'}">{gameTimeRemaining} </p> time remaining
|
||||
</div>
|
||||
|
||||
<div id="memory-game-container" class="memory-game-container" style="gap: 13px;">
|
||||
{#each allCubes as cube}
|
||||
<div
|
||||
id={'each-cube-'+cube.cubeIndex}
|
||||
on:click={() => guessAnswer(cube)}
|
||||
style="width: 0px; height: 0px; border: 0px;"
|
||||
class="each-cube {gameStarted ? 'cursor-pointer' : 'cursor-default'} {!gameStarted ? (cube.isCorrectAnswer ? 'ps-bg-green-cube' : '') : (cube.isClicked && cube.isCorrectAnswer ? 'ps-bg-green-cube' : (cube.isClicked && !cube.isCorrectAnswer ? 'ps-bg-wrong-cube' : ''))}">
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.memory-game-base {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
height: 30vw;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--color-lightgrey);
|
||||
}
|
||||
|
||||
.memory-game-base > .time-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
font-size: 0.85vw;
|
||||
}
|
||||
.memory-game-base > .time-left > .clock-icon {
|
||||
padding-top: 0.17vw;
|
||||
margin-right: 0.3vw;
|
||||
}
|
||||
.memory-game-base > .time-left > .game-timer-var {
|
||||
width: 2.5vw;
|
||||
}
|
||||
|
||||
.memory-game-base > .memory-game-container {
|
||||
/* border: 0.1px solid red; */
|
||||
|
||||
margin-top: 1vw;
|
||||
width: 30vw;
|
||||
height: 29vw;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
/* gap: 1.5vw; */
|
||||
}
|
||||
.memory-game-base > .memory-game-container > .each-cube {
|
||||
/* width: 4.5vw;
|
||||
height: 4.5vw; */
|
||||
|
||||
background-color: var(--cube-bg-darkgreen);
|
||||
border: 2px solid var(--color-green);
|
||||
}
|
||||
|
||||
.ps-bg-green-cube {
|
||||
background-color: var(--color-green) !important;
|
||||
}
|
||||
.ps-bg-wrong-cube {
|
||||
background-color: var(--color-red) !important;
|
||||
}
|
||||
</style>
|
331
resources/[freizeit]/[gym]/ps-ui/web/src/games/NumberMaze.svelte
Normal file
331
resources/[freizeit]/[gym]/ps-ui/web/src/games/NumberMaze.svelte
Normal file
|
@ -0,0 +1,331 @@
|
|||
<script lang="ts">
|
||||
import { closeGame, gameSettings } from "../stores/GameLauncherStore";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import fetchNui from "../../utils/fetch";
|
||||
import { getRandomArbitrary, isDevMode } from "../stores/GeneralStores";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let gameTimeRemaining = 0;
|
||||
|
||||
let gameTime = $gameSettings.gameTime * 100;
|
||||
let numberOfWrongClicksAllowed = $gameSettings.maxAnswersIncorrect;
|
||||
|
||||
let counter, gameStarted = false, gameEnded = false;
|
||||
let numberOfCubes = 49, allCubes = [];
|
||||
let blinkingIndex, correctRoute = [], goodPositions = [], stopBlinking = false;
|
||||
let lastPos = 0, wrongAnswerCount = 0;
|
||||
let displayCubeNumbers = false;
|
||||
|
||||
onMount(() => {
|
||||
//get starting blinking index
|
||||
blinkingIndex = getRandomArbitrary(1,4); // 4 => highest index, => lowest index for blink
|
||||
|
||||
//generate the correct route
|
||||
correctRoute = generateBestRoute(blinkingIndex);
|
||||
|
||||
goodPositions = Object.keys(correctRoute);
|
||||
|
||||
//generating an array to maintain each cube data by index
|
||||
for(let i = 0; i < numberOfCubes; i++) {
|
||||
const cubeValue = [blinkingIndex, blinkingIndex * 7].includes(i) ? getRandomArbitrary(1,4) : getRandomArbitrary(1,5);
|
||||
|
||||
const cubeData = {
|
||||
cubeIndex: i,
|
||||
cubeValue: goodPositions.includes(i.toLocaleString()) ? correctRoute[i] : cubeValue,
|
||||
classList: ''
|
||||
};
|
||||
allCubes.push(cubeData);
|
||||
allCubes = allCubes;
|
||||
}
|
||||
|
||||
//stop showing the correct cubes and start the guessing game
|
||||
setTimeout(() => {
|
||||
gameStarted = true;
|
||||
counter = setInterval(startTimer, 10);
|
||||
}, 1000);
|
||||
|
||||
});
|
||||
|
||||
function maxVertical(pos) {
|
||||
return Math.floor((48-pos)/7);
|
||||
}
|
||||
|
||||
function maxHorizontal(pos) {
|
||||
let max = (pos+1) % 7;
|
||||
if(max > 0) return 7-max;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
function generateNextPosition(pos) {
|
||||
let maxV = maxVertical(pos);
|
||||
let maxH = maxHorizontal(pos);
|
||||
if(maxV === 0 ){
|
||||
let new_pos = getRandomArbitrary(getRandomArbitrary(1,maxH), maxH);
|
||||
return [new_pos, pos+new_pos];
|
||||
}
|
||||
if(maxH === 0 ){
|
||||
let new_pos = getRandomArbitrary(getRandomArbitrary(1,maxV), maxV);
|
||||
return [new_pos, pos+(new_pos*7)];
|
||||
}
|
||||
if(Math.floor(Math.random() * 1000 + 1) % 2 === 0 ){
|
||||
let new_pos = getRandomArbitrary(getRandomArbitrary(1,maxH), maxH);
|
||||
return [new_pos, pos+new_pos];
|
||||
} else {
|
||||
let new_pos = getRandomArbitrary(getRandomArbitrary(1,maxV), maxV);
|
||||
return [new_pos, pos+(new_pos*7)];
|
||||
}
|
||||
}
|
||||
|
||||
function generateBestRoute(start_pos) {
|
||||
let route = [];
|
||||
if(getRandomArbitrary(1,1000) % 2 === 0 ){
|
||||
start_pos *= 7;
|
||||
}
|
||||
while(start_pos < 48){
|
||||
let new_pos = generateNextPosition(start_pos);
|
||||
route[start_pos] = new_pos[0];
|
||||
start_pos = new_pos[1];
|
||||
}
|
||||
|
||||
return route;
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
if (gameTime <= 0)
|
||||
{
|
||||
wrongAnswerCount = numberOfWrongClicksAllowed;
|
||||
checkMazeAnswer();
|
||||
return;
|
||||
}
|
||||
gameTime--;
|
||||
gameTimeRemaining = gameTime/100;
|
||||
}
|
||||
|
||||
function updateAllCubesArrayWithClassListOfClickedCube(isGood, clickedCube) {
|
||||
const additionClassString = isGood ? ' ps-bg-green-cube' : ' ps-bg-wrong-cube';
|
||||
const newClassList = clickedCube.classList + additionClassString;
|
||||
clickedCube.classList = newClassList;
|
||||
|
||||
//replace the clicked cube data in allCubes array
|
||||
allCubes[clickedCube.cubeIndex] = clickedCube;
|
||||
allCubes = allCubes;
|
||||
}
|
||||
|
||||
|
||||
function handleCubeClick(clickedCube) {
|
||||
if(!gameEnded && clickedCube.cubeIndex !== 0) {
|
||||
let posClicked = clickedCube.cubeIndex;
|
||||
//game just started and user made first click
|
||||
if(lastPos === 0) {
|
||||
//stop the blinking and hide numbers on cubes
|
||||
stopBlinking = true;
|
||||
if([blinkingIndex, blinkingIndex * 7].includes(posClicked)) {
|
||||
lastPos = posClicked;
|
||||
|
||||
//display good cube click and update allCubes array
|
||||
updateAllCubesArrayWithClassListOfClickedCube(true, clickedCube);
|
||||
} else {
|
||||
wrongAnswerCount++;
|
||||
//display bad cube click and update allCubes array
|
||||
updateAllCubesArrayWithClassListOfClickedCube(false, clickedCube);
|
||||
}
|
||||
} else {
|
||||
let posJumps = allCubes[lastPos].cubeValue;
|
||||
let maxV = maxVertical(lastPos);
|
||||
let maxH = maxHorizontal(lastPos);
|
||||
|
||||
if(posJumps <= maxH && posClicked === lastPos + posJumps) {
|
||||
lastPos = posClicked;
|
||||
//display good cube click and update allCubes array
|
||||
updateAllCubesArrayWithClassListOfClickedCube(true, clickedCube);
|
||||
} else if (posJumps <= maxV && posClicked === lastPos + (posJumps * 7)) {
|
||||
lastPos = posClicked;
|
||||
//display good cube click and update allCubes array
|
||||
updateAllCubesArrayWithClassListOfClickedCube(true, clickedCube);
|
||||
} else {
|
||||
wrongAnswerCount++;
|
||||
//display bad cube click and update allCubes array
|
||||
updateAllCubesArrayWithClassListOfClickedCube(false, clickedCube);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkMazeAnswer();
|
||||
}
|
||||
|
||||
function checkMazeAnswer() {
|
||||
// check if wrong answers exceeded / game ended - both with same condition
|
||||
// if yes, end the game, clear counter and display correct answers and then stop game
|
||||
// else, end the game within few seconds
|
||||
if(wrongAnswerCount === numberOfWrongClicksAllowed) {
|
||||
clearInterval(counter);
|
||||
|
||||
displayCubeNumbers = true;
|
||||
|
||||
allCubes = allCubes.map((cube) => {
|
||||
cube.classList = goodPositions.includes(cube.cubeIndex.toLocaleString()) ? 'ps-bg-green-cube' : '';
|
||||
return cube;
|
||||
});
|
||||
allCubes = allCubes;
|
||||
|
||||
setTimeout(() => {
|
||||
gameEnded = true;
|
||||
if(!isDevMode) {
|
||||
fetchNui('minigame:callback', false);
|
||||
dispatch('game-ended', { hackSuccess: false });
|
||||
}
|
||||
dispatch('closeUI', {hackSuccess: false});
|
||||
}, 3000);
|
||||
|
||||
return;
|
||||
} else if(lastPos === 48){
|
||||
clearInterval(counter);
|
||||
|
||||
displayCubeNumbers = true;
|
||||
|
||||
setTimeout(() => {
|
||||
gameEnded = true;
|
||||
if(!isDevMode) {
|
||||
fetchNui('minigame:callback', true);
|
||||
dispatch('game-ended', { hackSuccess: true });
|
||||
}
|
||||
dispatch('closeUI', {hackSuccess: true});
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyEvent(event) {
|
||||
let key_pressed = event.key;
|
||||
let valid_keys = ['Escape'];
|
||||
|
||||
if(gameStarted && valid_keys.includes(key_pressed) && !gameEnded) {
|
||||
switch(key_pressed){
|
||||
case 'Escape':
|
||||
closeGame(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown|preventDefault={handleKeyEvent} />
|
||||
<div class="maze-game-base">
|
||||
<div class="time-left">
|
||||
<i class="fa-solid fa-clock ps-text-lightgrey clock-icon"></i>
|
||||
<p class="{gameTimeRemaining !== 0 ? 'game-timer-var' : 'mr-1'}">{gameTimeRemaining} </p> time remaining
|
||||
</div>
|
||||
<div id="maze-game-container" class="maze-game-container">
|
||||
{#each allCubes as cube}
|
||||
<div
|
||||
id={'each-cube-'+cube.cubeIndex} on:click={() => handleCubeClick(cube)}
|
||||
class="each-cube {cube.classList}
|
||||
{[0, numberOfCubes - 1].includes(cube.cubeIndex) ? 'start-dest-cube' : ''}
|
||||
{!stopBlinking && [blinkingIndex, blinkingIndex * 7].includes(cube.cubeIndex) ? 'blinking-cube' : ''}
|
||||
"
|
||||
>
|
||||
{#if cube.cubeIndex === 0}
|
||||
<i class="fa-solid fa-ethernet"></i>
|
||||
{:else if cube.cubeIndex === numberOfCubes - 1}
|
||||
<i class="fa-solid fa-network-wired"></i>
|
||||
{:else}
|
||||
{#if !stopBlinking || displayCubeNumbers}
|
||||
<p>{ cube.cubeValue }</p>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.maze-game-base {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
height: 28vw;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--color-lightgrey);
|
||||
}
|
||||
|
||||
.maze-game-base > .time-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
font-size: 0.85vw;
|
||||
}
|
||||
.maze-game-base > .time-left > .clock-icon {
|
||||
padding-top: 0.17vw;
|
||||
margin-right: 0.3vw;
|
||||
}
|
||||
.maze-game-base > .time-left > .game-timer-var {
|
||||
width: 2.5vw;
|
||||
}
|
||||
|
||||
.maze-game-base > .maze-game-container {
|
||||
/* border: 0.1px solid red; */
|
||||
|
||||
margin-top: 0.5vw;
|
||||
width: 30vw;
|
||||
height: 29vw;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.9vw;
|
||||
}
|
||||
.maze-game-base > .maze-game-container > .start-dest-cube {
|
||||
background-color: var(--color-green) !important;
|
||||
color: var(--color-darkblue);
|
||||
|
||||
font-size: 1.65vw;
|
||||
text-align: center;
|
||||
}
|
||||
.maze-game-base > .maze-game-container > .start-dest-cube > i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.maze-game-base > .maze-game-container > .each-cube {
|
||||
width: 3vw;
|
||||
height: 3vw;
|
||||
|
||||
background-color: var(--cube-bg-darkgreen);
|
||||
border: 2px solid var(--color-green);
|
||||
text-align: center;
|
||||
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.maze-game-base > .maze-game-container > .each-cube > p {
|
||||
font-size: 1.5vw;
|
||||
font-weight: bold;
|
||||
margin-top: 0.2vw
|
||||
}
|
||||
|
||||
.blinking-cube {
|
||||
animation: blink 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
0%,
|
||||
100% {
|
||||
background-color: var(--color-green);
|
||||
/* background-color: var(--cube-bg-darkgreen); */
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: var(--cube-bg-darkgreen);
|
||||
}
|
||||
}
|
||||
|
||||
.ps-bg-green-cube {
|
||||
background-color: var(--color-green) !important;
|
||||
}
|
||||
.ps-bg-wrong-cube {
|
||||
background-color: var(--color-red) !important;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
|
@ -0,0 +1,240 @@
|
|||
<script lang="ts">
|
||||
import { gameSettings, currentActiveGameDetails, closeGame } from "../stores/GameLauncherStore";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import fetchNui from "../../utils/fetch";
|
||||
import mojs from '@mojs/core';
|
||||
import { convertVwToPx, getRandomArbitrary, isDevMode } from "../stores/GeneralStores";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let gameTimeRemaining = 0;
|
||||
|
||||
let blocksInput = $gameSettings.amountOfAnswers; // how many cubes to remember for game - increment number based on difficulty level
|
||||
let gameTime = $gameSettings.gameTime * 100;
|
||||
let numberOfWrongClicksAllowed = $gameSettings.maxAnswersIncorrect;
|
||||
let displayNumbersOnCubesFor = $currentActiveGameDetails.timeForNumberDisplay * 100;
|
||||
|
||||
let counter, gameStarted = false, gameEnded = false;
|
||||
let allCubes = [];
|
||||
let order = 0, wrongClicks = 0;
|
||||
let cubeBgColors = ['var(--color-green)', 'var(--color-palegreen)', 'var(--color-blue)'];
|
||||
|
||||
// let topLowerBound = 290, topHigherBound = 660; px
|
||||
// let leftLowerBound = 77, leftHigherBound = 459;
|
||||
let topLowerBound = 18, topHigherBound = 40; //vw
|
||||
let leftLowerBound = 2, leftHigherBound = 28;
|
||||
|
||||
onMount(() => {
|
||||
//generate shuffled cubes indices
|
||||
let cubeIndicesList = [];
|
||||
while(cubeIndicesList.length < blocksInput){
|
||||
const r = Math.floor(Math.random() * blocksInput);
|
||||
if(cubeIndicesList.indexOf(r) === -1) cubeIndicesList.push(r);
|
||||
}
|
||||
|
||||
//generating an array to maintain each cube data by index
|
||||
for(let i = 0; i < cubeIndicesList.length; i++) {
|
||||
//height between 290 and 660 px
|
||||
//horizontal between 77 and 459 px
|
||||
const cubeData = {
|
||||
cubeIndex: cubeIndicesList[i],
|
||||
cubeValue: cubeIndicesList[i],
|
||||
bgColor: cubeBgColors[Math.floor(Math.random() * cubeBgColors.length)],
|
||||
top: getRandomArbitrary(topLowerBound, topHigherBound),
|
||||
left: getRandomArbitrary(leftLowerBound, leftHigherBound)
|
||||
};
|
||||
allCubes.push(cubeData);
|
||||
allCubes = allCubes;
|
||||
}
|
||||
|
||||
//stop showing the correct cubes and start the guessing game
|
||||
setTimeout(() => {
|
||||
gameStarted = true;
|
||||
|
||||
let eachCube = document.querySelectorAll('.each-cube');
|
||||
eachCube.forEach(el => { newPos(el) });
|
||||
|
||||
counter = setInterval(startTimer, 10);
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
function newPos(element) {
|
||||
let top = element.offsetTop;
|
||||
let left = element.offsetLeft;
|
||||
|
||||
let new_top_vw = getRandomArbitrary(topLowerBound,topHigherBound);
|
||||
let new_left_vw = getRandomArbitrary(leftLowerBound,leftHigherBound);
|
||||
|
||||
let new_top = convertVwToPx(new_top_vw);
|
||||
let new_left = convertVwToPx(new_left_vw);
|
||||
|
||||
let diff_top = new_top - top;
|
||||
let diff_left = new_left - left;
|
||||
|
||||
let duration = getRandomArbitrary(10,40)*100;
|
||||
|
||||
new mojs.Html({
|
||||
el: '#'+element.id,
|
||||
x: {
|
||||
0:diff_left,
|
||||
duration: duration,
|
||||
easing: 'linear.none'
|
||||
},
|
||||
y: {
|
||||
0:diff_top,
|
||||
duration: duration,
|
||||
easing: 'linear.none'
|
||||
},
|
||||
duration: duration+50,
|
||||
onComplete() {
|
||||
if(element.offsetTop === 0 && element.offsetLeft === 0) {
|
||||
this.pause();
|
||||
return;
|
||||
}
|
||||
const bgColor = element.style.backgroundColor;
|
||||
element.style = 'background-color: '+bgColor+'; top: '+new_top_vw+'vw; left: '+new_left_vw+'vw; transform: none;';
|
||||
newPos(element);
|
||||
},
|
||||
onUpdate() {
|
||||
if(gameStarted === false) this.pause();
|
||||
}
|
||||
}).play();
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
if (gameTime <= 0)
|
||||
{
|
||||
endGame(false);
|
||||
return;
|
||||
}
|
||||
displayNumbersOnCubesFor--;
|
||||
gameTime--;
|
||||
gameTimeRemaining = gameTime/100;
|
||||
}
|
||||
|
||||
function handleClick(clickedCube) {
|
||||
if(gameStarted && !gameEnded && displayNumbersOnCubesFor <= 0) {
|
||||
//correct click
|
||||
if(order === clickedCube.cubeIndex) {
|
||||
let clickedCubeDom = document.getElementById('each-cube-'+clickedCube.cubeIndex);
|
||||
clickedCubeDom.style.backgroundColor = 'var(--color-darkgrey)';
|
||||
order = order + 1;
|
||||
} else {
|
||||
wrongClicks = wrongClicks + 1;
|
||||
}
|
||||
checkGameStatus();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function checkGameStatus() {
|
||||
if(order === allCubes.length - 1 && wrongClicks < numberOfWrongClicksAllowed) {
|
||||
endGame(true);
|
||||
} else if(order < allCubes.length - 1 && wrongClicks >= numberOfWrongClicksAllowed) {
|
||||
endGame(false);
|
||||
}
|
||||
}
|
||||
|
||||
function endGame(isSuccess) {
|
||||
if(!gameEnded) {
|
||||
gameEnded = true;
|
||||
clearInterval(counter);
|
||||
|
||||
setTimeout(() => {
|
||||
if(!isDevMode) {
|
||||
fetchNui('minigame:callback', isSuccess);
|
||||
dispatch('game-ended', { hackSuccess: isSuccess });
|
||||
}
|
||||
dispatch('minigame:callback', {hackSuccess: isSuccess});
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyEvent(event) {
|
||||
let key_pressed = event.key;
|
||||
let valid_keys = ['Escape'];
|
||||
|
||||
if(gameStarted && valid_keys.includes(key_pressed) && !gameEnded) {
|
||||
switch(key_pressed){
|
||||
case 'Escape':
|
||||
closeGame(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown|preventDefault={handleKeyEvent} />
|
||||
<div class="var-game-base">
|
||||
<div class="time-left">
|
||||
<i class="fa-solid fa-clock ps-text-lightgrey clock-icon"></i>
|
||||
<p class="{gameTimeRemaining !== 0 ? 'game-timer-var' : 'mr-1'}">{gameTimeRemaining} </p> time remaining
|
||||
</div>
|
||||
|
||||
<div id="var-game-container" class="var-game-container">
|
||||
{#each allCubes as cube}
|
||||
<div
|
||||
id={'each-cube-'+cube.cubeIndex}
|
||||
class="each-cube"
|
||||
style="background-color:{cube.bgColor}; top: {cube.top}vw; left: {cube.left}vw;"
|
||||
on:click={() => handleClick(cube)}
|
||||
>
|
||||
{#if displayNumbersOnCubesFor > 0}
|
||||
<p>{cube.cubeValue + 1}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.var-game-base {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
height: 28vw;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--color-lightgrey);
|
||||
}
|
||||
|
||||
.var-game-base > .time-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
font-size: 0.85vw;
|
||||
}
|
||||
.var-game-base > .time-left > .clock-icon {
|
||||
padding-top: 0.17vw;
|
||||
margin-right: 0.3vw;
|
||||
}
|
||||
.var-game-base > .time-left > .game-timer-var {
|
||||
width: 2.5vw;
|
||||
}
|
||||
|
||||
.var-game-base > .var-game-container {
|
||||
border: 2px solid var(--color-green);
|
||||
background-color: var(--cube-bg-darkgreen);
|
||||
|
||||
margin-top: 1vw;
|
||||
width: 30vw;
|
||||
height: 28vw;
|
||||
}
|
||||
.var-game-base > .var-game-container > .each-cube {
|
||||
width: 3vw;
|
||||
height: 3vw;
|
||||
|
||||
border: 2px solid var(--color-lightgrey);
|
||||
position: absolute;
|
||||
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
}
|
||||
.var-game-base > .var-game-container > .each-cube > p {
|
||||
font-size: 1.5vw;
|
||||
font-weight: bold;
|
||||
margin-top: 0.2vw;
|
||||
color: var(--color-black);
|
||||
}
|
||||
</style>
|
321
resources/[freizeit]/[gym]/ps-ui/web/src/games/Scrambler.svelte
Normal file
321
resources/[freizeit]/[gym]/ps-ui/web/src/games/Scrambler.svelte
Normal file
|
@ -0,0 +1,321 @@
|
|||
<script lang="ts">
|
||||
import { closeGame, currentActiveGameDetails, gameSettings } from "../stores/GameLauncherStore";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import fetchNui from "../../utils/fetch";
|
||||
import { getRandomArbitrary, isDevMode } from "../stores/GeneralStores";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
const randomSetChar = () => {
|
||||
let str='?';
|
||||
switch($currentActiveGameDetails.sets) {
|
||||
case 'numeric':
|
||||
str="0123456789";
|
||||
break;
|
||||
case 'alphabet':
|
||||
str="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
break;
|
||||
case 'alphanumeric':
|
||||
str="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
break;
|
||||
case 'greek':
|
||||
str="ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ";
|
||||
break;
|
||||
case 'braille':
|
||||
str="⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿"+
|
||||
"⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿"+
|
||||
"⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿";
|
||||
break;
|
||||
case 'runes':
|
||||
str="ᚠᚥᚧᚨᚩᚬᚭᚻᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟᛤ";
|
||||
break;
|
||||
}
|
||||
return str.charAt(getRandomArbitrary(0, str.length));
|
||||
}
|
||||
|
||||
let gameTimeRemaining = 0;
|
||||
|
||||
let amountOfAnswers = $gameSettings.amountOfAnswers; // how many numbers for game
|
||||
let gameTime = $gameSettings.gameTime * 100; // seconds
|
||||
|
||||
let correctIndices = [], correctAnswers = [];
|
||||
let changeBoardAfter = $currentActiveGameDetails.changeBoardAfter * 100; //3 seconds
|
||||
let originalChangeBoardAfter = changeBoardAfter;
|
||||
let counter, gameStarted = false, gameEnded = false;
|
||||
let hackSuccess = false;
|
||||
let numberOfCubes = 80, allCubes = [];
|
||||
let totalNumberOfColumns = 10;
|
||||
|
||||
let cursorIndices = [], cursorStartIndex = 43;
|
||||
|
||||
onMount(() => {
|
||||
//generating an array to maintain each cube data by index
|
||||
for(let i = 0; i < numberOfCubes; i++) {
|
||||
const cubeData = {
|
||||
cubeIndex: i,
|
||||
cubeValue: randomSetChar() + randomSetChar(),
|
||||
};
|
||||
allCubes.push(cubeData);
|
||||
allCubes = allCubes;
|
||||
}
|
||||
|
||||
//generate correct answer values - column number - has to be Number of cols - 4 to have straight 4 answers
|
||||
const columnNumber = Math.floor(Math.random() * 5);
|
||||
//generate correct answer values - row number
|
||||
const rowNumber = Math.floor(Math.random() * 7);
|
||||
|
||||
const startIndex = rowNumber * totalNumberOfColumns + columnNumber;
|
||||
correctAnswers = [];
|
||||
for(let i = 0; i < amountOfAnswers; i++) {
|
||||
correctAnswers.push(allCubes[i + startIndex]);
|
||||
correctIndices.push(i+startIndex);
|
||||
}
|
||||
|
||||
getCursorIndices();
|
||||
|
||||
//start game
|
||||
setTimeout(() => {
|
||||
gameStarted = true;
|
||||
counter = setInterval(startTimer, 10);
|
||||
}, 1000);
|
||||
|
||||
});
|
||||
|
||||
function getCursorIndices() {
|
||||
cursorIndices = [cursorStartIndex];
|
||||
for(let i=1; i<4; i++){
|
||||
if( cursorStartIndex+i >= 80 ){
|
||||
cursorIndices.push( (cursorStartIndex+i) - 80 );
|
||||
}else{
|
||||
cursorIndices.push( cursorStartIndex+i );
|
||||
}
|
||||
}
|
||||
// return group;
|
||||
}
|
||||
|
||||
function endTheGame() {
|
||||
clearInterval(counter);
|
||||
gameEnded = true;
|
||||
|
||||
setTimeout(() => {
|
||||
if(!isDevMode) {
|
||||
fetchNui('minigame:callback', hackSuccess);
|
||||
dispatch('game-ended', { hackSuccess });
|
||||
}
|
||||
dispatch('minigame:callback', hackSuccess);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function startTimer() {
|
||||
if (gameTime <= 0)
|
||||
{
|
||||
hackSuccess = false;
|
||||
endTheGame();
|
||||
return;
|
||||
} else if (changeBoardAfter <= 0) {
|
||||
scrambleBoard();
|
||||
}
|
||||
|
||||
gameTime--;
|
||||
changeBoardAfter--;
|
||||
|
||||
gameTimeRemaining = gameTime/100;
|
||||
}
|
||||
|
||||
function scrambleBoard() {
|
||||
changeBoardAfter = originalChangeBoardAfter;
|
||||
|
||||
//generating an array to maintain each cube data by index
|
||||
let newCubeData = [];
|
||||
for(let i = 0; i < numberOfCubes; i++) {
|
||||
let cubeValue;
|
||||
|
||||
if(i === numberOfCubes - 1) {
|
||||
cubeValue = allCubes[0].cubeValue;
|
||||
} else {
|
||||
cubeValue = allCubes[i+1].cubeValue;
|
||||
}
|
||||
const cubeData = {
|
||||
cubeIndex: i,
|
||||
cubeValue: cubeValue,
|
||||
};
|
||||
newCubeData.push(cubeData);
|
||||
newCubeData = newCubeData;
|
||||
}
|
||||
getCursorIndices();
|
||||
|
||||
allCubes = newCubeData;
|
||||
}
|
||||
|
||||
function checkAnswer() {
|
||||
let selectedValues = cursorIndices.map((currentCursorIndex) => {
|
||||
return allCubes[currentCursorIndex];
|
||||
});
|
||||
|
||||
const selectedValuesData = selectedValues.map((item) => {
|
||||
return item.cubeValue;
|
||||
});
|
||||
|
||||
const correctAnswerValues = correctAnswers.map((item) => {
|
||||
return item.cubeValue;
|
||||
});
|
||||
|
||||
if(JSON.stringify(selectedValuesData) === JSON.stringify(correctAnswerValues)) {
|
||||
hackSuccess = true;
|
||||
} else {
|
||||
hackSuccess = false;
|
||||
}
|
||||
|
||||
endTheGame();
|
||||
}
|
||||
|
||||
function handleKeyEvent(event) {
|
||||
let key_pressed = event.key;
|
||||
let valid_keys = ['a','w','s','d', 'A','W','S','D' ,'ArrowUp','ArrowDown','ArrowRight','ArrowLeft','Enter', 'Escape'];
|
||||
|
||||
if(gameStarted && valid_keys.includes(key_pressed) && !gameEnded) {
|
||||
switch(key_pressed){
|
||||
case 'w':
|
||||
case 'ArrowUp':
|
||||
cursorStartIndex -= 10;
|
||||
if(cursorStartIndex < 0) {
|
||||
cursorStartIndex += 80;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
case 'ArrowDown':
|
||||
cursorStartIndex += 10;
|
||||
cursorStartIndex %= 80;
|
||||
break;
|
||||
case 'a':
|
||||
case 'ArrowLeft':
|
||||
cursorStartIndex--;
|
||||
if(cursorStartIndex < 0) {
|
||||
cursorStartIndex = 79;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
case 'ArrowRight':
|
||||
cursorStartIndex++;
|
||||
cursorStartIndex %= 80;
|
||||
break;
|
||||
case 'Enter':
|
||||
clearInterval(counter);
|
||||
checkAnswer();
|
||||
return;
|
||||
case 'Escape':
|
||||
closeGame(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if(cursorStartIndex) {
|
||||
getCursorIndices();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown|preventDefault={handleKeyEvent} />
|
||||
<div class="scrambler-game-base">
|
||||
<div class="game-info-container">
|
||||
<div class="scrambler-find-data">
|
||||
<p>Match the numbers underneath.</p>
|
||||
<div class="original-data-wrapper">
|
||||
{#each correctAnswers as value}
|
||||
<p class="original-digits">{value.cubeValue}</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-left">
|
||||
<i class="fa-solid fa-clock ps-text-lightgrey clock-icon"></i>
|
||||
<p class="{gameTimeRemaining !== 0 ? 'game-timer-var' : 'mr-1'}">{gameTimeRemaining} </p> time remaining
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="scrambler-game-container" class="scrambler-game-container">
|
||||
{#each allCubes as cube}
|
||||
<div id={'each-cube-'+cube.cubeIndex} class="each-cube">
|
||||
<p class="{!gameEnded && cursorIndices.includes(cube.cubeIndex) ? 'ps-text-red' : ''}">{cube.cubeValue}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.scrambler-game-base {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 28vw;
|
||||
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--color-lightgrey);
|
||||
}
|
||||
|
||||
.scrambler-game-base > .game-info-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.scrambler-game-base > .game-info-container > .scrambler-find-data {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.scrambler-game-base > .game-info-container > .scrambler-find-data > .original-data-wrapper {
|
||||
width: max-content;
|
||||
border: 2px solid var(--color-green);
|
||||
border-radius: 0.2vw;
|
||||
background-color: var(--cube-bg-darkgreen);
|
||||
color: var(--color-green);
|
||||
|
||||
margin: 0.5vw auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
.scrambler-game-base > .game-info-container > .scrambler-find-data > .original-data-wrapper > .original-digits {
|
||||
font-size: 1vw;
|
||||
padding: 0.3vw 0.5vw;
|
||||
}
|
||||
|
||||
.scrambler-game-base > .game-info-container > .time-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
font-size: 0.85vw;
|
||||
}
|
||||
.scrambler-game-base > .game-info-container > .time-left > .clock-icon {
|
||||
padding-top: 0.17vw;
|
||||
margin-right: 0.3vw;
|
||||
}
|
||||
.scrambler-game-base > .game-info-container > .time-left > .game-timer-var {
|
||||
width: 2.5vw;
|
||||
}
|
||||
|
||||
.scrambler-game-base > .scrambler-game-container {
|
||||
/* border: 0.1px solid red; */
|
||||
|
||||
margin-top: 1vw;
|
||||
width: 30vw;
|
||||
height: 24vw;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5vw;
|
||||
}
|
||||
.scrambler-game-base > .scrambler-game-container > .each-cube {
|
||||
width: 2.5vw;
|
||||
height: 2.5vw;
|
||||
|
||||
font-size: 1.75vw;
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,61 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte";
|
||||
import Skull from "../assets/svgs/Skull.svelte";
|
||||
import { closeUi } from "../stores/GameLauncherStore";
|
||||
|
||||
export let isSuccess = false;
|
||||
const skullColor: string = 'var(--color-green)';
|
||||
|
||||
onMount(() => {
|
||||
setTimeout(() => {
|
||||
closeUi();
|
||||
}, 2000);
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="result-container">
|
||||
<div class="result-wrapper ps-bg-darkblue">
|
||||
<div class="skull-logo">
|
||||
<Skull color={skullColor} />
|
||||
</div>
|
||||
{#if isSuccess}
|
||||
<h1 class="text-white text-3xl uppercase">
|
||||
Access granted
|
||||
</h1>
|
||||
{:else}
|
||||
<h1 class="text-white text-3xl uppercase">
|
||||
Access denied
|
||||
</h1>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.result-container {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
-webkit-transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
border-radius: 0.2vw;
|
||||
}
|
||||
|
||||
.result-container > .result-wrapper {
|
||||
width: 30vw;
|
||||
height: 15vw;
|
||||
border-radius: 0.3vw;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.result-container > .result-wrapper > .skull-logo {
|
||||
margin: 0 auto 1vw auto;
|
||||
width: 6vw;
|
||||
}
|
||||
</style>
|
86
resources/[freizeit]/[gym]/ps-ui/web/src/index.css
Normal file
86
resources/[freizeit]/[gym]/ps-ui/web/src/index.css
Normal file
|
@ -0,0 +1,86 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: roboto;
|
||||
src: url('./assets/fonts/Roboto/Roboto-Regular.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: hacker;
|
||||
src: url('./assets/fonts/Hacker-Technology-Font/Hacker.ttf');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: arcade;
|
||||
src: url('./assets/fonts/karmatic_arcade/ka1.ttf');
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.ps-text-roboto {
|
||||
font-family: 'roboto';
|
||||
}
|
||||
.ps-font-arcade {
|
||||
font-family: 'arcade';
|
||||
}
|
||||
.ps-font-hacker {
|
||||
font-family: 'hacker';
|
||||
}
|
||||
|
||||
.ps-text-green {
|
||||
color: var(--color-green) !important;
|
||||
}
|
||||
.ps-text-lightgrey {
|
||||
color: var(--color-lightgrey);
|
||||
}
|
||||
.ps-text-darkblue {
|
||||
color: var(--color-darkblue);
|
||||
}
|
||||
.ps-text-red {
|
||||
color: var(--color-red) !important;
|
||||
}
|
||||
|
||||
.ps-border-green {
|
||||
border: 1px solid var(--color-green);
|
||||
}
|
||||
.ps-outline-green {
|
||||
outline: 1px solid var(--color-green);
|
||||
}
|
||||
|
||||
.ps-bg-lightgrey {
|
||||
background-color: var(--color-lightgrey);
|
||||
}
|
||||
.ps-bg-darkblue {
|
||||
background-color: var(--color-darkblue);
|
||||
}
|
||||
.ps-bg-green {
|
||||
background-color: var(--color-green);
|
||||
}
|
||||
|
||||
.ps-bg-green-w-opacity {
|
||||
background-color: rgba(2, 241, 181, 0.2);
|
||||
}
|
||||
.ps-notification-success {
|
||||
background-color: #2ebd2e;
|
||||
color: black;
|
||||
border: 1px solid #158515;
|
||||
}
|
||||
.ps-notification-warning {
|
||||
background-color: #fbb433;
|
||||
border: 1px solid #cf8d15;
|
||||
color: black;
|
||||
}
|
||||
.ps-notification-error {
|
||||
background-color: #cd2222;
|
||||
border: 1px solid #8d0e0e;
|
||||
color: white;
|
||||
}
|
||||
.ps-notification-info {
|
||||
background-color: #33addf;
|
||||
border: 1px solid #1886b3;
|
||||
color: black;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export interface IDrawText {
|
||||
// title: string;
|
||||
icon: string;
|
||||
keys: string;
|
||||
color: string;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export interface IGameSettings {
|
||||
game: string;
|
||||
gameName: string;
|
||||
gameDescription: string;
|
||||
gameTime: number;
|
||||
amountOfAnswers: number;
|
||||
maxAnswersIncorrect: number;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface IImage {
|
||||
action?: string;
|
||||
show: boolean;
|
||||
url: string;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export interface IInput {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
placeholder?: string;
|
||||
type: string;
|
||||
}
|
15
resources/[freizeit]/[gym]/ps-ui/web/src/interfaces/IMenu.ts
Normal file
15
resources/[freizeit]/[gym]/ps-ui/web/src/interfaces/IMenu.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
export interface IMenu {
|
||||
header: string;
|
||||
text?: string;
|
||||
icon: string;
|
||||
color: string;
|
||||
callback: string;
|
||||
subMenu: Array<null|ISubMenu>;
|
||||
};
|
||||
|
||||
export interface ISubMenu {
|
||||
header: string;
|
||||
text?: string;
|
||||
icon: string;
|
||||
color: string;
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
import type { NotificationTypes } from './../enums/NotificationTypesEnum';
|
||||
|
||||
export interface INotification {
|
||||
text: string;
|
||||
type: NotificationTypes;
|
||||
icon: string;
|
||||
length: number;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
export interface IStatusBar {
|
||||
title: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
items: Array<IStatusBarItem>;
|
||||
}
|
||||
|
||||
export interface IStatusBarItem {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
9
resources/[freizeit]/[gym]/ps-ui/web/src/main.ts
Normal file
9
resources/[freizeit]/[gym]/ps-ui/web/src/main.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import App from './App.svelte'
|
||||
import './index.css'
|
||||
import '@fortawesome/fontawesome-free/css/all.css'
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById('app')
|
||||
})
|
||||
|
||||
export default app
|
|
@ -0,0 +1,25 @@
|
|||
import { writable } from 'svelte/store';
|
||||
import CircleGame from '../games/CircleGame.svelte';
|
||||
|
||||
const circleGameStore = writable<CircleGame | null>(null);
|
||||
|
||||
export function initializeCircleGame() {
|
||||
circleGameStore.update(component => {
|
||||
if (!component) {
|
||||
const newComponent = new CircleGame({
|
||||
target: document.body
|
||||
});
|
||||
return newComponent;
|
||||
}
|
||||
return component;
|
||||
});
|
||||
}
|
||||
|
||||
export function setupCircleGame(data: { circles?: number; time?: number }) {
|
||||
circleGameStore.update(component => {
|
||||
if (component) {
|
||||
component.setupCircleGame(data);
|
||||
}
|
||||
return component;
|
||||
});
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import { writable, type Writable } from 'svelte/store';
|
||||
import { showComponent, showUi } from './GeneralStores';
|
||||
import { UIComponentsEnum } from '../enums/UIComponentsEnum';
|
||||
import type { IDrawText } from '../interfaces/IDrawText';
|
||||
|
||||
export const drawTextStore: Writable<IDrawText> = writable({
|
||||
// title: '',
|
||||
icon: '',
|
||||
keys: '',
|
||||
color: ''
|
||||
});
|
||||
|
||||
export const hideDrawTextStore: Writable<any> = writable(false);
|
||||
|
||||
export function showDrawTextMenu(data: IDrawText) {
|
||||
showUi.set(true);
|
||||
showComponent.set(UIComponentsEnum.DrawText);
|
||||
|
||||
drawTextStore.set({
|
||||
// title: data.title,
|
||||
icon: data.icon || 'fa-solid fa-circle-info',
|
||||
keys: data.keys,
|
||||
color: data.color || 'var(--color-green)'
|
||||
});
|
||||
}
|
||||
|
||||
export function hideDrawTextMenu() {
|
||||
hideDrawTextStore.set(true);
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
import { UIComponentsEnum } from '../enums/UIComponentsEnum';
|
||||
import { GamesEnum } from '../enums/GamesEnum';
|
||||
import { ConnectingGameMessageEnum } from '../enums/GameConnectionMessages';
|
||||
import { hideUi, isDevMode, showComponent } from './GeneralStores';
|
||||
import type { IGameSettings } from './../interfaces/IGameSettings';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import fetchNui from './../../utils/fetch';
|
||||
|
||||
export const gameSettings: Writable<IGameSettings> = writable({
|
||||
game: '',
|
||||
gameName: '',
|
||||
gameDescription: '',
|
||||
amountOfAnswers: 0,
|
||||
gameTime: 0,
|
||||
maxAnswersIncorrect: 0,
|
||||
});
|
||||
export const currentGameActive: Writable<GamesEnum> | undefined = writable();
|
||||
export const currentActiveGameDetails: Writable<any> | undefined = writable();
|
||||
export const connectionText: Writable<ConnectingGameMessageEnum> = writable();
|
||||
export const showLoading: Writable<boolean> = writable(true);
|
||||
|
||||
export function setupGame(data): void {
|
||||
const settings = data;
|
||||
currentActiveGameDetails.set(settings);
|
||||
|
||||
switch (settings.game) {
|
||||
case GamesEnum.Memory: {
|
||||
showComponent.set(UIComponentsEnum.Game);
|
||||
|
||||
currentGameActive.set(GamesEnum.Memory);
|
||||
connectionText.set(ConnectingGameMessageEnum.Connecting);
|
||||
|
||||
gameSettings.set({
|
||||
game: GamesEnum.Memory,
|
||||
gameName: settings.gameName,
|
||||
gameDescription: settings.gameDescription,
|
||||
gameTime: settings.gameTime || 2,// 1000 = 10 seconds
|
||||
amountOfAnswers: settings.amountOfAnswers || 15,
|
||||
maxAnswersIncorrect: settings.maxAnswersIncorrect || 2
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case GamesEnum.Scrambler: {
|
||||
showComponent.set(UIComponentsEnum.Game);
|
||||
|
||||
currentGameActive.set(GamesEnum.Scrambler);
|
||||
connectionText.set(ConnectingGameMessageEnum.Connecting);
|
||||
|
||||
gameSettings.set({
|
||||
game: GamesEnum.Scrambler,
|
||||
gameName: settings.gameName,
|
||||
gameDescription: settings.gameDescription,
|
||||
gameTime: settings.gameTime || 2,// 1000 = 10 seconds
|
||||
amountOfAnswers: settings.amountOfAnswers || 4,
|
||||
maxAnswersIncorrect: settings.maxAnswersIncorrect || 0,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case GamesEnum.NumberMaze: {
|
||||
showComponent.set(UIComponentsEnum.Game);
|
||||
|
||||
currentGameActive.set(GamesEnum.NumberMaze);
|
||||
connectionText.set(ConnectingGameMessageEnum.Connecting);
|
||||
|
||||
gameSettings.set({
|
||||
game: GamesEnum.NumberMaze,
|
||||
gameName: settings.gameName,
|
||||
gameDescription: settings.gameDescription,
|
||||
gameTime: settings.gameTime || 2,// 1000 = 10 seconds
|
||||
amountOfAnswers: settings.amountOfAnswers || 4,
|
||||
maxAnswersIncorrect: settings.maxAnswersIncorrect || 0,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case GamesEnum.NumberPuzzle: {
|
||||
showComponent.set(UIComponentsEnum.Game);
|
||||
|
||||
currentGameActive.set(GamesEnum.NumberPuzzle);
|
||||
connectionText.set(ConnectingGameMessageEnum.Connecting);
|
||||
|
||||
gameSettings.set({
|
||||
game: GamesEnum.NumberPuzzle,
|
||||
gameName: settings.gameName,
|
||||
gameDescription: settings.gameDescription,
|
||||
gameTime: settings.gameTime || 2,// 1000 = 10 seconds
|
||||
amountOfAnswers: settings.amountOfAnswers || 4,
|
||||
maxAnswersIncorrect: settings.maxAnswersIncorrect || 0,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function closeGame(isSuccess: boolean): void {
|
||||
if(!isDevMode) {
|
||||
fetchNui('minigame:callback', isSuccess);
|
||||
}
|
||||
closeUi();
|
||||
}
|
||||
|
||||
export function closeUi() {
|
||||
hideUi();
|
||||
currentGameActive.set(null);
|
||||
currentActiveGameDetails.set(null);
|
||||
gameSettings.set({
|
||||
game: '',
|
||||
gameName: '',
|
||||
gameDescription: '',
|
||||
amountOfAnswers: 0,
|
||||
gameTime: 0,
|
||||
maxAnswersIncorrect: 0,
|
||||
});
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import type { UIComponentsEnum } from './../enums/UIComponentsEnum';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import { currentGameActive } from './GameLauncherStore';
|
||||
|
||||
export const showComponent: Writable<UIComponentsEnum | string> = writable();
|
||||
|
||||
export const showUi: Writable<boolean> = writable();
|
||||
export const isDevMode = false;
|
||||
|
||||
export function hideUi(): void {
|
||||
showUi.set(false);
|
||||
showComponent.set(undefined);
|
||||
currentGameActive.set(undefined);
|
||||
}
|
||||
|
||||
export function getRandomArbitrary(min, max) {
|
||||
return Math.floor(Math.random() * (max - min) + min);
|
||||
}
|
||||
|
||||
export function convertVwToPx(vw) {
|
||||
return (document.documentElement.clientWidth * vw) / 100;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import type { IImage } from './../interfaces/IImage';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import { showComponent, showUi } from './GeneralStores';
|
||||
import { UIComponentsEnum } from './../enums/UIComponentsEnum';
|
||||
|
||||
export const imageStore: Writable<IImage> = writable({ show: false, url: '' });
|
||||
|
||||
export function showImage(event: any) {
|
||||
showUi.set(true);
|
||||
showComponent.set(UIComponentsEnum.Image);
|
||||
imageStore.set({
|
||||
show: event.show,
|
||||
url: event.url,
|
||||
});
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import { writable, type Writable } from 'svelte/store';
|
||||
import type { IInput } from './../interfaces/IInput';
|
||||
import { showComponent, showUi } from './GeneralStores';
|
||||
import { UIComponentsEnum } from '../enums/UIComponentsEnum';
|
||||
|
||||
export const inputStore: Writable<Array<IInput>> = writable([
|
||||
{
|
||||
id: '1',
|
||||
label: 'Label',
|
||||
icon: 'fa-solid fa-user',
|
||||
placeholder: 'Insert name',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
label: 'Label',
|
||||
icon: 'fa-solid fa-user',
|
||||
placeholder: 'Placeholder',
|
||||
type: 'password',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
label: 'Label',
|
||||
icon: 'fa-solid fa-user',
|
||||
placeholder: 'Placeholder',
|
||||
type: 'phone',
|
||||
},
|
||||
]);
|
||||
|
||||
/** Show the input component */
|
||||
export function showInput(data: Array<IInput>): void {
|
||||
showUi.set(true);
|
||||
showComponent.set(UIComponentsEnum.Input);
|
||||
|
||||
inputStore.set([...data]);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import type { IMenu } from './../interfaces/IMenu';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import { hideUi, showComponent, showUi } from './GeneralStores';
|
||||
import { UIComponentsEnum } from '..//enums/UIComponentsEnum';
|
||||
import fetchNui from '../../utils/fetch';
|
||||
|
||||
export const menuStore: Writable<Array<IMenu>> = writable([]);
|
||||
|
||||
export const menuIdShown: Writable<string> = writable();
|
||||
|
||||
export function setupInteractionMenu(data: Array<IMenu>): void {
|
||||
showUi.set(true);
|
||||
showComponent.set(UIComponentsEnum.Menu);
|
||||
menuStore.set(data.menuData);
|
||||
}
|
||||
|
||||
export function closeInteractionMenu(): void {
|
||||
showUi.set(false);
|
||||
showComponent.set(null);
|
||||
menuStore.set([
|
||||
{
|
||||
header: '',
|
||||
text: '',
|
||||
icon: '',
|
||||
color: '',
|
||||
callback: '',
|
||||
subMenu: null,
|
||||
}
|
||||
]);
|
||||
fetchNui('menuClose');
|
||||
hideUi();
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import type { INotification } from 'src/interfaces/INotification';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import { showComponent } from './GeneralStores';
|
||||
import { UIComponentsEnum } from './../enums/UIComponentsEnum';
|
||||
|
||||
export const notifications: Writable<Array<INotification>> = writable([]);
|
||||
|
||||
export function addNotification(newNotification: INotification): void {
|
||||
showComponent.set(UIComponentsEnum.Notification);
|
||||
notifications.update((currentNotifications) => {
|
||||
const updatedNotifications = [...currentNotifications, newNotification];
|
||||
return updatedNotifications;
|
||||
});
|
||||
|
||||
const unsubscribe = notifications.subscribe((data: Array<INotification>) => {
|
||||
data.forEach((notification: INotification) => {
|
||||
setTimeout(() => {
|
||||
removeNotification(notification);
|
||||
}, notification.length);
|
||||
});
|
||||
});
|
||||
unsubscribe();
|
||||
}
|
||||
|
||||
function removeNotification(notification: INotification): void {
|
||||
notifications.update((currentNotifications) => {
|
||||
const updatedNotifications = currentNotifications.filter((n) => n !== notification);
|
||||
return updatedNotifications;
|
||||
});
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import { writable, type Writable } from 'svelte/store';
|
||||
import type { IStatusBar } from '../interfaces/IStatusBar';
|
||||
import { showComponent, showUi } from './GeneralStores';
|
||||
import { UIComponentsEnum } from '../enums/UIComponentsEnum';
|
||||
|
||||
export const statusBarStore: Writable<IStatusBar> = writable({
|
||||
title: '',
|
||||
description: '',
|
||||
items: [],
|
||||
icon: '',
|
||||
});
|
||||
|
||||
export const hideStatusBarStore: Writable<any> = writable(false);
|
||||
|
||||
export function showStatusBar(data: IStatusBar) {
|
||||
showUi.set(true);
|
||||
showComponent.set(UIComponentsEnum.StatusBar);
|
||||
|
||||
statusBarStore.set({
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
items: data.items,
|
||||
icon: data.icon || 'fa-solid fa-circle-info',
|
||||
});
|
||||
}
|
||||
|
||||
export function hideStatusBar() {
|
||||
hideStatusBarStore.set(true);
|
||||
}
|
2
resources/[freizeit]/[gym]/ps-ui/web/src/vite-env.d.ts
vendored
Normal file
2
resources/[freizeit]/[gym]/ps-ui/web/src/vite-env.d.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
Loading…
Add table
Add a link
Reference in a new issue