Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions apps/website/src/lib/components/CurrencyDisplay.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script lang="ts">
import type { CurrencyDetail } from '$lib/demo/model/CurrencyDetail';
import type { Asset } from '$app/types';
import { asset } from '$app/paths';

interface Props {
amount: number;
currency: CurrencyDetail;
}

let { amount, currency }: Props = $props();

const icon = $derived(asset(currency.icon as Asset));
</script>

<div class="flex flex-row items-center space-x-1">
<img class="pixelated h-6 w-6" src={icon} alt={currency.name} />
<span>{amount}</span>
</div>
114 changes: 114 additions & 0 deletions apps/website/src/lib/components/farming/FarmDisplay.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<script lang="ts">
import type { Farming } from '$lib/demo/features/Farming.svelte';
import FarmPlot from '$lib/components/farming/FarmPlot.svelte';
import { getCurrency, getPlant, type PlantId, plants } from '$lib/demo/content';
import { asset } from '$app/paths';
import { currency, game } from '$lib/demo/demo.svelte';
import CurrencyDisplay from '$lib/components/CurrencyDisplay.svelte';

interface Props {
farming: Farming;
}

let { farming }: Props = $props();

const plotClicked = (index: number) => {
if (farming.isReady(index)) {
farming.reap(index);
} else {
farming.sow(index, selectedSeed);
}
};

let selectedSeed: PlantId = $state(plants[0].id);

const selectSeed = (id: PlantId) => {
selectedSeed = id;
};

const buySeed = (id: PlantId) => {
const plant = getPlant(id);
game.engine.handleTransaction({
input: {
type: 'currency',
id: '/currency/money',
amount: plant.seedCost,
},
output: {
type: 'currency',
id: plant.id,
amount: 1,
},
});
};

const harvestAll = (): void => {
game.request({
type: '/farming/reap-all',
});
};

const plantAll = (): void => {
game.request({
type: '/farming/sow-all',
plant: selectedSeed,
});
};
</script>

<div class="flex flex-col">
<div class="flex flex-row justify-center">
<div class="flex flex-row space-x-8">
<div class="flex flex-col">
<div class="mb-4 flex flex-row justify-center space-x-4">
<button class="btn btn-error">Toggle amounts</button>
<button class="btn btn-error">Or something</button>
</div>

