Skip to content
53 changes: 36 additions & 17 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -792,20 +792,38 @@ export type ActivityDataCommon = {
item: string; // item_id
count?: number | [number, number];
}[];
prying_data?: {
prying_nails?: boolean;
difficulty?: integer;
prying_level?: integer;
noisy?: boolean;
alarm?: boolean;
breakable?: boolean;
failure?: Translation;
};

result?: string;
message?: Translation;
sound?: Translation;
};

export type PryDataCommon = {
pry_quality?: integer; // default: -1
pry_bonus_mult?: integer; // default: 1
difficulty?: integer; // default: 1
noise?: integer; // default: 0
break_noise?: integer; // default: noise
alarm?: boolean; // default: false
breakable?: boolean; // default: false
pry_items?: ItemGroupEntry[];
break_items?: ItemGroupEntry[];
sound?: Translation;
break_sound?: Translation;
success_message?: Translation;
fail_message?: Translation;
break_message?: Translation;
};

export type TerrainPryData = PryDataCommon & {
new_ter_type?: string; // terrain_id, default: t_null
break_ter_type?: string; // terrain_id, default: t_null
};

export type FurniturePryData = PryDataCommon & {
new_furn_type?: string; // furniture_id, default: f_null
break_furn_type?: string; // furniture_id, default: f_null
};

