main
Yanick Champoux 2023-01-14 13:25:46 -05:00
parent 59d4856904
commit 6bbba634c8
9 changed files with 259 additions and 121 deletions

View File

@ -1,30 +1,46 @@
<h5><a href={`#/campaign/${params.campaignId}`}>{$activeCampaign?.name}</a></h5> <article class="scroll">
<h6>Chapter {chapter}, battle {chapterBattle} -- {status}</h6> <header class="fixed">
<dl> <nav>
{#if status === 'ongoing'} <a href="#/">
<dt>wave</dt> <button class="circle transparent">
<dd>{wave === 2 ? 'second' : 'first'}</dd> <i>menu</i>
{/if} </button></a
<dt>city</dt> >
<dd>{city}</dd> <h5 class="max center-align">
<dt>scenario</dt> {#if $activeCampaign}
<dd>{scenario}</dd> <a href={`#/campaign/${$activeCampaign._id}`}>
<dt>character</dt> {$activeCampaign.name}
<dd>{character}</dd> </a>
{/if}
</h5>
</nav>
</header>
<h6>Chapter {chapter}, battle {chapterBattle} -- {status}</h6>
<dl>
{#if status === 'ongoing'}
<dt>wave</dt>
<dd>{wave === 2 ? 'second' : 'first'}</dd>
{/if}
<dt>city</dt>
<dd>{city}</dd>
<dt>scenario</dt>
<dd>{scenario}</dd>
<dt>character</dt>
<dd>{character}</dd>
<dt>difficulty</dt> <dt>difficulty</dt>
<dd> <dd>
<div class="field border"> <div class="field border">
<input <input
type="number" type="number"
step="0.5" step="0.5"
value={difficulty} value={difficulty}
on:change={({ target: { value } }) => on:change={({ target: { value } }) =>
event.setBattleDifficulty(params.battleId, value)} event.setBattleDifficulty(params.battleId, value)}
/> />
</div> </div>
<!-- <!--
<Stars <Stars
on:change={(e) => (difficulty = e.target.value)} on:change={(e) => (difficulty = e.target.value)}
config={{ config={{
@ -44,15 +60,42 @@
/> />
{difficulty} {difficulty}
--> -->
</dd> </dd>
</dl> </dl>
{#if status === 'upcoming'} {#if status === 'upcoming'}
<button>Start battle</button> <button>Start battle</button>
{:else if status === 'ongoing'} {:else if status === 'ongoing'}
<button on:click={battleVerdict('won')}>Battle won</button> <button on:click={battleVerdict('won')}>Battle won</button>
<button on:click={battleVerdict('lost')}>Battle lost</button> <button on:click={battleVerdict('lost')}>Battle lost</button>
{/if} {/if}
<footer class="fixed">
<nav>
<a
href={`#/campaign/${$activeCampaign?._id}` +
(battle?.id == 1
? ''
: `/battle/${parseInt(battle?.id) - 1}`)}
>
<button class="circle transparent">
<i>arrow_backward</i>
</button>
</a>
{#if $activeCampaign.battles.length > battle.id}
<a
href={`#/campaign/${$activeCampaign._id}/battle/${
battle.id + 1
}`}
>
<button class="circle transparent">
<i>arrow_forward</i>
</button>
</a>
{/if}
</nav>
</footer>
</article>
<script> <script>
import { getContext } from 'svelte'; import { getContext } from 'svelte';
@ -79,7 +122,7 @@
let chapter = 1 + parseInt(params.battleId / 2); let chapter = 1 + parseInt(params.battleId / 2);
let chapterBattle = params.battleId % 2; let chapterBattle = params.battleId % 2;
const battleVerdict = (verdict) => const battleVerdict = (verdict) => () =>
event.setBattleVerdict(params.battleId, verdict); event.setBattleVerdict(params.battleId, verdict);
// $: event.setBattleDifficulty(params.battleId, difficulty); // $: event.setBattleDifficulty(params.battleId, difficulty);
@ -121,4 +164,8 @@
input { input {
width: 5em; width: 5em;
} }
footer nav {
display: flex;
justify-content: space-between;
}
</style> </style>

View File

@ -1,35 +1,113 @@
<article> <article class="scroll">
<header class="fixed">
<nav>
<a href="#/">
<button class="circle transparent">
<i>menu</i>
</button></a
>
<h5 class="max center-align">
{#if $activeCampaign}
{$activeCampaign.name}
{/if}
</h5>
</nav>
</header>
{#if $activeCampaign} {#if $activeCampaign}
<h5>{$activeCampaign.name}</h5>
<h6>Chapter 1</h6> <h6>Chapter 1</h6>
<Battle {...battles[0]} {campaignId} chapter={1} chapterBattle={1} /> <Battle {...battles[0]} {campaignId} chapter={1} chapterBattle={1} />
<div class="medium-divider" /> <div class="medium-divider" />
<Battle {...battles[1]} {campaignId} chapter={1} chapterBattle={2} /> <Battle
{...battles[1] ?? { id: 'notYet' }}
{campaignId}
chapter={1}
chapterBattle={2}
/>
<div class="medium-divider" {campaignId} /> <div class="medium-divider" {campaignId} />
<h6>Chapter 2</h6> <h6>Chapter 2</h6>
<Battle {...battles[2]} {campaignId} chapter={2} chapterBattle={1} /> <Battle
{...battles[2] ?? { id: 'notYet' }}
{campaignId}
chapter={2}
chapterBattle={1}
/>
<div class="medium-divider" /> <div class="medium-divider" />
<Battle {...battles[3]} {campaignId} chapter={2} chapterBattle={2} /> <Battle
{...battles[3] ?? { id: 'notYet' }}
{campaignId}
chapter={2}
chapterBattle={2}
/>
<div class="medium-divider" /> <div class="medium-divider" />
<h6>Chapter 3</h6> <h6>Chapter 3</h6>
<Battle {...battles[4]} {campaignId} chapter={3} chapterBattle={1} /> <Battle
{...battles[4] ?? { id: 'notYet' }}
{campaignId}
chapter={3}
chapterBattle={1}
/>
<div class="medium-divider" /> <div class="medium-divider" />
<Battle {...battles[5]} {campaignId} chapter={3} chapterBattle={2} /> <Battle
{...battles[4] ?? { id: 'notYet' }}
{campaignId}
chapter={3}
chapterBattle={2}
/>
<div class="medium-divider" /> <div class="medium-divider" />
<h6>Chapter 4</h6> <h6>Chapter 4</h6>
<Battle {...battles[6]} {campaignId} chapter={4} chapterBattle={1} /> <Battle
{...battles[6] ?? { id: 'notYet' }}
{campaignId}
chapter={4}
chapterBattle={1}
/>
<div class="medium-divider" /> <div class="medium-divider" />
<Battle {...battles[7]} {campaignId} chapter={4} chapterBattle={2} /> <Battle
{...battles[7] ?? { id: 'notYet' }}
{campaignId}
chapter={4}
chapterBattle={2}
/>
<div class="medium-divider" /> <div class="medium-divider" />
<Battle {...battles[8]} {campaignId} chapter={4} chapterBattle={3} /> <Battle
{...battles[8] ?? { id: 'notYet' }}
{campaignId}
chapter={4}
chapterBattle={3}
/>
<div class="medium-divider" /> <div class="medium-divider" />
<Battle {...battles[9]} {campaignId} chapter={4} chapterBattle={4} /> <Battle
{...battles[9] ?? { id: 'notYet' }}
{campaignId}
chapter={4}
chapterBattle={4}
/>
<div class="medium-divider" /> <div class="medium-divider" />
<Battle {...battles[10]} {campaignId} chapter={4} chapterBattle={5} /> <Battle
{...battles[10] ?? { id: 'notYet' }}
{campaignId}
chapter={4}
chapterBattle={5}
/>
<div class="medium-divider" /> <div class="medium-divider" />
<Battle {...battles[11]} {campaignId} chapter={4} chapterBattle={6} /> <Battle
{...battles[11] ?? { id: 'notYet' }}
{campaignId}
chapter={4}
chapterBattle={6}
/>
<div class="medium-divider" /> <div class="medium-divider" />
{/if} {/if}
<footer class="fixed">
<nav>
<a href={`#/campaign/${$activeCampaign._id}/battle/1`}>
<button class="circle transparent">
<i>arrow_forward</i>
</button>
</a>
</nav>
</footer>
</article> </article>
<script> <script>
@ -48,8 +126,6 @@
$: battles = $activeCampaign?.battles ?? []; $: battles = $activeCampaign?.battles ?? [];
$: while (battles.length <= 12) battles.push({ notYet: true });
const campaignId = params.campaignId; const campaignId = params.campaignId;
$: byChapter = R.pipe( $: byChapter = R.pipe(
@ -61,4 +137,14 @@
</script> </script>
<style> <style>
article {
height: 100vh;
}
footer {
display: flex;
justify-content: end;
}
footer nav {
text-align: right;
}
</style> </style>

View File

@ -1,6 +1,6 @@
<div class="row"> <div class="row">
<p> <p>
{#if notYet} {#if id == 'notYet'}
??? ???
{:else} {:else}
<a href={`#/campaign/${campaignId}/battle/${id}`}> <a href={`#/campaign/${campaignId}/battle/${id}`}>
@ -14,7 +14,6 @@
export let city = ''; export let city = '';
export let status = ''; export let status = '';
export let id = 0; export let id = 0;
export let notYet = false;
export let campaignId; export let campaignId;
export let chapter; export let chapter;
export let chapterBattle; export let chapterBattle;

View File

@ -1,10 +1,14 @@
<article> <header>
<header> <nav>
<h5>Campaigns</h5> <h5 class="max center-align">Campaigns</h5>
<button on:click={newCampaign}>new campaign</button> <button class="circle small" on:click={newCampaign}>
</header> <i>add</i>
</button>
</nav>
</header>
{#each $campaigns as campaign (campaign.id)} <article>
{#each $campaigns as campaign (campaign._id)}
<div class="row"> <div class="row">
<i class="light-green-text">south_america</i> <i class="light-green-text">south_america</i>
<div class="max"> <div class="max">
@ -63,9 +67,9 @@
R.clamp(1 + parseInt(battles?.length / 2), { max: 4 }); R.clamp(1 + parseInt(battles?.length / 2), { max: 4 });
const currentCity = ({ battles }) => R.last(battles).city; const currentCity = ({ battles }) => R.last(battles).city;
async function deleteCampaign({ name, id }) { async function deleteCampaign({ name, _id }) {
if (!window.confirm(`delete campaign ${name}?`)) return; if (!window.confirm(`delete campaign ${name}?`)) return;
api.event.deleteCampaign(id); api.event.deleteCampaign(_id);
} }
const newCampaign = () => { const newCampaign = () => {
@ -80,6 +84,9 @@
</script> </script>
<style> <style>
nav {
width: 100%;
}
header { header {
display: flex; display: flex;
} }

View File

@ -4,14 +4,13 @@ import Dexie, { liveQuery } from 'dexie';
import { writable, derived, get } from 'svelte/store'; import { writable, derived, get } from 'svelte/store';
import { genNextBattle } from './genNextBattle.js'; import { genNextBattle } from './genNextBattle.js';
import u from '@yanick/updeep-remeda'; import u from '@yanick/updeep-remeda';
import pouchMem from 'pouchdb-adapter-memory';
const seedCampaign = { const seedCampaign = {
battles: [], battles: [],
}; };
export function genApi(options = {}) { export function genApi(options = {}) {
if (options.local) PouchDB.plugin(pouchMem); // if (options.local) MyPouch.plugin(pouchMem);
const pouchdb = new PouchDB( const pouchdb = new PouchDB(
'Campaigns', 'Campaigns',

View File

@ -1,40 +1,40 @@
export default [ export default [
{ {
cities: ['Havana', 'Montreal', 'Mexico City', 'Rio de Janeiro'], cities: ['Havana', 'Montreal', 'Mexico City', 'Rio de Janeiro'],
characters: [ characters: [
'Clinton Harper', 'Clinton Harper',
'Jackson Moss', 'Jackson Moss',
'Lucia Ortego', 'Lucia Ortego',
'Samantha Legrand', 'Samantha Legrand',
], ],
scenarios: [ scenarios: [
'Evacuation', 'Evacuation',
'Reinforcements', 'Reinforcements',
'Battle for the sky', 'Battle for the sky',
'Satellites', 'Satellites',
], ],
}, },
{ {
cities: ['Johannesburg', 'Moscow', 'Cairo', 'London', 'Paris'], cities: ['Johannesburg', 'Moscow', 'Cairo', 'London', 'Paris'],
characters: [ characters: [
'Jaroslav Ruzicka', 'Jaroslav Ruzicka',
'Karima Almasi', 'Karima Almasi',
'Shanti Aumann', 'Shanti Aumann',
'Pieter Bernstein', 'Pieter Bernstein',
], ],
scenarios: [ scenarios: [
'Command ship', 'Command ship',
'Reactor leak', 'Reactor leak',
'Dangerous research', 'Dangerous research',
'Repairing the base', 'Repairing the base',
], ],
}, },
{ {
cities: ['Seoul', 'Beijing', 'Tokyo', 'Sydney', 'Singapore'], cities: ['Seoul', 'Beijing', 'Tokyo', 'Sydney', 'Singapore'],
characters: ['Wang Lin', "Iz'ox", 'Jang Chanwook', 'Archie Bell'], characters: ['Wang Lin', "Iz'ox", 'Jang Chanwook', 'Archie Bell'],
scenarios: ['Storm', 'Contamination', 'Kamikaze', 'Saboteur'], scenarios: ['Storm', 'Contamination', 'Kamikaze', 'Saboteur'],
}, },
{ {
scenarios: ['The final battle'], scenarios: ['The final battle'],
}, },
]; ];

View File

@ -4,25 +4,25 @@ import chapters from './chapters.js';
const pickOne = (choices) => choices[parseInt(Math.random() * choices.length)]; const pickOne = (choices) => choices[parseInt(Math.random() * choices.length)];
export function genNextBattle(battles = []) { export function genNextBattle(battles = []) {
let chapter = R.clamp(1 + parseInt(battles.length / 2), { min: 1, max: 4 }); let chapter = R.clamp(1 + parseInt(battles.length / 2), { min: 1, max: 4 });
if (chapter === 1) { if (chapter === 1) {
const scenario = pickOne( const scenario = pickOne(
chapters[chapter - 1].scenarios.filter( chapters[chapter - 1].scenarios.filter(
(s) => !battles.map(R.prop('scenario')).includes(s), (s) => !battles.map(R.prop('scenario')).includes(s),
), ),
); );
const character = pickOne( const character = pickOne(
chapters[chapter - 1].characters.filter( chapters[chapter - 1].characters.filter(
(s) => !battles.map(R.prop('character')).includes(s), (s) => !battles.map(R.prop('character')).includes(s),
), ),
); );
const city = pickOne( const city = pickOne(
chapters[chapter - 1].cities.filter( chapters[chapter - 1].cities.filter(
(s) => !battles.map(R.prop('city')).includes(s), (s) => !battles.map(R.prop('city')).includes(s),
), ),
); );
return { scenario, character, city, status: 'ongoing', wave: 1 }; return { scenario, character, city, status: 'ongoing', wave: 1 };
} }
} }

View File

@ -2,7 +2,7 @@
import App from './lib/components/App.svelte'; import App from './lib/components/App.svelte';
const app = new App({ const app = new App({
target: document.getElementById('app'), target: document.getElementById('app'),
}); });
export default app; export default app;

View File

@ -1,4 +1,4 @@
export function load({ params }) { export function load({ params }) {
params.campaignId = parseInt(params?.campaignId); params.campaignId = parseInt(params?.campaignId);
return params; return params;
} }