<table class="table h-min w-min">
<tbody>
{#each plants as plant (plant.id)}
<tr
onclick={() => selectSeed(plant.id)}
class="cursor-pointer {selectedSeed === plant.id ? 'bg-base-200' : ''}"
>
<td>
<div class="flex flex-row items-center space-x-2">
<img class="pixelated h-4 w-4" src={asset(plant.stages[0])} alt={plant.name} />
<span>{plant.name}</span>
</div>
</td>
<td><span>{currency.getBalance(plant.id)}</span> </td>
<td>
<button class="btn btn-success w-28" onclick={() => buySeed(plant.id)}>
Buy
<CurrencyDisplay amount={plant.seedCost} currency={getCurrency('/currency/money')} />
</button>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<div class="flex flex-col">
<div class="mb-4 flex flex-row justify-center space-x-4">
<button class="btn btn-success" onclick={() => plantAll()}>Plant All</button>
<button class="btn btn-success" onclick={() => harvestAll()}>Harvest All</button>
</div>

<div class="grid grid-cols-5 gap-4">
{#each farming.plots as plot, i (i)}
<button
class="cursor-pointer"
onclick={() => {
plotClicked(i);
}}
>
<FarmPlot {plot} index={i}></FarmPlot>
</button>
{/each}
</div>
</div>
</div>
</div>
</div>
32 changes: 32 additions & 0 deletions apps/website/src/lib/components/farming/FarmPlot.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script lang="ts">
import type { FarmPlotState } from '$lib/demo/features/FarmingState';
import { getPlant } from '$lib/demo/content';
import { farming } from '$lib/demo/demo.svelte';
import { asset } from '$app/paths';
import type { Asset } from '$app/types';

interface Props {
index: number;
plot: FarmPlotState;
}

let { index, plot }: Props = $props();

let plant = $derived(plot.plant ? getPlant(plot.plant) : null);
let plantIcon = $derived(asset(farming.getGrowthStage(index) as Asset));
</script>

<div class="border-primary h-24 w-24 border">
{#if plant}
<div class="flex h-full flex-col items-center justify-between p-3">
<img class="pixelated h-12 w-12" src={plantIcon} alt={plant.name} />

{#if !farming.isReady(index)}
<progress class="progress progress-info" value={plot.progress} max={plant.growthTime}></progress>
{/if}
</div>
{/if}
</div>

<style>
</style>
147 changes: 143 additions & 4 deletions apps/website/src/lib/demo/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,154 @@ import type { PlantDetail } from '$lib/demo/model/PlantDetail';

// First we declare our fully static content
export const plants = [
{ id: '/plant/sunflower', name: 'Sunflower', growthTime: 1, moneyReward: 10 },
{ id: '/plant/cauliflower', name: 'Cauliflower', growthTime: 1.5, moneyReward: 20 },
{
id: '/plant/cabbage',
name: 'Cabbage',
growthTime: 10,
moneyReward: 10,
seedCost: 6,
stages: [
'/plants/cabbage/cabbage_00.png',
'/plants/cabbage/cabbage_01.png',
'/plants/cabbage/cabbage_02.png',
'/plants/cabbage/cabbage_03.png',
'/plants/cabbage/cabbage_04.png',
'/plants/cabbage/cabbage_05.png',
],
},
{
id: '/plant/potato',
name: 'Potato',
growthTime: 20,
moneyReward: 20,
seedCost: 12,
stages: [
'/plants/potato/potato_00.png',
'/plants/potato/potato_01.png',
'/plants/potato/potato_02.png',
'/plants/potato/potato_03.png',
'/plants/potato/potato_04.png',
'/plants/potato/potato_05.png',
],
},
{
id: '/plant/carrot',
name: 'Carrot',
growthTime: 30,
moneyReward: 40,
seedCost: 20,
stages: [
'/plants/carrot/carrot_00.png',
'/plants/carrot/carrot_01.png',
'/plants/carrot/carrot_02.png',
'/plants/carrot/carrot_03.png',
'/plants/carrot/carrot_04.png',
'/plants/carrot/carrot_05.png',
],
},
{
id: '/plant/beetroot',
name: 'Beetroot',
growthTime: 40,
moneyReward: 50,
seedCost: 40,
stages: [
'/plants/beetroot/beetroot_00.png',
'/plants/beetroot/beetroot_01.png',
'/plants/beetroot/beetroot_02.png',
'/plants/beetroot/beetroot_03.png',
'/plants/beetroot/beetroot_04.png',
'/plants/beetroot/beetroot_05.png',
],
},
{
id: '/plant/radish',
name: 'Radish',
growthTime: 50,
moneyReward: 20,
seedCost: 75,
stages: [
'/plants/radish/radish_00.png',
'/plants/radish/radish_01.png',
'/plants/radish/radish_02.png',
'/plants/radish/radish_03.png',
'/plants/radish/radish_04.png',
'/plants/radish/radish_05.png',
],
},
{
id: '/plant/cauliflower',
name: 'Cauliflower',
growthTime: 30,
moneyReward: 20,
seedCost: 120,
stages: [
'/plants/cauliflower/cauliflower_00.png',
'/plants/cauliflower/cauliflower_01.png',
'/plants/cauliflower/cauliflower_02.png',
'/plants/cauliflower/cauliflower_03.png',
'/plants/cauliflower/cauliflower_04.png',
'/plants/cauliflower/cauliflower_05.png',
],
},
{
id: '/plant/pumpkin',
name: 'Pumpkin',
growthTime: 30,
seedCost: 200,
moneyReward: 20,
stages: [
'/plants/pumpkin/pumpkin_00.png',
'/plants/pumpkin/pumpkin_01.png',
'/plants/pumpkin/pumpkin_02.png',
'/plants/pumpkin/pumpkin_03.png',
'/plants/pumpkin/pumpkin_04.png',
'/plants/pumpkin/pumpkin_05.png',
],
},
{
id: '/plant/sunflower',
name: 'Sunflower',
growthTime: 10,
moneyReward: 10,
seedCost: 1000,
stages: [
'/plants/sunflower/sunflower_00.png',
'/plants/sunflower/sunflower_01.png',
'/plants/sunflower/sunflower_02.png',
'/plants/sunflower/sunflower_03.png',
'/plants/sunflower/sunflower_04.png',
'/plants/sunflower/sunflower_05.png',
],
},
] as const satisfies PlantDetail[];
export type PlantId = (typeof plants)[number]['id'];

export const getPlant = (id: PlantId): PlantDetail => {
// TODO(@Isha): Make into dictionary lookup
const plant = plants.find((p) => p.id === id);
if (!plant) {
throw new Error(`Plant with id '${id}' not found.`);
}
return plant;
};

export const currencies: CurrencyDetail[] = [
{ id: '/currency/money', name: 'Money', icon: '/icon/coin' },
{ id: '/currency/gems', name: 'Gems', icon: '/icon/gem-blue' },
{ id: '/currency/money', name: 'Money', icon: '/icons/egg.png' },
{ id: '/currency/compost', name: 'Compost', icon: '/icon/compost' },
];

export type CurrencyId = (typeof currencies)[number]['id'];

export const getCurrency = (id: CurrencyId): CurrencyDetail => {
// TODO(@Isha): Make into dictionary lookup
const currency = currencies.find((c) => c.id === id);
if (!currency) {
throw new Error(`Currency with id '${id}' not found.`);
}
return currency;
};

export const statistics: StatisticDetail[] = [
{ id: '/statistic/total-money', type: 'scalar' },
{ id: '/statistic/plants-planted', type: 'map' },
Expand Down
12 changes: 7 additions & 5 deletions apps/website/src/lib/demo/demo.svelte.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import {
AchievementPlugin,
AlwaysTrueCondition,
type ExtractOutput,
CouponPlugin,
createAchievementState,
createCouponState,
createCurrencyState,
createStatisticState,
CurrencyPlugin,
type ExtractCondition,
type ExtractInput,
type ExtractOutput,
LudiekEngine,
LudiekGame,
type ExtractCondition,
StatisticPlugin,
} from '@123ishatest/ludiek';
import { Farming } from '$lib/demo/features/Farming';
import { Farming } from '$lib/demo/features/Farming.svelte';
import { achievements, currencies, plants, statistics } from '$lib/demo/content';

// Define plugins with reactive state
Expand Down Expand Up @@ -43,17 +43,19 @@ export type Condition = ExtractCondition<typeof engine.conditions>;
export type PlantId = (typeof plants)[number]['id'];

// Create your game
const farming = new Farming(plants);
const farmingFeature = new Farming(plants);

export const game = new LudiekGame(engine, {
features: [farming],
features: [farmingFeature],
saveKey: '@123ishatest/ludiek-demo',
tickDuration: 0.1,
saveInterval: 30,
});

engine.plugins.currency.loadContent(currencies);
engine.plugins.currency.loadContent(plants);
engine.plugins.statistic.loadContent(statistics);
engine.plugins.achievement.loadContent(achievements);

export const { currency, statistic, achievement } = engine.plugins;
export const { farming } = game.features;
Loading