export type MapDataCommon = {
color?: string | [string] | [string, string, string, string];
bgcolor?: string | [string] | [string, string, string, string];
Expand Down Expand Up @@ -849,10 +867,10 @@ export type Terrain = MapDataCommon & {
| { type: "cardreader" }
| { type: "effect_on_condition" };

oxytorch?: ActivityDataCommon & { result: string };
boltcut?: ActivityDataCommon & { result: string };
hacksaw?: ActivityDataCommon & { result: string };
prying?: ActivityDataCommon & { result: string };
oxytorch?: ActivityDataCommon;
boltcut?: ActivityDataCommon;
hacksaw?: ActivityDataCommon;
pry?: TerrainPryData;
};

export type Furniture = MapDataCommon & {
Expand Down Expand Up @@ -880,9 +898,10 @@ export type Furniture = MapDataCommon & {
bash?: MapBashInfo;
deconstruct?: MapDeconstructInfo;

oxytorch?: ActivityDataCommon & { result?: string };
boltcut?: ActivityDataCommon & { result?: string };
hacksaw?: ActivityDataCommon & { result?: string };
oxytorch?: ActivityDataCommon;
boltcut?: ActivityDataCommon;
hacksaw?: ActivityDataCommon;
pry?: FurniturePryData;

// TODO:
// open
Expand Down
7 changes: 7 additions & 0 deletions src/types/Furniture.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import FurnitureSpawnedIn from "./item/FurnitureSpawnedIn.svelte";
import LimitedList from "../LimitedList.svelte";
import HarvestedTo from "./item/HarvestedTo.svelte";
import { gameSingular } from "../i18n/game-locale";
import TerFurnPry from "./TerFurnPry.svelte";

const data = getContext<CBNData>("data");
const _context = "Terrain / Furniture";
Expand Down Expand Up @@ -111,6 +112,12 @@ const pseudo_items: string[] = asArray(item.crafting_pseudo_item);
<TerFurnActivity act={item.oxytorch} resultType="furniture" />
</dd>
{/if}
{#if item.pry}
<dt><ThingLink type="item_action" id="CROWBAR" showIcon={false} /></dt>
<dd>
<TerFurnPry act={item.pry} resultType="furniture" />
</dd>
{/if}
{#if deconstruct.length}
<dt>{t("Deconstruct", { _context })}</dt>
<dd>
Expand Down
46 changes: 46 additions & 0 deletions src/types/Furniture.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,52 @@ import { makeTestCBNData } from "../data.test-helpers";
import Furniture from "./Furniture.svelte";

describe("Furniture", () => {
it("shows pry details from the real furniture pry data", () => {
const data = makeTestCBNData([
{
type: "tool_quality",
id: "PRY",
name: "prying",
},
{
type: "furniture",
id: "f_coffin_c",
name: "closed coffin",
description: "A sealed test coffin.",
move_cost_mod: 0,
required_str: 0,
pry: {
pry_quality: 1,
noise: 12,
difficulty: 7,
new_furn_type: "f_coffin_o",
},
},
{
type: "furniture",
id: "f_coffin_o",
name: "open coffin",
description: "The lid has yielded.",
move_cost_mod: 0,
required_str: 0,
},
]);

const { getByText, queryByText } = render(WithData, {
Component: Furniture,
data,
item: data.byId("furniture", "f_coffin_c"),
});

expect(getByText("Requires")).toBeTruthy();
expect(getByText("prying")).toBeTruthy();
expect(getByText("Difficulty")).toBeTruthy();
expect(getByText("7")).toBeTruthy();
expect(getByText("Becomes")).toBeTruthy();
expect(getByText("open coffin")).toBeTruthy();
expect(queryByText("Duration")).toBeNull();
Comment thread
ushkinaz marked this conversation as resolved.
Outdated
});

it("shows furniture constructions from post_furniture", () => {
const data = makeTestCBNData([
{
Expand Down
65 changes: 31 additions & 34 deletions src/types/TerFurnActivity.svelte
Original file line number Diff line number Diff line change
@@ -1,58 +1,55 @@
<script lang="ts">
import { t } from "@transifex/native";
import type { CBNData } from "src/data";
import type { ActivityDataCommon } from "src/types";
import { getContext } from "svelte";
import ThingLink from "./ThingLink.svelte";
import { untrack } from "svelte";

interface Props {
act: ActivityDataCommon & { result?: string };
act: ActivityDataCommon;
resultType: "terrain" | "furniture";
}

let { act, resultType }: Props = $props();

const data = getContext<CBNData>("data");
function visibleResult(
value: ActivityDataCommon,
resultType: "terrain" | "furniture",
): string | undefined {
if (value.result === (resultType === "terrain" ? "t_null" : "f_null")) {
return undefined;
}
return value.result;
}

const activity = untrack(() => act);
const result = untrack(() => visibleResult(act, resultType));

const _context = "Terrain / Furniture";
const _comment = "activity (prying, hacksawing, etc.)";
const _comment = "activity (oxytorch, hacksaw, boltcut, etc.)";
</script>

<ul class="comma-separated">
{#each act.byproducts ?? [] as { item: i, count }}
<li>
<ThingLink
id={i}
type="item"
showIcon={false} />{#if typeof count === "number"}&nbsp;({count}){:else if Array.isArray(count)}&nbsp;({count[0]}–{count[1]}){/if}
</li>
{/each}
</ul>
<dl>
<dt>{t("Duration", { _context, _comment })}</dt>
<dd>{act.duration ?? "1 s"}</dd>
{#if act.prying_data}
<dt>{t("Difficulty", { _context, _comment })}</dt>
<dd>{act.prying_data.difficulty ?? 0}</dd>
<dt>{t("Requires", { _context, _comment })}</dt>
<dd>{activity.duration ?? "1 s"}</dd>
{#if result}
<dt>{t("Becomes", { _context, _comment })}</dt>
<dd>
<ThingLink id="PRY" type="tool_quality" showIcon={false} />
{act.prying_data.prying_level ?? 0}{#if act.prying_data.prying_nails}, <ThingLink
id="PRYING_NAIL"
type="tool_quality"
showIcon={false} />&nbsp;1{/if}
<ThingLink id={result} type={resultType} showIcon={false} />
</dd>
<dt>{t("Noisy", { _context, _comment })}</dt>
<dd>{act.prying_data.noisy ? t("Yes") : t("No")}</dd>
<dt>{t("Alarm", { _context, _comment })}</dt>
<dd>{act.prying_data.alarm ? t("Yes") : t("No")}</dd>
<dt>{t("Breakable", { _context, _comment })}</dt>
<dd>{act.prying_data.breakable ? t("Yes") : t("No")}</dd>
{/if}
{#if act.result && act.result !== "t_null"}
<dt>{t("Result", { _context, _comment })}</dt>
{#if activity.byproducts}
<dt>{t("Byproducts", { _context, _comment })}</dt>
<dd>
<ThingLink id={act.result} type={resultType} />
<ul class="comma-separated">
{#each activity.byproducts ?? [] as { item: i, count }}
<li>
<ThingLink
id={i}
type="item"
showIcon={false} />{#if typeof count === "number"}&nbsp;({count}){:else if Array.isArray(count)}&nbsp;({count[0]}–{count[1]}){/if}
</li>
{/each}
</ul>
</dd>
{/if}
</dl>
130 changes: 130 additions & 0 deletions src/types/TerFurnPry.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<script lang="ts">
import { t } from "@transifex/native";
import { formatPercent } from "../data";
import type {
FurniturePryData,
ItemGroupEntry,
TerrainPryData,
} from "src/types";
import { untrack } from "svelte";
import ThingLink from "./ThingLink.svelte";

type PryData = TerrainPryData | FurniturePryData;
type PryItemEntry = ItemGroupEntry & { item: string };

interface Props {
act: PryData;
resultType: "terrain" | "furniture";
}

let { act, resultType }: Props = $props();

const pry = untrack(() => act);
const result = untrack(() => visibleResult(act, resultType));
const pryItems = untrack(() => (act.pry_items ?? []).filter(isItemEntry));
const breakItems = untrack(() => (act.break_items ?? []).filter(isItemEntry));

function isItemEntry(entry: ItemGroupEntry): entry is PryItemEntry {
return "item" in entry;
}
Comment thread
ushkinaz marked this conversation as resolved.
Outdated

function formatAmount(
value?: number | [number, number],
hideOne = false,
): string | undefined {
if (value == null) {
return undefined;
}
if (typeof value === "number") {
return hideOne && value === 1 ? undefined : String(value);
}
if (value[0] === value[1]) {
return hideOne && value[0] === 1 ? undefined : String(value[0]);
}
return `${value[0]}–${value[1]}`;
}

function formatProbability(prob?: number): string | undefined {
if (prob == null || prob === 1) {
return undefined;
}
return prob > 1 ? `${prob}%` : formatPercent(prob);
}
Comment thread
ushkinaz marked this conversation as resolved.
Outdated

function visibleResult(
value: PryData,
resultType: "terrain" | "furniture",
): string | undefined {
let result = undefined;
if (resultType === "terrain") {
result = "new_ter_type" in value ? value.new_ter_type : undefined;
} else if (resultType === "furniture") {
result = "new_furn_type" in value ? value.new_furn_type : undefined;
}
if (result === (resultType === "terrain" ? "t_null" : "f_null")) {
return undefined;
}
return result;
}

const _context = "Terrain / Furniture";
const _comment = "prying";
</script>

<dl>
<dt>{t("Difficulty", { _context, _comment })}</dt>
<dd>{pry.difficulty ?? 1}</dd>
<dt>{t("Requires", { _context, _comment })}</dt>
<dd>
<ThingLink id="PRY" type="tool_quality" showIcon={false} />
{pry.pry_quality ?? 0}
</dd>
<dt>{t("Noisy", { _context, _comment })}</dt>
<dd>{(pry.noise ?? 0) > 0 ? t("Yes") : t("No")}</dd>
Comment thread
ushkinaz marked this conversation as resolved.
Outdated
<dt>{t("Alarm", { _context, _comment })}</dt>
<dd>{pry.alarm ? t("Yes") : t("No")}</dd>
<dt>{t("Breakable", { _context, _comment })}</dt>
<dd>{pry.breakable ? t("Yes") : t("No")}</dd>
Comment thread
ushkinaz marked this conversation as resolved.
{#if result}
<dt>{t("Becomes", { _context, _comment })}</dt>
<dd>
<ThingLink id={result} type={resultType} showIcon={false} />
</dd>
{/if}
Comment thread
ushkinaz marked this conversation as resolved.
{#if pryItems.length}
<dt>{t("Pry Items", { _context, _comment })}</dt>
<dd>
<ul class="comma-separated">
{#each pryItems as entry}
{@const amount =
formatAmount(entry.count, true) ?? formatAmount(entry.charges)}
{@const probability = formatProbability(entry.prob)}
<li>
<ThingLink
id={entry.item}
type="item"
showIcon={false} />{#if amount}&nbsp;({amount}){/if}{#if probability}&nbsp;({probability}){/if}
</li>
{/each}
</ul>
</dd>
{/if}
{#if breakItems.length}
<dt>{t("Break Items", { _context, _comment })}</dt>
<dd>
<ul class="comma-separated">
{#each breakItems as entry}
{@const amount =
formatAmount(entry.count, true) ?? formatAmount(entry.charges)}
{@const probability = formatProbability(entry.prob)}
<li>
<ThingLink
id={entry.item}
type="item"
showIcon={false} />{#if amount}&nbsp;({amount}){/if}{#if probability}&nbsp;({probability}){/if}
</li>
{/each}
</ul>
</dd>
{/if}
Comment thread
ushkinaz marked this conversation as resolved.
</dl>
5 changes: 3 additions & 2 deletions src/types/Terrain.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import TerFurnActivity from "./TerFurnActivity.svelte";
import TerrainSpawnedIn from "./item/TerrainSpawnedIn.svelte";
import HarvestedTo from "./item/HarvestedTo.svelte";
import { gameSingular } from "../i18n/game-locale";
import TerFurnPry from "./TerFurnPry.svelte";

const data = getContext<CBNData>("data");
const _context = "Terrain / Furniture";
Expand Down Expand Up @@ -87,10 +88,10 @@ const deconstructions = data
<TerFurnActivity act={item.oxytorch} resultType="terrain" />
</dd>
{/if}
{#if item.prying}
{#if item.pry}
<dt><ThingLink type="item_action" id="CROWBAR" showIcon={false} /></dt>
<dd>
<TerFurnActivity act={item.prying} resultType="terrain" />
<TerFurnPry act={item.pry} resultType="terrain" />
</dd>
{/if}
{#if deconstruct.length || item.deconstruct?.ter_set}
Expand Down
Loading
Loading