🚧 Changed GameState to make it dynamic with svelte components and cleaned code
This commit is contained in:
@@ -5,10 +5,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { QuizData } from '$lib/quiz_types';
|
|
||||||
import { getLocalization } from '$lib/i18n';
|
import { getLocalization } from '$lib/i18n';
|
||||||
import { get_question_title } from '$lib/admin.ts';
|
|
||||||
import type { PlayerAnswer } from '$lib/admin.ts';
|
|
||||||
import { socket } from './socket';
|
import { socket } from './socket';
|
||||||
import { QuizQuestionType } from '$lib/quiz_types';
|
import { QuizQuestionType } from '$lib/quiz_types';
|
||||||
import Spinner from '$lib/Spinner.svelte';
|
import Spinner from '$lib/Spinner.svelte';
|
||||||
@@ -20,31 +17,18 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
const { t } = getLocalization();
|
const { t } = getLocalization();
|
||||||
const default_colors = ['#D6EDC9', '#B07156', '#7F7057', '#4E6E58'];
|
const default_colors = ['#D6EDC9', '#B07156', '#7F7057', '#4E6E58'];
|
||||||
|
|
||||||
let question_results = $state(null);
|
|
||||||
let selected_question = $state(-1);
|
|
||||||
let timer_res: string = $state();
|
|
||||||
let shown_question_now: number = $state();
|
|
||||||
let final_results_clicked = $state(false);
|
let final_results_clicked = $state(false);
|
||||||
let timer_interval: NodeJS.Timeout;
|
let timer_interval: NodeJS.Timeout;
|
||||||
let answer_count = $state(0);
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
game_token: string;
|
game_token: string;
|
||||||
quiz_data: QuizData;
|
|
||||||
bg_color: string;
|
bg_color: string;
|
||||||
final_results?: Array<null> | Array<Array<PlayerAnswer>>;
|
|
||||||
control_visible: boolean;
|
|
||||||
player_scores: any;
|
|
||||||
game_state: GameState;
|
game_state: GameState;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
game_token,
|
game_token,
|
||||||
quiz_data = $bindable(),
|
|
||||||
bg_color,
|
bg_color,
|
||||||
final_results = $bindable([null]),
|
|
||||||
control_visible,
|
|
||||||
player_scores = $bindable(),
|
|
||||||
game_state = $bindable()
|
game_state = $bindable()
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
@@ -52,70 +36,51 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
console.log('get_question_results');
|
console.log('get_question_results');
|
||||||
});
|
});
|
||||||
socket.on('set_question_number', (data) => {
|
socket.on('set_question_number', (data) => {
|
||||||
timer_res = '0';
|
|
||||||
clearInterval(timer_interval);
|
|
||||||
question_results = null;
|
|
||||||
shown_question_now = data.question_index;
|
|
||||||
timer_res = quiz_data.questions[data.question_index].time;
|
|
||||||
selected_question = selected_question + 1;
|
|
||||||
answer_count = 0;
|
|
||||||
timer(timer_res);
|
|
||||||
|
|
||||||
game_state.timer_res = '0';
|
game_state.timer_res = '0';
|
||||||
game_state.question_results = null;
|
game_state.question_results = null;
|
||||||
game_state.shown_question_now = data.question_index;
|
game_state.shown_question_now = data.question_index;
|
||||||
game_state.timer_res = quiz_data.questions[data.question_index].time;
|
game_state.timer_res = game_state.quiz_data.questions[data.question_index].time;
|
||||||
game_state.selected_question = game_state.selected_question + 1;
|
game_state.selected_question = game_state.selected_question + 1;
|
||||||
game_state.answer_count = 0;
|
game_state.answer_count = 0;
|
||||||
|
|
||||||
|
clearInterval(timer_interval);
|
||||||
|
timer(game_state.timer_res);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('solutions', (_) => {
|
socket.on('solutions', (_) => {
|
||||||
timer_res = '0';
|
|
||||||
clearInterval(timer_interval);
|
|
||||||
|
|
||||||
game_state.timer_res = '0';
|
game_state.timer_res = '0';
|
||||||
|
clearInterval(timer_interval);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('final_results', (data) => {
|
socket.on('final_results', (data) => {
|
||||||
// data = JSON.parse(data);
|
|
||||||
final_results_clicked = true;
|
final_results_clicked = true;
|
||||||
timer_res = '0';
|
|
||||||
final_results = data;
|
|
||||||
|
|
||||||
game_state.final_results = data;
|
|
||||||
game_state.timer_res = '0';
|
game_state.timer_res = '0';
|
||||||
|
game_state.final_results = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('everyone_answered', (_) => {
|
socket.on('everyone_answered', (_) => {
|
||||||
timer_res = '0';
|
|
||||||
game_state.timer_res = '0';
|
game_state.timer_res = '0';
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('question_results', (data) => {
|
socket.on('question_results', (data) => {
|
||||||
console.log('question_results:', data);
|
|
||||||
question_results = data;
|
|
||||||
timer_res = '0';
|
|
||||||
|
|
||||||
game_state.question_results = data;
|
game_state.question_results = data;
|
||||||
game_state.timer_res = '0';
|
game_state.timer_res = '0';
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('player_answer', (_) => {
|
socket.on('player_answer', (_) => {
|
||||||
answer_count += 1;
|
|
||||||
game_state.answer_count += 1;
|
game_state.answer_count += 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
const timer = (time: string) => {
|
const timer = (time: string) => {
|
||||||
let seconds = Number(time);
|
let seconds = Number(time);
|
||||||
timer_interval = setInterval(() => {
|
timer_interval = setInterval(() => {
|
||||||
if (timer_res === '0') {
|
if (game_state.timer_res === '0') {
|
||||||
clearInterval(timer_interval);
|
clearInterval(timer_interval);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
seconds--;
|
seconds--;
|
||||||
}
|
}
|
||||||
|
|
||||||
timer_res = seconds.toString();
|
|
||||||
game_state.timer_res = seconds.toString();
|
game_state.timer_res = seconds.toString();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
};
|
};
|
||||||
@@ -123,57 +88,51 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
const socket_game_controls: SocketGameControls = new SocketGameControls(socket);
|
const socket_game_controls: SocketGameControls = new SocketGameControls(socket);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if control_visible}
|
{#if game_state.control_visible}
|
||||||
<Controls
|
<Controls
|
||||||
{bg_color}
|
{bg_color}
|
||||||
{selected_question}
|
|
||||||
{quiz_data}
|
|
||||||
bind:timer_res
|
|
||||||
{final_results}
|
|
||||||
{socket_game_controls}
|
{socket_game_controls}
|
||||||
{question_results}
|
|
||||||
{game_token}
|
{game_token}
|
||||||
{shown_question_now}
|
|
||||||
bind:game_state
|
bind:game_state
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{#if timer_res !== '0' && selected_question >= 0}
|
{#if game_state.timer_res !== '0' && game_state.selected_question >= 0}
|
||||||
<span
|
<span
|
||||||
class="fixed top-0 bg-red-500 h-8 transition-all"
|
class="fixed top-0 bg-red-500 h-8 transition-all"
|
||||||
class:mt-10={control_visible}
|
class:mt-10={game_state.control_visible}
|
||||||
style="width: {(100 / parseInt(quiz_data.questions[selected_question].time)) *
|
style="width: {(100 / parseInt(game_state.quiz_data.questions[game_state.selected_question].time)) *
|
||||||
parseInt(timer_res)}vw"
|
parseInt(game_state.timer_res)}vw"
|
||||||
></span>
|
></span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="w-full h-full" class:pt-28={control_visible} class:pt-12={!control_visible}>
|
<div class="w-full h-full" class:pt-28={game_state.control_visible} class:pt-12={!game_state.control_visible}>
|
||||||
{#if timer_res !== undefined && !final_results_clicked && !question_results}
|
{#if game_state.timer_res !== undefined && !final_results_clicked && !game_state.question_results}
|
||||||
<!-- Question is shown -->
|
<!-- Question is shown -->
|
||||||
{#if quiz_data.questions[selected_question].type === QuizQuestionType.SLIDE}
|
{#if game_state.quiz_data.questions[game_state.selected_question].type === QuizQuestionType.SLIDE}
|
||||||
{#await import('$lib/play/admin/slide.svelte')}
|
{#await import('$lib/play/admin/slide.svelte')}
|
||||||
<Spinner my_20={false} />
|
<Spinner my_20={false} />
|
||||||
{:then c}
|
{:then c}
|
||||||
<c.default question={quiz_data.questions[selected_question]} />
|
<c.default question={game_state.quiz_data.questions[game_state.selected_question]} />
|
||||||
{/await}
|
{/await}
|
||||||
{:else}
|
{:else}
|
||||||
<Question {quiz_data} {selected_question} {timer_res} {answer_count} {default_colors} />
|
<Question quiz_data={game_state.quiz_data} selected_question={game_state.selected_question} timer_res={game_state.timer_res} answer_count={game_state.answer_count} default_colors={default_colors} />
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
<br />
|
<br />
|
||||||
{#if timer_res === '0' && JSON.stringify(final_results) === JSON.stringify( [null] ) && quiz_data.questions[selected_question].type !== QuizQuestionType.SLIDE && question_results !== null && quiz_data.questions[selected_question]?.hide_results !== true}
|
{#if game_state.timer_res === '0' && JSON.stringify(game_state.final_results) === JSON.stringify( [null] ) && game_state.quiz_data.questions[game_state.selected_question].type !== QuizQuestionType.SLIDE && game_state.question_results !== null && game_state.quiz_data.questions[game_state.selected_question]?.hide_results !== true}
|
||||||
{#if question_results === undefined}
|
{#if game_state.question_results === undefined}
|
||||||
{#if !final_results_clicked}
|
{#if !final_results_clicked}
|
||||||
<div class="w-full flex justify-center">
|
<div class="w-full flex justify-center">
|
||||||
<h1 class="text-3xl">{$t('admin_page.no_answers')}</h1>
|
<h1 class="text-3xl">{$t('admin_page.no_answers')}</h1>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if quiz_data.questions[selected_question].type === QuizQuestionType.VOTING}
|
{:else if game_state.quiz_data.questions[game_state.selected_question].type === QuizQuestionType.VOTING}
|
||||||
{#await import('$lib/play/admin/voting_results.svelte')}
|
{#await import('$lib/play/admin/voting_results.svelte')}
|
||||||
<Spinner />
|
<Spinner />
|
||||||
{:then c}
|
{:then c}
|
||||||
<c.default
|
<c.default
|
||||||
data={question_results}
|
data={game_state.question_results}
|
||||||
question={quiz_data.questions[selected_question]}
|
question={game_state.quiz_data.questions[game_state.selected_question]}
|
||||||
/>
|
/>
|
||||||
{/await}
|
{/await}
|
||||||
{:else}
|
{:else}
|
||||||
@@ -181,24 +140,24 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<Spinner />
|
<Spinner />
|
||||||
{:then c}
|
{:then c}
|
||||||
<c.default
|
<c.default
|
||||||
bind:data={player_scores}
|
bind:data={game_state.player_scores}
|
||||||
question={quiz_data.questions[selected_question]}
|
question={game_state.quiz_data.questions[game_state.selected_question]}
|
||||||
new_data={question_results}
|
new_data={game_state.question_results}
|
||||||
/>
|
/>
|
||||||
{/await}
|
{/await}
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
<br />
|
<br />
|
||||||
{#if selected_question === -1}
|
{#if game_state.selected_question === -1}
|
||||||
<div class="flex flex-col justify-center w-screen h-full">
|
<div class="flex flex-col justify-center w-screen h-full">
|
||||||
<h1 class="text-7xl text-center">{@html quiz_data.title}</h1>
|
<h1 class="text-7xl text-center">{@html game_state.quiz_data.title}</h1>
|
||||||
<p class="text-3xl pt-8 text-center">{@html quiz_data.description}</p>
|
<p class="text-3xl pt-8 text-center">{@html game_state.quiz_data.description}</p>
|
||||||
{#if quiz_data.cover_image}
|
{#if game_state.quiz_data.cover_image}
|
||||||
<div class="flex justify-center align-middle items-center">
|
<div class="flex justify-center align-middle items-center">
|
||||||
<div class="h-[30vh] m-auto w-auto mt-12">
|
<div class="h-[30vh] m-auto w-auto mt-12">
|
||||||
<img
|
<img
|
||||||
class="max-h-full max-w-full block"
|
class="max-h-full max-w-full block"
|
||||||
src="/api/v1/storage/download/{quiz_data.cover_image}"
|
src="/api/v1/storage/download/{game_state.quiz_data.cover_image}"
|
||||||
alt="Not provided"
|
alt="Not provided"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,35 +6,21 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { QuizQuestionType } from '$lib/quiz_types';
|
import { QuizQuestionType } from '$lib/quiz_types';
|
||||||
import type { QuizData } from '$lib/quiz_types';
|
|
||||||
import type { Socket } from 'socket.io-client';
|
|
||||||
import { getLocalization } from '$lib/i18n';
|
import { getLocalization } from '$lib/i18n';
|
||||||
import { SocketGameControls } from '$lib/play/admin/socket_game_controls.ts';
|
import { SocketGameControls } from '$lib/play/admin/socket_game_controls.ts';
|
||||||
import { GameState } from '$lib/play/admin/game_state.ts';
|
import { GameState } from '$lib/play/admin/game_state.ts';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
bg_color: string;
|
bg_color: string;
|
||||||
selected_question: number;
|
|
||||||
quiz_data: QuizData;
|
|
||||||
timer_res: string;
|
|
||||||
final_results: any;
|
|
||||||
socket_game_controls: SocketGameControls;
|
socket_game_controls: SocketGameControls;
|
||||||
game_token: string;
|
game_token: string;
|
||||||
question_results: any;
|
|
||||||
shown_question_now: number;
|
|
||||||
game_state: GameState;
|
game_state: GameState;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
bg_color,
|
bg_color,
|
||||||
selected_question,
|
|
||||||
quiz_data,
|
|
||||||
timer_res = $bindable(),
|
|
||||||
final_results,
|
|
||||||
socket_game_controls,
|
socket_game_controls,
|
||||||
game_token,
|
game_token,
|
||||||
question_results,
|
|
||||||
shown_question_now,
|
|
||||||
game_state = $bindable()
|
game_state = $bindable()
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
@@ -42,7 +28,6 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
|
|
||||||
const show_solutions = () => {
|
const show_solutions = () => {
|
||||||
socket_game_controls.show_solutions();
|
socket_game_controls.show_solutions();
|
||||||
timer_res = '0';
|
|
||||||
game_state.timer_res = '0';
|
game_state.timer_res = '0';
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -53,60 +38,60 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
class:text-black={bg_color}
|
class:text-black={bg_color}
|
||||||
>
|
>
|
||||||
<p class="mr-auto ml-0 col-start-1 col-end-1">
|
<p class="mr-auto ml-0 col-start-1 col-end-1">
|
||||||
{selected_question === -1 ? '0' : selected_question + 1}
|
{game_state.selected_question === -1 ? '0' : game_state.selected_question + 1}
|
||||||
/{quiz_data.questions.length}
|
/{game_state.quiz_data.questions.length}
|
||||||
</p>
|
</p>
|
||||||
<div class="justify-self-end ml-auto mr-0 col-start-3 col-end-3">
|
<div class="justify-self-end ml-auto mr-0 col-start-3 col-end-3">
|
||||||
{#if selected_question + 1 === quiz_data.questions.length && ((timer_res === '0' && question_results !== null) || quiz_data?.questions?.[selected_question]?.type === QuizQuestionType.SLIDE)}
|
{#if game_state.selected_question + 1 === game_state.quiz_data.questions.length && ((game_state.timer_res === '0' && game_state.question_results !== null) || game_state.quiz_data?.questions?.[game_state.selected_question]?.type === QuizQuestionType.SLIDE)}
|
||||||
{#if JSON.stringify(final_results) === JSON.stringify([null])}
|
{#if JSON.stringify(game_state.final_results) === JSON.stringify([null])}
|
||||||
<button onclick={() => socket_game_controls.get_final_results()} class="admin-button"
|
<button onclick={() => socket_game_controls.get_final_results()} class="admin-button"
|
||||||
>{$t('admin_page.get_final_results')}
|
>{$t('admin_page.get_final_results')}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if timer_res === '0' || selected_question === -1}
|
{:else if game_state.timer_res === '0' || game_state.selected_question === -1}
|
||||||
{#if (selected_question + 1 !== quiz_data.questions.length && question_results !== null) || selected_question === -1}
|
{#if (game_state.selected_question + 1 !== game_state.quiz_data.questions.length && game_state.question_results !== null) || game_state.selected_question === -1}
|
||||||
<button
|
<button
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
socket_game_controls.set_question_number(selected_question + 1);
|
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
||||||
}}
|
}}
|
||||||
class="admin-button"
|
class="admin-button"
|
||||||
>{$t('admin_page.next_question', { question: selected_question + 2 })}
|
>{$t('admin_page.next_question', { question: game_state.selected_question + 2 })}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{#if question_results === null && selected_question !== -1}
|
{#if game_state.question_results === null && game_state.selected_question !== -1}
|
||||||
{#if quiz_data.questions[selected_question].type === QuizQuestionType.SLIDE}
|
{#if game_state.quiz_data.questions[game_state.selected_question].type === QuizQuestionType.SLIDE}
|
||||||
<button
|
<button
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
socket_game_controls.set_question_number(selected_question + 1);
|
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
||||||
}}
|
}}
|
||||||
class="admin-button"
|
class="admin-button"
|
||||||
>{$t('admin_page.next_question', { question: selected_question + 2 })}
|
>{$t('admin_page.next_question', { question: game_state.selected_question + 2 })}
|
||||||
</button>
|
</button>
|
||||||
{:else if quiz_data.questions[selected_question]?.hide_results === true}
|
{:else if game_state.quiz_data.questions[game_state.selected_question]?.hide_results === true}
|
||||||
<button
|
<button
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
socket_game_controls.get_question_results(game_token, shown_question_now);
|
socket_game_controls.get_question_results(game_token, game_state.shown_question_now);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
socket_game_controls.set_question_number(selected_question + 1);
|
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
||||||
}, 200);
|
}, 200);
|
||||||
}}
|
}}
|
||||||
class="admin-button"
|
class="admin-button"
|
||||||
>{$t('admin_page.next_question', { question: selected_question + 2 })}
|
>{$t('admin_page.next_question', { question: game_state.selected_question + 2 })}
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
<button onclick={() => socket_game_controls.get_question_results(game_token, shown_question_now)} class="admin-button"
|
<button onclick={() => socket_game_controls.get_question_results(game_token, game_state.shown_question_now)} class="admin-button"
|
||||||
>{$t('admin_page.show_results')}
|
>{$t('admin_page.show_results')}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{:else if selected_question !== -1}
|
{:else if game_state.selected_question !== -1}
|
||||||
{#if quiz_data.questions[selected_question].type === QuizQuestionType.SLIDE}
|
{#if game_state.quiz_data.questions[game_state.selected_question].type === QuizQuestionType.SLIDE}
|
||||||
<button
|
<button
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
socket_game_controls.set_question_number(selected_question + 1);
|
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
||||||
}}
|
}}
|
||||||
class="admin-button"
|
class="admin-button"
|
||||||
>{$t('admin_page.next_question', { question: selected_question + 2 })}
|
>{$t('admin_page.next_question', { question: game_state.selected_question + 2 })}
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
<button onclick={show_solutions} class="admin-button"
|
<button onclick={show_solutions} class="admin-button"
|
||||||
|
|||||||
@@ -11,15 +11,16 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
import GrayButton from '$lib/components/buttons/gray.svelte';
|
import GrayButton from '$lib/components/buttons/gray.svelte';
|
||||||
import { fade } from 'svelte/transition';
|
import { fade } from 'svelte/transition';
|
||||||
import { SocketGameControls } from '$lib/play/admin/socket_game_controls.ts';
|
import { SocketGameControls } from '$lib/play/admin/socket_game_controls.ts';
|
||||||
|
import { GameState } from '$lib/play/admin/game_state';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
game_pin: string;
|
game_pin: string;
|
||||||
players: any;
|
game_state: GameState;
|
||||||
socket_game_controls: SocketGameControls;
|
socket_game_controls: SocketGameControls;
|
||||||
cqc_code: string;
|
cqc_code: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { game_pin, players = $bindable(), socket_game_controls, cqc_code = $bindable() }: Props = $props();
|
let { game_pin, game_state = $bindable(), socket_game_controls, cqc_code = $bindable() }: Props = $props();
|
||||||
|
|
||||||
let fullscreen_open = $state(false);
|
let fullscreen_open = $state(false);
|
||||||
const { t } = getLocalization();
|
const { t } = getLocalization();
|
||||||
@@ -55,13 +56,13 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<div class="m-auto">
|
<div class="m-auto">
|
||||||
<div class="flex justify-center my-4">
|
<div class="flex justify-center my-4">
|
||||||
<p class="m-auto text-2xl">
|
<p class="m-auto text-2xl">
|
||||||
{#if players.length <= 1}
|
{#if game_state.players.length <= 1}
|
||||||
{$t('play_page.players_waiting', {
|
{$t('play_page.players_waiting', {
|
||||||
count: players.length ?? 0
|
count: game_state.players.length ?? 0
|
||||||
})}
|
})}
|
||||||
{:else}
|
{:else}
|
||||||
{$t('play_page.players_waiting_plural', {
|
{$t('play_page.players_waiting_plural', {
|
||||||
count: players.length ?? 0
|
count: game_state.players.length ?? 0
|
||||||
})}
|
})}
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
</p>
|
||||||
@@ -74,13 +75,13 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
{:else}
|
{:else}
|
||||||
<div class="flex justify-center">
|
<div class="flex justify-center">
|
||||||
<p class="m-auto text-2xl">
|
<p class="m-auto text-2xl">
|
||||||
{#if players.length <= 1}
|
{#if game_state.players.length <= 1}
|
||||||
{$t('play_page.players_waiting', {
|
{$t('play_page.players_waiting', {
|
||||||
count: players.length ?? 0
|
count: game_state.players.length ?? 0
|
||||||
})}
|
})}
|
||||||
{:else}
|
{:else}
|
||||||
{$t('play_page.players_waiting_plural', {
|
{$t('play_page.players_waiting_plural', {
|
||||||
count: players.length ?? 0
|
count: game_state.players.length ?? 0
|
||||||
})}
|
})}
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
</p>
|
||||||
@@ -93,7 +94,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<div class="flex justify-center w-full mt-4">
|
<div class="flex justify-center w-full mt-4">
|
||||||
<div>
|
<div>
|
||||||
<GrayButton
|
<GrayButton
|
||||||
disabled={players.length < 1}
|
disabled={game_state.players.length < 1}
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
socket_game_controls.start_game()
|
socket_game_controls.start_game()
|
||||||
}}
|
}}
|
||||||
@@ -102,13 +103,13 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row w-full mt-4 px-10 flex-wrap">
|
<div class="flex flex-row w-full mt-4 px-10 flex-wrap">
|
||||||
{#if players.length > 0}
|
{#if game_state.players.length > 0}
|
||||||
{#each players as player}
|
{#each game_state.players as player}
|
||||||
<div class="p-2 m-2 border-2 border-[#B07156] rounded-sm hover:cursor-pointer">
|
<div class="p-2 m-2 border-2 border-[#B07156] rounded-sm hover:cursor-pointer">
|
||||||
<span
|
<span
|
||||||
class="hover:line-through text-lg"
|
class="hover:line-through text-lg"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
socket_game_controls.kick_player(player.username, players);
|
socket_game_controls.kick_player(player.username, game_state.players);
|
||||||
}}>{player.username}</span
|
}}>{player.username}</span
|
||||||
>
|
>
|
||||||
<!-- <button>{$t('words.kick')}</button>-->
|
<!-- <button>{$t('words.kick')}</button>-->
|
||||||
|
|||||||
@@ -1,23 +1,19 @@
|
|||||||
export class GameState {
|
import type { Player, PlayerAnswer } from '$lib/admin.ts';
|
||||||
public game_id: string;
|
import { QuizData } from '$lib/quiz_types';
|
||||||
public players: any[];
|
|
||||||
public selected_question: number;
|
|
||||||
public timer_res: string;
|
|
||||||
public question_results: any;
|
|
||||||
public answer_count: number;
|
|
||||||
public shown_question_now: number;
|
|
||||||
public final_results: any;
|
|
||||||
public game_started: boolean;
|
|
||||||
|
|
||||||
constructor(game_id: string, players: any[]) {
|
export interface IGameState {
|
||||||
this.game_id = game_id;
|
game_id: string;
|
||||||
this.players = players;
|
players: Player[];
|
||||||
this.selected_question = -1;
|
player_scores: Record<string, number>;
|
||||||
this.timer_res = '0';
|
selected_question: number;
|
||||||
this.question_results = null;
|
timer_res: string;
|
||||||
this.answer_count = 0;
|
question_results: any;
|
||||||
this.shown_question_now = -1;
|
answer_count: number;
|
||||||
this.final_results = null;
|
shown_question_now: number;
|
||||||
this.game_started = false;
|
final_results: Array<null> | Array<Array<PlayerAnswer>>;
|
||||||
}
|
game_started: boolean;
|
||||||
|
quiz_data: QuizData;
|
||||||
|
control_visible: boolean;
|
||||||
|
|
||||||
|
constructor(game_id: string);
|
||||||
}
|
}
|
||||||
@@ -5,12 +5,9 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { QuizData } from '$lib/quiz_types';
|
|
||||||
|
|
||||||
import { socket } from '$lib/socket';
|
import { socket } from '$lib/socket';
|
||||||
import { getLocalization } from '$lib/i18n';
|
import { getLocalization } from '$lib/i18n';
|
||||||
import { navbarVisible } from '$lib/stores.svelte.ts';
|
import { navbarVisible } from '$lib/stores.svelte.ts';
|
||||||
import type { PlayerAnswer, Player } from '$lib/admin.ts';
|
|
||||||
import SomeAdminScreen from '$lib/admin.svelte';
|
import SomeAdminScreen from '$lib/admin.svelte';
|
||||||
import GameNotStarted from '$lib/play/admin/game_not_started.svelte';
|
import GameNotStarted from '$lib/play/admin/game_not_started.svelte';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
@@ -19,7 +16,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
import GrayButton from '$lib/components/buttons/gray.svelte';
|
import GrayButton from '$lib/components/buttons/gray.svelte';
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
import { SocketGameControls } from '$lib/play/admin/socket_game_controls.ts';
|
import { SocketGameControls } from '$lib/play/admin/socket_game_controls.ts';
|
||||||
import { GameState } from '$lib/play/admin/game_state.ts';
|
import { IGameState } from '$lib/play/admin/game_state.ts';
|
||||||
import { QuizQuestionType } from '$lib/quiz_types';
|
import { QuizQuestionType } from '$lib/quiz_types';
|
||||||
|
|
||||||
navbarVisible.visible = false;
|
navbarVisible.visible = false;
|
||||||
@@ -35,25 +32,50 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
data: any;
|
data: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GameState implements IGameState {
|
||||||
|
public game_id: string;
|
||||||
|
public players: Player[];
|
||||||
|
public player_scores: Record<string, number>;
|
||||||
|
public selected_question: number;
|
||||||
|
public timer_res: string;
|
||||||
|
public question_results: any;
|
||||||
|
public answer_count: number;
|
||||||
|
public shown_question_now: number;
|
||||||
|
public final_results: Array<null> | Array<Array<PlayerAnswer>>;
|
||||||
|
public game_started: boolean;
|
||||||
|
public quiz_data: QuizData;
|
||||||
|
public control_visible: boolean;
|
||||||
|
|
||||||
|
constructor(game_id: string) {
|
||||||
|
this.game_id = game_id;
|
||||||
|
this.players = $state([]);
|
||||||
|
this.player_scores = $state({});
|
||||||
|
this.selected_question = $state(-1);
|
||||||
|
this.timer_res = $state(undefined);
|
||||||
|
this.quiz_data = $state(null);
|
||||||
|
this.control_visible = $state(true);
|
||||||
|
this.shown_question_now = $state(-1);
|
||||||
|
this.final_results = $state([null]);
|
||||||
|
this.game_started = $state(false);
|
||||||
|
this.question_results = $state(null);
|
||||||
|
this.answer_count = $state(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
let game_mode = $state();
|
let game_mode = $state();
|
||||||
let { auto_connect, game_token } = $state(data);
|
let { auto_connect, game_token } = $state(data);
|
||||||
const game_pin = data.game_pin;
|
const game_pin = data.game_pin;
|
||||||
|
|
||||||
let players: Array<Player> = $state([]);
|
|
||||||
let player_scores = $state({});
|
|
||||||
let errorMessage = $state('');
|
let errorMessage = $state('');
|
||||||
let game_started = $state(false);
|
|
||||||
let quiz_data: QuizData = $state();
|
|
||||||
let control_visible = $state(true);
|
|
||||||
//let question_number = '0';
|
|
||||||
// let question_results = null;
|
|
||||||
let final_results: Array<null> | Array<Array<PlayerAnswer>> = $state([null]);
|
|
||||||
let success = $state(false);
|
let success = $state(false);
|
||||||
let dataexport_download_a = $state();
|
let dataexport_download_a = $state();
|
||||||
let warnToLeave = true;
|
let warnToLeave = true;
|
||||||
let export_token = $state(undefined);
|
let export_token = $state(undefined);
|
||||||
|
|
||||||
|
|
||||||
|
const socket_game_controls: SocketGameControls = new SocketGameControls(socket);
|
||||||
|
let game_state: GameState = $state(new GameState(game_token));
|
||||||
|
|
||||||
const connect = async () => {
|
const connect = async () => {
|
||||||
socket.emit('register_as_admin', {
|
socket.emit('register_as_admin', {
|
||||||
game_pin: game_pin,
|
game_pin: game_pin,
|
||||||
@@ -73,12 +95,12 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('registered_as_admin', (data) => {
|
socket.on('registered_as_admin', (data) => {
|
||||||
quiz_data = JSON.parse(data['game']);
|
game_state.quiz_data = JSON.parse(data['game']);
|
||||||
console.log(quiz_data);
|
console.log(game_state.quiz_data);
|
||||||
success = true;
|
success = true;
|
||||||
});
|
});
|
||||||
socket.on('player_joined', (int_data) => {
|
socket.on('player_joined', (int_data) => {
|
||||||
players = [...players, int_data];
|
game_state.players = [...game_state.players, int_data];
|
||||||
});
|
});
|
||||||
socket.on('already_registered_as_admin', () => {
|
socket.on('already_registered_as_admin', () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
@@ -87,11 +109,11 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on('start_game', (_) => {
|
socket.on('start_game', (_) => {
|
||||||
game_started = true;
|
game_state.game_started = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('control_visibility', (data) => {
|
socket.on('control_visibility', (data) => {
|
||||||
control_visible = data.visible;
|
game_state.control_visible = data.visible;
|
||||||
});
|
});
|
||||||
|
|
||||||
/* socket.on('question_results', (int_data) => {
|
/* socket.on('question_results', (int_data) => {
|
||||||
@@ -141,40 +163,39 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
window.matchMedia('(prefers-color-scheme: dark)').matches);
|
window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
let bg_color = $derived(quiz_data ? quiz_data.background_color : undefined);
|
let bg_color = $derived(game_state.quiz_data ? game_state.quiz_data.background_color : undefined);
|
||||||
let bg_image = $derived(quiz_data ? quiz_data.background_image : undefined);
|
let bg_image = $derived(game_state.quiz_data ? game_state.quiz_data.background_image : undefined);
|
||||||
let results_saved = $state(false);
|
let results_saved = $state(false);
|
||||||
|
|
||||||
let show_final_results = $derived(JSON.stringify(final_results) !== JSON.stringify([null]));
|
let show_final_results = $derived(JSON.stringify(game_state.final_results) !== JSON.stringify([null]));
|
||||||
|
|
||||||
const socket_game_controls: SocketGameControls = new SocketGameControls(socket);
|
|
||||||
let game_state: GameState = $state(new GameState(game_token));
|
|
||||||
|
|
||||||
// This function in called in every keyboard event in this page
|
// This function in called in every keyboard event in this page
|
||||||
const next_action = (e: KeyboardEvent) => {
|
const next_action = (e: KeyboardEvent) => {
|
||||||
if((e.key in ["Enter", " "])) return; // Don't catch events other than enter or spacebar
|
if((e.key in ["Enter", " "])) return; // Don't catch events other than enter or spacebar
|
||||||
|
|
||||||
if(!game_started && players.length > 0) { // Game not started with conditions matched
|
console.log(game_state);
|
||||||
|
|
||||||
|
if(!game_state.game_started && game_state.players.length > 0) { // Game not started with conditions matched
|
||||||
socket_game_controls.start_game()
|
socket_game_controls.start_game()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a messy condition to check if we can show final results
|
// This is a messy condition to check if we can show final results
|
||||||
else if(game_state.selected_question + 1 === quiz_data.questions.length && ((game_state.timer_res === '0' && game_state.question_results !== null) || quiz_data?.questions?.[game_state.selected_question]?.type === QuizQuestionType.SLIDE)){
|
else if(game_state.selected_question + 1 === game_state.quiz_data.questions.length && ((game_state.timer_res === '0' && game_state.question_results !== null) || game_state.quiz_data?.questions?.[game_state.selected_question]?.type === QuizQuestionType.SLIDE)){
|
||||||
socket_game_controls.get_final_results();
|
socket_game_controls.get_final_results();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Game is ongoing and the current question is eather finished or quiz just started
|
// Game is ongoing and the current question is either finished or quiz just started
|
||||||
else if(game_state.timer_res === '0' || game_state.selected_question === -1) {
|
else if(game_state.timer_res === '0' || game_state.selected_question === -1) {
|
||||||
// We are eather in the beginning of the quiz or in the end of it
|
// We are either in the beginning of the quiz or in the end of it
|
||||||
if((game_state.selected_question + 1 !== quiz_data.questions.length && game_state.question_results !== null) || game_state.selected_question === -1) {
|
if((game_state.selected_question + 1 !== game_state.quiz_data.questions.length && game_state.question_results !== null) || game_state.selected_question === -1) {
|
||||||
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
||||||
}
|
}
|
||||||
// Else we are in the middle of a question
|
// Else we are in the middle of a question
|
||||||
else if(game_state.question_results === null && game_state.selected_question !== -1) {
|
else if(game_state.question_results === null && game_state.selected_question !== -1) {
|
||||||
if(quiz_data.questions[game_state.selected_question].type === QuizQuestionType.SLIDE) {
|
if(game_state.quiz_data.questions[game_state.selected_question].type === QuizQuestionType.SLIDE) {
|
||||||
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
||||||
}
|
}
|
||||||
else if(quiz_data.questions[game_state.selected_question]?.hide_results === true) {
|
else if(game_state.quiz_data.questions[game_state.selected_question]?.hide_results === true) {
|
||||||
socket_game_controls.get_question_results(game_token, game_state.shown_question_now);
|
socket_game_controls.get_question_results(game_token, game_state.shown_question_now);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
||||||
@@ -188,7 +209,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
|
|
||||||
// Game started, question not finished we check if the question is slide or not
|
// Game started, question not finished we check if the question is slide or not
|
||||||
else if(game_state.selected_question !== -1) {
|
else if(game_state.selected_question !== -1) {
|
||||||
switch(quiz_data.questions[game_state.selected_question].type) {
|
switch(game_state.quiz_data.questions[game_state.selected_question].type) {
|
||||||
case QuizQuestionType.SLIDE:
|
case QuizQuestionType.SLIDE:
|
||||||
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
socket_game_controls.set_question_number(game_state.selected_question + 1);
|
||||||
break;
|
break;
|
||||||
@@ -213,8 +234,8 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
: `unset`}; background-color: {bg_color ? bg_color : 'transparent'}"
|
: `unset`}; background-color: {bg_color ? bg_color : 'transparent'}"
|
||||||
class:text-black={bg_color}
|
class:text-black={bg_color}
|
||||||
>
|
>
|
||||||
{#if JSON.stringify(final_results) !== JSON.stringify([null])}
|
{#if JSON.stringify(game_state.final_results) !== JSON.stringify([null])}
|
||||||
{#if control_visible}
|
{#if game_state.control_visible}
|
||||||
<div class="w-screen flex justify-center mt-16">
|
<div class="w-screen flex justify-center mt-16">
|
||||||
<div class="w-fit">
|
<div class="w-fit">
|
||||||
{#if export_token === undefined}
|
{#if export_token === undefined}
|
||||||
@@ -254,27 +275,23 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<FinalResults bind:data={player_scores} {show_final_results} />
|
<FinalResults bind:data={game_state.player_scores} {show_final_results} />
|
||||||
{/if}
|
{/if}
|
||||||
{#if !success}
|
{#if !success}
|
||||||
{#if errorMessage !== ''}
|
{#if errorMessage !== ''}
|
||||||
<p class="text-red-700">{errorMessage}</p>
|
<p class="text-red-700">{errorMessage}</p>
|
||||||
{/if}
|
{/if}
|
||||||
{:else if !game_started}
|
{:else if !game_state.game_started}
|
||||||
<GameNotStarted
|
<GameNotStarted
|
||||||
{game_pin}
|
{game_pin}
|
||||||
bind:players
|
bind:game_state
|
||||||
{socket_game_controls}
|
{socket_game_controls}
|
||||||
cqc_code={page.url.searchParams.get('cqc_code')}
|
cqc_code={page.url.searchParams.get('cqc_code')}
|
||||||
/>
|
/>
|
||||||
{:else}
|
{:else}
|
||||||
<SomeAdminScreen
|
<SomeAdminScreen
|
||||||
bind:final_results
|
|
||||||
{game_token}
|
{game_token}
|
||||||
bind:quiz_data
|
|
||||||
{bg_color}
|
{bg_color}
|
||||||
bind:player_scores
|
|
||||||
{control_visible}
|
|
||||||
bind:game_state
|
bind:game_state
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
Reference in New Issue
Block a user