diff --git a/ic10emu_wasm/src/lib.rs b/ic10emu_wasm/src/lib.rs index bfb2b28..96b1643 100644 --- a/ic10emu_wasm/src/lib.rs +++ b/ic10emu_wasm/src/lib.rs @@ -3,7 +3,7 @@ mod utils; // mod types; use ic10emu::{ - errors::{ICError, VMError}, + errors::{ICError, TemplateError, VMError}, network::FrozenCableNetwork, vm::{ object::{ @@ -288,11 +288,42 @@ impl VMRef { frozen: FrozenObject, quantity: u32, ) -> Result, JsError> { + let Some(prefab) = frozen.obj_info.prefab.as_ref() else { + return Err(TemplateError::MissingPrefab.into()); + }; let obj_id = if let Some(obj) = frozen.obj_info.id.and_then(|id| self.vm.get_object(id)) { // TODO: we just assume if the ID is found that the frozen object passed is the same object.. obj.get_id() } else { - self.vm.add_object_frozen(frozen)? + // check to see if frozen is using the same prefab as current occupant + let obj_id = if let Some(occupant_id) = { + let obj = self.vm.get_object(id).ok_or(VMError::UnknownId(id))?; + let obj_ref = obj.borrow(); + let storage = obj_ref.as_storage().ok_or(VMError::NotStorage(id))?; + let slot = storage + .get_slot(index) + .ok_or(ICError::SlotIndexOutOfRange(index as f64))?; + slot.occupant.as_ref().map(|info| info.id) + } { + let occupant = self + .vm + .get_object(id) + .ok_or(VMError::UnknownId(occupant_id))?; + let occupant_ref = occupant.borrow(); + let occupant_prefab = occupant_ref.get_prefab(); + if prefab.as_str() == occupant_prefab.value.as_str() { + Some(*occupant_ref.get_id()) + } else { + None + } + } else { + None + }; + if let Some(obj_id) = obj_id { + obj_id + } else { + self.vm.add_object_frozen(frozen)? + } }; Ok(self .vm diff --git a/www/src/ts/session.ts b/www/src/ts/session.ts index 3037e3a..b98a87f 100644 --- a/www/src/ts/session.ts +++ b/www/src/ts/session.ts @@ -1,4 +1,4 @@ -import type { ICError, FrozenVM, SlotType } from "ic10emu_wasm"; +import type { ICError, FrozenVM, Class } from "ic10emu_wasm"; import { App } from "./app"; import { openDB, DBSchema } from "idb"; @@ -230,7 +230,7 @@ export class Session extends EventTarget { async saveLocal(name: string) { const state: VMState = { - vm: (await window.VM.get()).ic10vm.saveVMState(), + vm: await (await window.VM.get()).ic10vm.saveVMState(), activeIC: this.activeIC, }; const db = await this.openIndexDB(); diff --git a/www/src/ts/virtual_machine/device/add_device.ts b/www/src/ts/virtual_machine/device/add_device.ts index 56499ef..cafa83a 100644 --- a/www/src/ts/virtual_machine/device/add_device.ts +++ b/www/src/ts/virtual_machine/device/add_device.ts @@ -1,4 +1,3 @@ - import { html, css } from "lit"; import { customElement, query, state } from "lit/decorators.js"; import { BaseElement, defaultCss } from "components"; @@ -6,14 +5,18 @@ import { BaseElement, defaultCss } from "components"; import SlInput from "@shoelace-style/shoelace/dist/components/input/input.js"; import SlDrawer from "@shoelace-style/shoelace/dist/components/drawer/drawer.js"; -import type { DeviceDBEntry } from "virtual_machine/device_db"; import { repeat } from "lit/directives/repeat.js"; import { cache } from "lit/directives/cache.js"; import { default as uFuzzy } from "@leeoniya/ufuzzy"; import { when } from "lit/directives/when.js"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; import { VMTemplateDBMixin } from "virtual_machine/base_device"; +import { LogicInfo, ObjectTemplate, StructureInfo } from "ic10emu_wasm"; +type LogicableStrucutureTemplate = Extract< + ObjectTemplate, + { structure: StructureInfo; logic: LogicInfo } +>; @customElement("vm-add-device-button") export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) { @@ -35,27 +38,30 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) { @query("sl-drawer") drawer: SlDrawer; @query(".device-search-input") searchInput: SlInput; - private _structures: Map = new Map(); + private _structures: Map = new Map(); private _datapoints: [string, string][] = []; private _haystack: string[] = []; postDBSetUpdate(): void { this._structures = new Map( - Object.values(this.templateDB.db) - .filter((entry) => this.templateDB.structures.includes(entry.name), this) - .filter( - (entry) => this.templateDB.logic_enabled.includes(entry.name), - this, - ) - .map((entry) => [entry.name, entry]), + Array.from(this.templateDB.values()).flatMap((template) => { + if ("structure" in template && "logic" in template) { + return [[template.prefab.prefab_name, template]] as [ + string, + LogicableStrucutureTemplate, + ][]; + } else { + return [] as [string, LogicableStrucutureTemplate][]; + } + }), ); const datapoints: [string, string][] = []; for (const entry of this._structures.values()) { datapoints.push( - [entry.title, entry.name], - [entry.name, entry.name], - [entry.desc, entry.name], + [entry.prefab.name, entry.prefab.prefab_name], + [entry.prefab.prefab_name, entry.prefab.prefab_name], + [entry.prefab.desc, entry.prefab.prefab_name], ); } const haystack: string[] = datapoints.map((data) => data[0]); @@ -78,7 +84,7 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) { } private _searchResults: { - entry: DeviceDBEntry; + entry: LogicableStrucutureTemplate; haystackEntry: string; ranges: number[]; }[] = []; @@ -116,7 +122,7 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) { // return everything this._searchResults = [...this._structures.values()].map((st) => ({ entry: st, - haystackEntry: st.title, + haystackEntry: st.prefab.prefab_name, ranges: [], })); } @@ -143,28 +149,28 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) { const totalPages = Math.ceil((this._searchResults?.length ?? 0) / perPage); let pageKeys = Array.from({ length: totalPages }, (_, index) => index); const extra: { - entry: { title: string; name: string }; + entry: { prefab: { name: string; prefab_name: string } }; haystackEntry: string; ranges: number[]; }[] = []; if (this.page < totalPages - 1) { extra.push({ - entry: { title: "", name: this.filter }, + entry: { prefab: { name: "", prefab_name: this.filter } }, haystackEntry: "...", ranges: [], }); } return when( typeof this._searchResults !== "undefined" && - this._searchResults.length < 20, + this._searchResults.length < 20, () => repeat( this._searchResults ?? [], - (result) => result.entry.name, + (result) => result.entry.prefab.prefab_name, (result) => cache(html` @@ -183,46 +189,46 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) {
Page: ${pageKeys.map( - (key, index) => html` + (key, index) => html` ${key + 1}${index < totalPages - 1 ? "," : ""} `, - )} + )}
${[ - ...this._searchResults.slice( - perPage * this.page, - perPage * this.page + perPage, - ), - ...extra, - ].map((result) => { - let hay = result.haystackEntry.slice(0, 15); - if (result.haystackEntry.length > 15) hay += "..."; - const ranges = result.ranges.filter((pos) => pos < 20); - const key = result.entry.name; - return html` + ...this._searchResults.slice( + perPage * this.page, + perPage * this.page + perPage, + ), + ...extra, + ].map((result) => { + let hay = result.haystackEntry.slice(0, 15); + if (result.haystackEntry.length > 15) hay += "..."; + const ranges = result.ranges.filter((pos) => pos < 20); + const key = result.entry.prefab.prefab_name; + return html`
- ${result.entry.title} ( + ${result.entry.prefab.name} ( ${ranges.length - ? unsafeHTML(uFuzzy.highlight(hay, ranges)) - : hay} )
`; - })} + })}
`, @@ -278,8 +284,8 @@ export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) { slot="footer" variant="primary" @click=${() => { - this.drawer.hide(); - }} + this.drawer.hide(); + }} > Close diff --git a/www/src/ts/virtual_machine/device/slot_add_dialog.ts b/www/src/ts/virtual_machine/device/slot_add_dialog.ts index 0946397..9523868 100644 --- a/www/src/ts/virtual_machine/device/slot_add_dialog.ts +++ b/www/src/ts/virtual_machine/device/slot_add_dialog.ts @@ -2,13 +2,21 @@ import { html, css } from "lit"; import { customElement, property, query, state } from "lit/decorators.js"; import { BaseElement, defaultCss } from "components"; import { VMTemplateDBMixin } from "virtual_machine/base_device"; -import type { DeviceDB, DeviceDBEntry } from "virtual_machine/device_db"; import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js"; import SlDialog from "@shoelace-style/shoelace/dist/components/dialog/dialog.component.js"; import { VMDeviceCard } from "./card"; import { when } from "lit/directives/when.js"; import uFuzzy from "@leeoniya/ufuzzy"; -import { LogicField, LogicSlotType, SlotOccupantTemplate } from "ic10emu_wasm"; +import { + FrozenObject, + ItemInfo, + LogicField, + LogicSlotType, + ObjectInfo, + ObjectTemplate, +} from "ic10emu_wasm"; + +type SlotableItemTemplate = Extract; @customElement("vm-slot-add-dialog") export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) { @@ -30,8 +38,8 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) { `, ]; - private _items: Map = new Map(); - private _filteredItems: DeviceDBEntry[]; + private _items: Map = new Map(); + private _filteredItems: SlotableItemTemplate[]; private _datapoints: [string, string][] = []; private _haystack: string[] = []; @@ -47,42 +55,52 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) { } private _searchResults: { - entry: DeviceDBEntry; + entry: SlotableItemTemplate; haystackEntry: string; ranges: number[]; }[] = []; postDBSetUpdate(): void { this._items = new Map( - Object.values(this.templateDB.db) - .filter((entry) => this.templateDB.items.includes(entry.name), this) - .map((entry) => [entry.name, entry]), + Array.from(this.templateDB.values()).flatMap((template) => { + if ("item" in template) { + return [[template.prefab.prefab_name, template]] as [ + string, + SlotableItemTemplate, + ][]; + } else { + return [] as [string, SlotableItemTemplate][]; + } + }), ); this.setupSearch(); this.performSearch(); } - setupSearch() { - let filteredItemss = Array.from(this._items.values()); - if( typeof this.deviceID !== "undefined" && typeof this.slotIndex !== "undefined") { - const device = window.VM.vm.objects.get(this.deviceID); - const dbDevice = this.templateDB.db[device.prefabName] - const slot = dbDevice.slots[this.slotIndex] + let filteredItems = Array.from(this._items.values()); + if ( + typeof this.objectID !== "undefined" && + typeof this.slotIndex !== "undefined" + ) { + const obj = window.VM.vm.objects.get(this.objectID); + const template = obj.template; + const slot = "slots" in template ? template.slots[this.slotIndex] : null; const typ = slot.typ; if (typeof typ === "string" && typ !== "None") { - filteredItemss = Array.from(this._items.values()).filter(item => item.item.slotclass === typ); + filteredItems = Array.from(this._items.values()).filter( + (item) => item.item.slot_class === typ, + ); } - } - this._filteredItems= filteredItemss; + this._filteredItems = filteredItems; const datapoints: [string, string][] = []; for (const entry of this._filteredItems) { datapoints.push( - [entry.title, entry.name], - [entry.name, entry.name], - [entry.desc, entry.name], + [entry.prefab.name, entry.prefab.prefab_name], + [entry.prefab.prefab_name, entry.prefab.prefab_name], + [entry.prefab.desc, entry.prefab.prefab_name], ); } @@ -93,7 +111,6 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) { performSearch() { if (this._filter) { - const uf = new uFuzzy({}); const [_idxs, info, order] = uf.search( this._haystack, @@ -102,18 +119,17 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) { 1e3, ); - const filtered = order?.map((infoIdx) => ({ - name: this._datapoints[info.idx[infoIdx]][1], - haystackEntry: this._haystack[info.idx[infoIdx]], - ranges: info.ranges[infoIdx], - })) ?? []; + const filtered = + order?.map((infoIdx) => ({ + name: this._datapoints[info.idx[infoIdx]][1], + haystackEntry: this._haystack[info.idx[infoIdx]], + ranges: info.ranges[infoIdx], + })) ?? []; const uniqueNames = new Set(filtered.map((obj) => obj.name)); - const unique = [...uniqueNames].map( - (result) => { - return filtered.find((obj) => obj.name === result); - }, - ); + const unique = [...uniqueNames].map((result) => { + return filtered.find((obj) => obj.name === result); + }); this._searchResults = unique.map(({ name, haystackEntry, ranges }) => ({ entry: this._items.get(name)!, @@ -124,7 +140,7 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) { // return everything this._searchResults = [...this._filteredItems].map((st) => ({ entry: st, - haystackEntry: st.title, + haystackEntry: st.prefab.prefab_name, ranges: [], })); } @@ -133,63 +149,61 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) { renderSearchResults() { const enableNone = false; const none = html` -
- None -
+
+ None +
`; return html`
${enableNone ? none : ""} ${this._searchResults.map((result) => { - const imgSrc = `img/stationpedia/${result.entry.name}.png`; - const img = html` - - `; - return html` -
- ${img} -
${result.entry.title}
-
- `; - })} + const imgSrc = `img/stationpedia/${result.entry.prefab.prefab_name}.png`; + const img = html` + + `; + return html` +
+ ${img} +
${result.entry.prefab.name}
+
+ `; + })}
`; } _handleClickNone() { - window.VM.vm.removeSlotOccupant(this.deviceID, this.slotIndex); + window.VM.vm.removeSlotOccupant(this.objectID, this.slotIndex); this.hide(); } _handleClickItem(e: Event) { const div = e.currentTarget as HTMLDivElement; - const key = div.getAttribute("key"); - const entry = this.templateDB.db[key]; - const device = window.VM.vm.objects.get(this.deviceID); - const dbDevice = this.templateDB.db[device.prefabName] - const sorting = this.templateDB.enums["SortingClass"][entry.item.sorting ?? "Default"] ?? 0; - console.log("using entry", dbDevice); - const fields: { [key in LogicSlotType]?: LogicField } = Object.fromEntries( - Object.entries(dbDevice.slotlogic[this.slotIndex] ?? {}) - .map(([slt_s, field_type]) => { - let slt = slt_s as LogicSlotType; - let value = 0.0 - if (slt === "FilterType") { - value = this.templateDB.enums["GasType"][entry.item.filtertype] - } - const field: LogicField = { field_type, value}; - return [slt, field]; - }) - ); - fields["PrefabHash"] = { field_type: "Read", value: entry.hash }; - fields["MaxQuantity"] = { field_type: "Read", value: entry.item.maxquantity ?? 1.0 }; - fields["SortingClass"] = { field_type: "Read", value: sorting }; - fields["Quantity"] = { field_type: "Read", value: 1 }; + const key = parseInt(div.getAttribute("key")); + const entry = this.templateDB.get(key) as SlotableItemTemplate; + const obj = window.VM.vm.objects.get(this.objectID); + const dbTemplate = obj.template; + console.log("using entry", dbTemplate); - const template: SlotOccupantTemplate = { - fields - } - window.VM.vm.setSlotOccupant(this.deviceID, this.slotIndex, template); + const template: FrozenObject = { + obj_info: { + prefab: entry.prefab.prefab_name, + } as ObjectInfo, + database_template: true, + template: undefined, + }; + window.VM.vm.setSlotOccupant(this.objectID, this.slotIndex, template, 1); this.hide(); } @@ -197,30 +211,35 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) { @query(".device-search-input") searchInput: SlInput; render() { - const device = window.VM.vm.objects.get(this.deviceID); - const name = device?.name ?? device?.prefabName ?? ""; - const id = this.deviceID ?? 0; + const device = window.VM.vm.objects.get(this.objectID); + const name = device?.obj_info.name ?? device?.obj_info.prefab ?? ""; + const id = this.objectID ?? 0; return html` - + Search Items ${when( - typeof this.deviceID !== "undefined" && - typeof this.slotIndex !== "undefined", - () => html` + typeof this.objectID !== "undefined" && + typeof this.slotIndex !== "undefined", + () => html`
${this.renderSearchResults()}
`, - () => html``, - )} + () => html``, + )}
`; } @@ -239,15 +258,15 @@ export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) { } _handleDialogHide() { - this.deviceID = undefined; + this.objectID = undefined; this.slotIndex = undefined; } - @state() private deviceID: number; + @state() private objectID: number; @state() private slotIndex: number; - show(deviceID: number, slotIndex: number) { - this.deviceID = deviceID; + show(objectID: number, slotIndex: number) { + this.objectID = objectID; this.slotIndex = slotIndex; this.setupSearch(); this.performSearch();