import { html, css } from "lit"; import { customElement, property} from "lit/decorators.js"; import { BaseElement, defaultCss } from "components"; import { VMDeviceDBMixin, VMDeviceMixin } from "virtual_machine/base_device"; import { clamp, displayNumber, parseNumber, } from "utils"; import { SlotLogicType, SlotType, } from "ic10emu_wasm"; import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js"; import { VMDeviceCard } from "./card"; import { when } from "lit/directives/when.js"; export interface SlotModifyEvent { deviceID: number; slotIndex: number; } @customElement("vm-device-slot") export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) { private _slotIndex: number; get slotIndex() { return this._slotIndex; } @property({ type: Number }) set slotIndex(val: number) { this._slotIndex = val; this.unsubscribe((sub) => typeof sub === "object" && "slot" in sub); this.subscribe({ slot: val }); } constructor() { super(); this.subscribe("active-ic", "prefabName"); } static styles = [ ...defaultCss, css` .slot-card { --padding: var(--sl-spacing-x-small); } .slot-card::part(header) { padding: var(--sl-spacing-x-small); } .slot-card::part(base) { background-color: var(--sl-color-neutral-50); } .quantity-input sl-input::part(input) { width: 3rem; } .clear-occupant::part(base) { color: var(--sl-color-warning-500); } .clear-occupant::part(base):hover, .clear-occupant::part(base):focus { color: var(--sl-color-warning-400); } .clear-occupant::part(base):active { color: var(--sl-color-warning-500); } `, ]; slotOccupantImg(): string { const slot = this.slots[this.slotIndex]; if (typeof slot.occupant !== "undefined") { const hashLookup = (this.deviceDB ?? {}).names_by_hash ?? {}; const prefabName = hashLookup[slot.occupant.prefab_hash] ?? "UnknownHash"; return `img/stationpedia/${prefabName}.png`; } else { return `img/stationpedia/SlotIcon_${slot.typ}.png`; } } slotOccupantPrefabName(): string { const slot = this.slots[this.slotIndex]; if (typeof slot.occupant !== "undefined") { const hashLookup = (this.deviceDB ?? {}).names_by_hash ?? {}; const prefabName = hashLookup[slot.occupant.prefab_hash] ?? "UnknownHash"; return prefabName; } else { return undefined; } } slotOcccupantTemplate(): { name: string; typ: SlotType } | undefined { if (this.deviceDB) { const entry = this.deviceDB.db[this.prefabName]; return entry?.slots[this.slotIndex]; } else { return undefined; } } renderHeader() { const inputIdBase = `vmDeviceSlot${this.deviceID}Slot${this.slotIndex}Head`; const slot = this.slots[this.slotIndex]; const slotImg = this.slotOccupantImg(); const img = html``; const template = this.slotOcccupantTemplate(); const thisIsActiveIc = this.activeICId === this.deviceID; const enableQuantityInput = false; return html`
${this.slotIndex}
${img} ${when( typeof slot.occupant !== "undefined", () => html`
${slot.occupant.quantity}/${slot.occupant .max_quantity}
`, )}
${when( typeof slot.occupant !== "undefined", () => html` ${this.slotOccupantPrefabName()} `, () => html` ${template?.name} `, )}
Type:${slot.typ}
${when( typeof slot.occupant !== "undefined", () => html`
${enableQuantityInput ? html`
Max Quantity: ${slot.occupant.max_quantity}
` : ""}
`, () => html``, )}
`; } _handleSlotOccupantRemove() { window.VM.vm.removeDeviceSlotOccupant(this.deviceID, this.slotIndex); } _handleSlotClick(_e: Event) { this.dispatchEvent( new CustomEvent("device-modify-slot", { bubbles: true, composed: true, detail: { deviceID: this.deviceID, slotIndex: this.slotIndex }, }), ); } _handleSlotQuantityChange(e: Event) { const input = e.currentTarget as SlInput; const slot = this.slots[this.slotIndex]; const val = clamp(input.valueAsNumber, 1, slot.occupant.max_quantity); if ( !window.VM.vm.setDeviceSlotField( this.deviceID, this.slotIndex, "Quantity", val, true, ) ) { input.value = this.device .getSlotField(this.slotIndex, "Quantity") .toString(); } } renderFields() { const inputIdBase = `vmDeviceSlot${this.deviceID}Slot${this.slotIndex}Field`; const _fields = this.device.getSlotFields(this.slotIndex); const fields = Array.from(_fields.entries()); return html`
${fields.map( ([name, field], _index, _fields) => html` ${name} ${field.field_type} `, )}
`; } _handleChangeSlotField(e: CustomEvent) { const input = e.target as SlInput; const field = input.getAttribute("key")! as SlotLogicType; let val = parseNumber(input.value); if (field === "Quantity") { const slot = this.slots[this.slotIndex]; val = clamp(input.valueAsNumber, 1, slot.occupant.max_quantity); } window.VM.get().then((vm) => { if ( !vm.setDeviceSlotField(this.deviceID, this.slotIndex, field, val, true) ) { input.value = this.device .getSlotField(this.slotIndex, field) .toString(); } this.updateDevice(); }); } render() { return html`
${this.renderHeader()}
${this.renderFields()}
`; } }