mirror of
https://github.com/tonhowtf/omniget.git
synced 2026-10-02 09:34:50 +08:00
feat(world): draw the workshop town and work its farms
The city canvas merges the house furniture atlas (craft-v1) with the workshop's town art (vale-v1: ground, houses, props, crop stages), generated from the offline export with a tile entry per frame. A click on a bed waters, harvests, clears or plants it (the server decides who may and what grows); the panel shows the harvest in the house and picks the crop to plant.
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
import { t } from "$lib/i18n";
|
||||
import { getSettings } from "$lib/stores/settings-store.svelte";
|
||||
import { codecErrorCode } from "$lib/world/codec";
|
||||
import { agentSprite, buildChunkTiles, loadWorldAtlas, objectFrame, type MapDef } from "$lib/world/assets";
|
||||
import { agentSprite, buildChunkTiles, loadWorldAtlas, objectFrame, CRAFT_ATLAS_URL, VALE_ATLAS_URL, type MapDef } from "$lib/world/assets";
|
||||
import { buildHud, capAgents, gameClockLabel, type AgentFrame } from "$lib/world/hud";
|
||||
import { attachInput, type Pickable } from "$lib/world/input";
|
||||
import { sample } from "$lib/world/interp";
|
||||
@@ -27,12 +27,16 @@
|
||||
interface Props {
|
||||
city: string;
|
||||
server?: string | null;
|
||||
/** What a click on an empty bed plants. */
|
||||
crop?: string;
|
||||
/** A farm action was acknowledged (or refused, with the code). */
|
||||
onfarm?: (result: { ok: boolean; code: string; action: string }) => void;
|
||||
onready?: (ready: CityReady) => void;
|
||||
onfailed?: (error: string) => void;
|
||||
onstate?: (state: { region: string; ent: number; tick: number; interior: boolean }) => void;
|
||||
onstats?: (stats: FrameStats, fps: number, tier: Tier) => void;
|
||||
}
|
||||
let { city, server = null, onready, onfailed, onstate, onstats }: Props = $props();
|
||||
let { city, server = null, crop = "carrot", onready, onfailed, onstate, onstats, onfarm }: Props = $props();
|
||||
|
||||
/** A chat line over someone's head. */
|
||||
export function say(ent: number, text: string): void {
|
||||
@@ -202,6 +206,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** The crop bed drawn on a tile, if any: its kind decides the action. */
|
||||
function bedAt(x: number, y: number): { kind: string } | null {
|
||||
for (const o of world.objects.values()) {
|
||||
if (o.tx === x && o.ty === y && (o.kind.startsWith("crop/") || o.kind === "prop/soil-empty")) return { kind: o.kind };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Water, harvest, clear or plant, by what the bed shows. */
|
||||
async function farm(x: number, y: number, kind: string): Promise<void> {
|
||||
const stage = kind.split("/")[2] ?? "";
|
||||
const action = kind === "prop/soil-empty" ? "plant" : stage === "ripe" ? "harvest" : stage === "withered" ? "clear" : "water";
|
||||
const seq = await session?.send({ type: "farm", tile: [x, y], action, crop: action === "plant" ? crop : undefined });
|
||||
if (seq !== undefined) pendingFarm.set(seq, action);
|
||||
}
|
||||
const pendingFarm = new Map<number, string>();
|
||||
|
||||
/** The door marker on a tile of the current region, if any. */
|
||||
function portalAt(x: number, y: number): string | null {
|
||||
const map = maps.get(region);
|
||||
@@ -225,6 +246,12 @@
|
||||
try {
|
||||
await bootRender();
|
||||
session = createCitySession({ city, server });
|
||||
stops.push(session.onAck((ack) => {
|
||||
const action = pendingFarm.get(ack.seq);
|
||||
if (!action) return;
|
||||
pendingFarm.delete(ack.seq);
|
||||
onfarm?.({ ok: ack.status === 0, code: ack.code, action });
|
||||
}));
|
||||
stops.push(session.onClosed((reason) => {
|
||||
if (reason !== "left") {
|
||||
errorCode = `ERR_CITY_${reason.toUpperCase()}`;
|
||||
@@ -255,7 +282,8 @@
|
||||
const r = mod.createRenderer();
|
||||
const caps = await r.init(canvas!, { tier });
|
||||
tier = caps.tier;
|
||||
const loaded = await loadWorldAtlas();
|
||||
// The house furniture (craft-v1) and the town (vale-v1) in one atlas.
|
||||
const loaded = await loadWorldAtlas(CRAFT_ATLAS_URL, [VALE_ATLAS_URL]);
|
||||
r.loadAtlas(loaded.json, loaded.pages);
|
||||
atlas = mod.parseAtlas(loaded.json);
|
||||
const rect = canvas!.getBoundingClientRect();
|
||||
@@ -304,6 +332,11 @@
|
||||
void send({ type: "enter", portal });
|
||||
return;
|
||||
}
|
||||
const bed = bedAt(tile.x, tile.y);
|
||||
if (bed) {
|
||||
void farm(tile.x, tile.y, bed.kind);
|
||||
return;
|
||||
}
|
||||
void send({ type: "move", to: [tile.x, tile.y] });
|
||||
},
|
||||
onAgent: (id) => {
|
||||
|
||||
@@ -17,10 +17,14 @@
|
||||
onleave: () => void;
|
||||
onsay?: (ent: number, text: string) => void;
|
||||
ongoto?: (tile: [number, number]) => void;
|
||||
/** The crop a click on an empty bed plants; the canvas reads it. */
|
||||
crop?: string;
|
||||
oncrop?: (crop: string) => void;
|
||||
}
|
||||
let { city, where, onenter, onleave, onsay, ongoto }: Props = $props();
|
||||
let { city, where, onenter, onleave, onsay, ongoto, crop = "carrot", oncrop }: Props = $props();
|
||||
|
||||
type Home = { home: { id: string; name: string; plot: string; interior_region: string }; plot: { id: string; address: string; entrance: [number, number]; region: string } | null };
|
||||
type Home = { home: { id: string; name: string; plot: string; interior_region: string; inventory?: Record<string, number> }; plot: { id: string; address: string; entrance: [number, number]; region: string; kind?: string } | null };
|
||||
const CROPS = ["carrot", "tomato", "wheat"];
|
||||
|
||||
let cityId = $state("cidade-piloto");
|
||||
let server = $state(getSettings()?.world?.city_server ?? "");
|
||||
@@ -90,6 +94,11 @@
|
||||
}
|
||||
});
|
||||
|
||||
/** The canvas tells the page a farm action landed; refresh the inventory. */
|
||||
export function refreshHome(): void {
|
||||
void loadHome();
|
||||
}
|
||||
|
||||
async function loadHome(): Promise<void> {
|
||||
if (!city) return;
|
||||
try {
|
||||
@@ -184,6 +193,14 @@
|
||||
<span class="label">{$t("world.city.address")}</span>
|
||||
<span class="chip host">{home.plot.address}</span>
|
||||
<button type="button" class="btn" onclick={goHome}>{$t("world.city.go_home")}</button>
|
||||
{#if home.home.inventory && Object.keys(home.home.inventory).length > 0}
|
||||
<span class="chip" title={$t("world.city.inventory") as string}>{Object.entries(home.home.inventory).map(([k, v]) => `${$t(`world.city.crop_${k}`)} ×${v}`).join(" · ")}</span>
|
||||
{/if}
|
||||
<label class="label">{$t("world.city.plant")}
|
||||
<select value={crop} onchange={(e) => oncrop?.((e.currentTarget as HTMLSelectElement).value)}>
|
||||
{#each CROPS as c}<option value={c}>{$t(`world.city.crop_${c}`)}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
{:else}
|
||||
<button type="button" class="btn primary" onclick={claim} disabled={busy}>{$t("world.city.claim")}</button>
|
||||
<span class="hint">{$t("world.city.claim_hint")}</span>
|
||||
@@ -236,6 +253,16 @@
|
||||
opacity: 0.5;
|
||||
cursor: default;
|
||||
}
|
||||
select {
|
||||
padding: 0.3rem 0.5rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(127, 127, 127, 0.3);
|
||||
background: rgba(127, 127, 127, 0.08);
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
font-size: 0.85rem;
|
||||
margin-left: 0.3rem;
|
||||
}
|
||||
input {
|
||||
padding: 0.35rem 0.6rem;
|
||||
border-radius: 8px;
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
@@ -9378,17 +9378,32 @@ export type TranslationKeys =
|
||||
| 'world.city.claim_hint'
|
||||
| 'world.city.claimed'
|
||||
| 'world.city.create_account'
|
||||
| 'world.city.crop_carrot'
|
||||
| 'world.city.crop_tomato'
|
||||
| 'world.city.crop_wheat'
|
||||
| 'world.city.enter'
|
||||
| 'world.city.entering'
|
||||
| 'world.city.farm_bad_action'
|
||||
| 'world.city.farm_conflict'
|
||||
| 'world.city.farm_empty'
|
||||
| 'world.city.farm_no_cell'
|
||||
| 'world.city.farm_no_home'
|
||||
| 'world.city.farm_not_ripe'
|
||||
| 'world.city.farm_not_withered'
|
||||
| 'world.city.farm_not_your_plot'
|
||||
| 'world.city.farm_occupied'
|
||||
| 'world.city.farm_unknown_crop'
|
||||
| 'world.city.go_home'
|
||||
| 'world.city.have_account'
|
||||
| 'world.city.hint'
|
||||
| 'world.city.in'
|
||||
| 'world.city.inside'
|
||||
| 'world.city.inventory'
|
||||
| 'world.city.leave'
|
||||
| 'world.city.no_account'
|
||||
| 'world.city.outside'
|
||||
| 'world.city.password'
|
||||
| 'world.city.plant'
|
||||
| 'world.city.reconnecting'
|
||||
| 'world.city.server'
|
||||
| 'world.city.server_hint'
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Criar conta",
|
||||
"have_account": "Já tenho conta",
|
||||
"no_account": "Criar uma conta",
|
||||
"sign_out": "Sair da conta"
|
||||
"sign_out": "Sair da conta",
|
||||
"inventory": "Colheita na sua casa",
|
||||
"plant": "Plantar",
|
||||
"crop_carrot": "cenoura",
|
||||
"crop_tomato": "tomate",
|
||||
"crop_wheat": "trigo",
|
||||
"farm_empty": "Esse canteiro está vazio",
|
||||
"farm_not_ripe": "Ainda não está pronto; regue e espere",
|
||||
"farm_not_withered": "Nada para limpar",
|
||||
"farm_occupied": "Esse canteiro já tem cultura",
|
||||
"farm_unknown_crop": "Cultura desconhecida",
|
||||
"farm_conflict": "O canteiro mudou, tente de novo",
|
||||
"farm_bad_action": "Nada a fazer aí",
|
||||
"farm_not_your_plot": "Esse canteiro é de outra pessoa",
|
||||
"farm_no_home": "Reivindique um lote primeiro",
|
||||
"farm_no_cell": "Não há canteiro aí"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
+16
-1
@@ -9621,7 +9621,22 @@
|
||||
"create_account": "Create account",
|
||||
"have_account": "I already have an account",
|
||||
"no_account": "Create an account",
|
||||
"sign_out": "Sign out"
|
||||
"sign_out": "Sign out",
|
||||
"inventory": "Harvest in your house",
|
||||
"plant": "Plant",
|
||||
"crop_carrot": "carrot",
|
||||
"crop_tomato": "tomato",
|
||||
"crop_wheat": "wheat",
|
||||
"farm_empty": "That bed is empty",
|
||||
"farm_not_ripe": "Not ripe yet; water it and wait",
|
||||
"farm_not_withered": "Nothing to clear",
|
||||
"farm_occupied": "That bed already has a crop",
|
||||
"farm_unknown_crop": "Unknown crop",
|
||||
"farm_conflict": "The bed changed under you, try again",
|
||||
"farm_bad_action": "Nothing to do there",
|
||||
"farm_not_your_plot": "That bed belongs to someone else",
|
||||
"farm_no_home": "Claim a plot first",
|
||||
"farm_no_cell": "No bed there"
|
||||
}
|
||||
},
|
||||
"llm": {
|
||||
|
||||
@@ -19,6 +19,8 @@ export const OMNI_ATLAS_URL = '/world/omni/atlas.json';
|
||||
export const CASA_ATLAS_URL = '/world/tiles/casa-v1/atlas.json';
|
||||
/** Visual theme only: the same tile keys and collision metadata as casa-v1. */
|
||||
export const CRAFT_ATLAS_URL = '/world/tiles/craft-v1/atlas.json';
|
||||
/** The workshop's town art (ground, houses, props, crops), for the city. */
|
||||
export const VALE_ATLAS_URL = '/world/tiles/vale-v1/atlas.json';
|
||||
export const HOUSE_MAP_URL = '/world/house-v1.json';
|
||||
|
||||
export const ERR_ASSETS = {
|
||||
@@ -237,16 +239,17 @@ async function json<T>(url: string, code: string): Promise<T> {
|
||||
}
|
||||
|
||||
/** Loads both published atlases plus the generated HUD page. */
|
||||
export async function loadWorldAtlas(tileAtlasUrl = CRAFT_ATLAS_URL): Promise<WorldAtlas> {
|
||||
const [omni, casa] = await Promise.all([
|
||||
export async function loadWorldAtlas(tileAtlasUrl = CRAFT_ATLAS_URL, extraTileAtlasUrls: string[] = []): Promise<WorldAtlas> {
|
||||
const urls = [tileAtlasUrl, ...extraTileAtlasUrls];
|
||||
const [omni, ...tileAtlases] = await Promise.all([
|
||||
json<RawAtlas>(OMNI_ATLAS_URL, ERR_ASSETS.ATLAS_MISSING),
|
||||
json<RawAtlas>(tileAtlasUrl, ERR_ASSETS.ATLAS_MISSING),
|
||||
...urls.map((u) => json<RawAtlas>(u, ERR_ASSETS.ATLAS_MISSING)),
|
||||
]);
|
||||
const hud = buildHudAtlasPart();
|
||||
const merged = mergeAtlases([omni, casa, hud.part]);
|
||||
const merged = mergeAtlases([omni, ...tileAtlases, hud.part]);
|
||||
const pages = await Promise.all([
|
||||
...omni.pages.map((p) => bitmap(`/world/omni/${p}`)),
|
||||
...casa.pages.map((p) => bitmap(`${tileAtlasUrl.slice(0, tileAtlasUrl.lastIndexOf('/') + 1)}${p}`)),
|
||||
...tileAtlases.flatMap((atlas, i) => atlas.pages.map((p) => bitmap(`${urls[i].slice(0, urls[i].lastIndexOf('/') + 1)}${p}`))),
|
||||
hudBitmap(hud.draw),
|
||||
]);
|
||||
return { json: merged.json, pages };
|
||||
|
||||
@@ -54,7 +54,8 @@ export type CityInput =
|
||||
| { type: 'wave'; ent: number }
|
||||
| { type: 'sit'; object: number }
|
||||
| { type: 'idle' }
|
||||
| { type: 'enter'; portal: string };
|
||||
| { type: 'enter'; portal: string }
|
||||
| { type: 'farm'; cell?: string; tile?: [number, number]; action: 'water' | 'harvest' | 'clear' | 'plant'; crop?: string };
|
||||
|
||||
export interface CityChat {
|
||||
from_ent: number;
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
let cityMode = $state<{ city: string; server: string | null } | null>(null);
|
||||
let cityState = $state<{ region: string; ent: number; tick: number; interior: boolean } | null>(null);
|
||||
let cityRef = $state<{ say: (ent: number, text: string) => void; goTo: (tile: [number, number]) => Promise<void> } | null>(null);
|
||||
let cityPanelRef = $state<{ refreshHome: () => void } | null>(null);
|
||||
let cityCrop = $state("carrot");
|
||||
let canvasRef = $state<{ say: (ent: number, text: string) => void; focus: (ent: number) => void; frameHouse: () => void } | null>(null);
|
||||
let residents = $state<AgentRow[]>([]);
|
||||
let demoBusy = $state(false);
|
||||
@@ -127,6 +129,11 @@
|
||||
bind:this={cityRef}
|
||||
city={cityMode.city}
|
||||
server={cityMode.server}
|
||||
crop={cityCrop}
|
||||
onfarm={(r) => {
|
||||
if (r.ok) cityPanelRef?.refreshHome();
|
||||
else showToast("error", $t(`world.city.farm_${r.code.toLowerCase().replace(/^err_world_(farm_)?/, "")}`) as string);
|
||||
}}
|
||||
onstate={(s) => (cityState = s)}
|
||||
onfailed={(error) => {
|
||||
showToast("error", error);
|
||||
@@ -137,8 +144,11 @@
|
||||
{/key}
|
||||
</div>
|
||||
<CityPanel
|
||||
bind:this={cityPanelRef}
|
||||
city={cityMode}
|
||||
where={cityState}
|
||||
crop={cityCrop}
|
||||
oncrop={(c) => (cityCrop = c)}
|
||||
onenter={(c) => (cityMode = c)}
|
||||
onleave={() => {
|
||||
cityMode = null;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 352 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
Reference in New Issue
Block a user