Tooltips
This commit is contained in:
@@ -99,6 +99,8 @@ export function block_light_emission(block: BlockRegistry | undefined): number {
|
||||
|
||||
export interface BlockRegistry {
|
||||
id: string;
|
||||
// what players see, like "Iron Ore". made from the id when not set, see display_name in common/utils.ts
|
||||
name?: string;
|
||||
textures: BlockTextures;
|
||||
// the block model, engine:cube when not set. see common/block_models.ts
|
||||
model?: string;
|
||||
@@ -135,6 +137,8 @@ export interface BlockRegistry {
|
||||
|
||||
export interface ItemRegistry<T = unknown | undefined> {
|
||||
texture_id: string | ((item: ItemStack<T>) => string);
|
||||
// what players see, like "Iron Ingot". made from the id when not set, see display_name in common/utils.ts
|
||||
name?: string;
|
||||
block_id?: string;
|
||||
tool_type?: string;
|
||||
max_stack?: number;
|
||||
|
||||
@@ -5,6 +5,8 @@ export interface ItemData {
|
||||
id: string;
|
||||
count: number;
|
||||
data?: unknown;
|
||||
// what server scripts say about it (get_lore), only sent to players, never saved
|
||||
lore?: string;
|
||||
}
|
||||
|
||||
export class ItemStack<T = unknown | undefined> {
|
||||
@@ -12,6 +14,8 @@ export class ItemStack<T = unknown | undefined> {
|
||||
amount: number;
|
||||
max_amount: number;
|
||||
data?: T;
|
||||
// on clients, the lore the server's scripts gave it when it was last synced
|
||||
lore?: string;
|
||||
|
||||
constructor(type_id: string | string, amount: number = 1, max_amount?: number) {
|
||||
const item_info = EverythingRegistry.get<ItemRegistry>("items", type_id);
|
||||
@@ -27,6 +31,7 @@ export class ItemStack<T = unknown | undefined> {
|
||||
clone(): ItemStack {
|
||||
const item = new ItemStack(this.type_id, this.amount, this.max_amount);
|
||||
item.data = structuredClone(this.data);
|
||||
item.lore = this.lore;
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -36,11 +41,24 @@ export class ItemStack<T = unknown | undefined> {
|
||||
: { id: this.type_id, count: this.amount, data: this.data };
|
||||
}
|
||||
|
||||
// what players are sent: to_data with the lore from the item's get_lore, which only runs on the server
|
||||
to_synced_data(): ItemData {
|
||||
const data = this.to_data();
|
||||
const lore = EverythingRegistry.get<ItemRegistry>("items", this.type_id)?.get_lore?.(this);
|
||||
if (lore) {
|
||||
data.lore = lore;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static from_data(data: ItemData): ItemStack {
|
||||
const item = new ItemStack(data.id, data.count);
|
||||
if (data.data !== undefined) {
|
||||
item.data = data.data;
|
||||
}
|
||||
if (data.lore !== undefined) {
|
||||
item.lore = data.lore;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -140,6 +158,11 @@ export class Container {
|
||||
return this.#slots.map((slot) => slot.get_item()?.to_data() ?? null);
|
||||
}
|
||||
|
||||
// what players are sent, with lore, see ItemStack.to_synced_data
|
||||
to_synced_data(): (ItemData | null)[] {
|
||||
return this.#slots.map((slot) => slot.get_item()?.to_synced_data() ?? null);
|
||||
}
|
||||
|
||||
load(data: (ItemData | null)[]) {
|
||||
for (let i = 0; i < this.size; i += 1) {
|
||||
const item = data[i];
|
||||
|
||||
@@ -31,6 +31,7 @@ export interface ManifestJson {
|
||||
|
||||
export interface BlockJson {
|
||||
id: string;
|
||||
name?: string;
|
||||
textures: BlockTextures;
|
||||
model?: string;
|
||||
variants?: Record<string, BlockVariant>;
|
||||
@@ -66,6 +67,7 @@ export interface ItemJson {
|
||||
tool?: string;
|
||||
places?: string;
|
||||
max_stack?: number;
|
||||
name?: string;
|
||||
lore?: string;
|
||||
components?: Record<string, unknown>;
|
||||
}
|
||||
@@ -100,6 +102,7 @@ export interface GridRecipe {
|
||||
|
||||
export function block_to_json(block: BlockRegistry, has_item: boolean): BlockJson {
|
||||
const json: BlockJson = { id: block.id, textures: block.textures };
|
||||
if (block.name !== undefined) json.name = block.name;
|
||||
if (block.model !== undefined) json.model = block.model;
|
||||
if (block.variants !== undefined) json.variants = block.variants;
|
||||
if (block.render_layer && block.render_layer !== "solid") json.render_layer = block.render_layer;
|
||||
@@ -128,6 +131,7 @@ export function block_from_json(json: BlockJson): { block: BlockRegistry; has_it
|
||||
textures: json.textures,
|
||||
has_collision: json.collision ?? true,
|
||||
};
|
||||
if (json.name !== undefined) block.name = json.name;
|
||||
if (json.model !== undefined) block.model = json.model;
|
||||
if (json.variants !== undefined) block.variants = json.variants;
|
||||
const render_layer = json.render_layer ?? (json.transparent ? "translucent" : undefined);
|
||||
@@ -159,6 +163,7 @@ export function item_to_json(id: string, item: ItemRegistry): ItemJson {
|
||||
if (item.tool_type !== undefined) json.tool = item.tool_type;
|
||||
if (item.block_id !== undefined) json.places = item.block_id;
|
||||
if (item.max_stack !== undefined) json.max_stack = item.max_stack;
|
||||
if (item.name !== undefined) json.name = item.name;
|
||||
if (item.lore !== undefined) json.lore = item.lore;
|
||||
if (item.components) json.components = item.components;
|
||||
return json;
|
||||
@@ -169,6 +174,7 @@ export function item_from_json(json: ItemJson): ItemRegistry {
|
||||
if (json.tool !== undefined) item.tool_type = json.tool;
|
||||
if (json.places !== undefined) item.block_id = json.places;
|
||||
if (json.max_stack !== undefined) item.max_stack = json.max_stack;
|
||||
if (json.name !== undefined) item.name = json.name;
|
||||
if (json.lore !== undefined) item.lore = json.lore;
|
||||
if (json.components) item.components = json.components;
|
||||
return item;
|
||||
@@ -318,6 +324,7 @@ export function validate_block(json: unknown): Problems {
|
||||
problems.push(`${key} must be a whole number from 0 to 15`);
|
||||
}
|
||||
}
|
||||
problems.push(...validate_text(json.name, "name"));
|
||||
for (const key of ["cull_same", "transparent", "collision", "item", "interactive", "replaceable"]) {
|
||||
if (json[key] !== undefined && typeof json[key] !== "boolean") problems.push(`${key} must be true or false`);
|
||||
}
|
||||
@@ -413,6 +420,7 @@ export function validate_item(json: unknown): Problems {
|
||||
const problems = validate_id(json.id, "id");
|
||||
problems.push(...validate_id(json.texture, "texture"));
|
||||
if (json.places !== undefined) problems.push(...validate_id(json.places, "places"));
|
||||
problems.push(...validate_text(json.name, "name"), ...validate_text(json.lore, "lore"));
|
||||
if (json.max_stack !== undefined && (!Number.isInteger(json.max_stack) || (json.max_stack as number) < 1)) {
|
||||
problems.push("max_stack must be a positive whole number");
|
||||
}
|
||||
@@ -481,6 +489,11 @@ export function validate_ore(json: unknown): Problems {
|
||||
return problems;
|
||||
}
|
||||
|
||||
// optional text players see
|
||||
function validate_text(value: unknown, field: string): Problems {
|
||||
return value === undefined || (typeof value === "string" && value.length > 0) ? [] : [`${field} must be text`];
|
||||
}
|
||||
|
||||
function validate_id(value: unknown, field: string): Problems {
|
||||
return typeof value === "string" && ID_PATTERN.test(value) ? [] : [`${field} must be an id like "my_mod:thing"`];
|
||||
}
|
||||
|
||||
+11
-4
@@ -41,10 +41,17 @@ export function distance_point_point(ax: number, ay: number, az: number, bx: num
|
||||
export function register_block_item(block: BlockRegistry) {
|
||||
// cubes are drawn from the block's textures, anything else as a sprite
|
||||
const texture_id = block_item_texture(block) ?? cube_face_texture(block, "side");
|
||||
EverythingRegistry.register<ItemRegistry>("items", block.id, {
|
||||
texture_id,
|
||||
block_id: block.id,
|
||||
});
|
||||
const item: ItemRegistry = { texture_id, block_id: block.id };
|
||||
if (block.name !== undefined) item.name = block.name;
|
||||
EverythingRegistry.register<ItemRegistry>("items", block.id, item);
|
||||
}
|
||||
|
||||
// what players see an item called: its name, or one made from its id, so bworld:iron_ingot is "Iron Ingot"
|
||||
export function display_name(item_id: string): string {
|
||||
const name = EverythingRegistry.get<ItemRegistry>("items", item_id)?.name;
|
||||
if (name) return name;
|
||||
const words = (item_id.split(":")[1] ?? item_id).split("_").filter((word) => word.length > 0);
|
||||
return words.map((word) => word[0].toUpperCase() + word.slice(1)).join(" ");
|
||||
}
|
||||
|
||||
export function get_state_value(value: number, block_info: BlockRegistry, name: string) {
|
||||
|
||||
Reference in New Issue
Block a user