diff --git a/ic10emu/src/device.rs b/ic10emu/src/device.rs index 199b0a7..e8f4f68 100644 --- a/ic10emu/src/device.rs +++ b/ic10emu/src/device.rs @@ -152,13 +152,17 @@ impl SlotOccupant { copy } - pub fn set_field( - &mut self, - field: SlotLogicType, - val: f64, - force: bool, - ) -> Result<(), ICError> { - if let Some(logic) = self.fields.get_mut(&field) { + pub fn set_field(&mut self, typ: SlotLogicType, val: f64, force: bool) -> Result<(), ICError> { + if (typ == SlotLogicType::Quantity) && force { + self.quantity = val as u32; + Ok(()) + } else if (typ == SlotLogicType::MaxQuantity) && force { + self.max_quantity = val as u32; + Ok(()) + } else if (typ == SlotLogicType::Damage) && force { + self.damage = val; + Ok(()) + } else if let Some(logic) = self.fields.get_mut(&typ) { match logic.field_type { FieldType::ReadWrite | FieldType::Write => { logic.value = val; @@ -169,13 +173,13 @@ impl SlotOccupant { logic.value = val; Ok(()) } else { - Err(ICError::ReadOnlyField(field.to_string())) + Err(ICError::ReadOnlyField(typ.to_string())) } } } } else if force { self.fields.insert( - field, + typ, LogicField { field_type: FieldType::ReadWrite, value: val, @@ -183,7 +187,7 @@ impl SlotOccupant { ); Ok(()) } else { - Err(ICError::ReadOnlyField(field.to_string())) + Err(ICError::ReadOnlyField(typ.to_string())) } } @@ -392,27 +396,20 @@ impl Slot { } } - pub fn set_field( - &mut self, - field: SlotLogicType, - val: f64, - force: bool, - ) -> Result<(), ICError> { + pub fn set_field(&mut self, typ: SlotLogicType, val: f64, force: bool) -> Result<(), ICError> { if matches!( - field, + typ, SlotLogicType::Occupied | SlotLogicType::OccupantHash - | SlotLogicType::Quantity - | SlotLogicType::MaxQuantity | SlotLogicType::Class | SlotLogicType::PrefabHash | SlotLogicType::SortingClass | SlotLogicType::ReferenceId ) { - return Err(ICError::ReadOnlyField(field.to_string())); + return Err(ICError::ReadOnlyField(typ.to_string())); } if let Some(occupant) = self.occupant.as_mut() { - occupant.set_field(field, val, force) + occupant.set_field(typ, val, force) } else { Err(ICError::SlotNotOccupied) } diff --git a/ic10emu/src/vm.rs b/ic10emu/src/vm.rs index d07e3c8..72a391c 100644 --- a/ic10emu/src/vm.rs +++ b/ic10emu/src/vm.rs @@ -768,11 +768,31 @@ impl VM { } let occupant = SlotOccupant::from_template(template, || self.id_space.next()); + if let Some(last) = slot.occupant.as_ref() { + self.id_space.free_id(last.id); + } slot.occupant = Some(occupant); Ok(()) } + pub fn remove_slot_occupant(&mut self, id: u32, index: usize) -> Result<(), VMError> { + let Some(device) = self.devices.get(&id) else { + return Err(VMError::UnknownId(id)); + }; + + let mut device_ref = device.borrow_mut(); + let slot = device_ref + .slots + .get_mut(index) + .ok_or(ICError::SlotIndexOutOfRange(index as f64))?; + if let Some(last) = slot.occupant.as_ref() { + self.id_space.free_id(last.id); + } + slot.occupant = None; + Ok(()) + } + pub fn save_vm_state(&self) -> FrozenVM { FrozenVM { ics: self.ics.values().map(|ic| ic.borrow().into()).collect(), diff --git a/ic10emu_wasm/src/lib.rs b/ic10emu_wasm/src/lib.rs index 7b5c173..571b795 100644 --- a/ic10emu_wasm/src/lib.rs +++ b/ic10emu_wasm/src/lib.rs @@ -495,6 +495,11 @@ impl VMRef { Ok(self.vm.borrow_mut().set_slot_occupant(id, index, template)?) } + #[wasm_bindgen(js_name = "removeSlotOccupant")] + pub fn remove_slot_occupant(&self, id: u32, index: usize) -> Result<(), JsError> { + Ok(self.vm.borrow_mut().remove_slot_occupant(id, index)?) + } + #[wasm_bindgen(js_name = "saveVMState", skip_typescript)] pub fn save_vm_state(&self) -> JsValue { let state = self.vm.borrow().save_vm_state(); diff --git a/www/src/ts/utils.ts b/www/src/ts/utils.ts index 62c94b6..9dcdbb7 100644 --- a/www/src/ts/utils.ts +++ b/www/src/ts/utils.ts @@ -242,3 +242,7 @@ export function parseIntWithHexOrBinary(s: string): number { } return parseInt(s); } + +export function clamp (val: number, min: number, max: number) { + return Math.min(Math.max(val, min), max); +} diff --git a/www/src/ts/virtual_machine/device/slot.ts b/www/src/ts/virtual_machine/device/slot.ts index e200652..76b6694 100644 --- a/www/src/ts/virtual_machine/device/slot.ts +++ b/www/src/ts/virtual_machine/device/slot.ts @@ -4,7 +4,7 @@ import { BaseElement, defaultCss } from "components"; import { VMDeviceDBMixin, VMDeviceMixin } from "virtual_machine/base_device"; import type { DeviceDB, DeviceDBEntry } from "virtual_machine/device_db"; import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js"; -import { displayNumber, parseIntWithHexOrBinary, parseNumber } from "utils"; +import { clamp, displayNumber, parseIntWithHexOrBinary, parseNumber } from "utils"; import { LogicType, Slot, @@ -147,6 +147,7 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) { .value=${slot.occupant.quantity.toString()} .min=${1} .max=${slot.occupant.max_quantity} + @sl-change=${this._handleSlotQuantityChange} >
Max Quantity: ${slot.occupant.max_quantity} @@ -170,6 +171,15 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) { ); } + _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); @@ -202,7 +212,11 @@ export class VMDeviceSlot extends VMDeviceMixin(VMDeviceDBMixin(BaseElement)) { _handleChangeSlotField(e: CustomEvent) { const input = e.target as SlInput; const field = input.getAttribute("key")! as SlotLogicType; - const val = parseNumber(input.value); + 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) 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 6ad43fa..4bc2079 100644 --- a/www/src/ts/virtual_machine/device/slot_add_dialog.ts +++ b/www/src/ts/virtual_machine/device/slot_add_dialog.ts @@ -31,6 +31,7 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) { ]; private _items: Map = new Map(); + private _filteredItems: DeviceDBEntry[]; private _datapoints: [string, string][] = []; private _haystack: string[] = []; @@ -57,33 +58,41 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) { .filter((entry) => this.deviceDB.items.includes(entry.name), this) .map((entry) => [entry.name, entry]), ); + this.setupSearch(); this.performSearch(); } - performSearch() { - if (this._filter) { + + setupSearch() { + let filteredItemss = Array.from(this._items.values()); + if( typeof this.deviceID !== "undefined" && typeof this.slotIndex !== "undefined") { const device = window.VM.vm.devices.get(this.deviceID); const dbDevice = this.deviceDB.db[device.prefabName] const slot = dbDevice.slots[this.slotIndex] const typ = slot.typ; - let filterdItems = Array.from(this._items.values()); - if (typ !== "None") { - filterdItems = Array.from(this._items.values()).filter(item => item.item.slotclass === typ); + if (typeof typ === "string" && typ !== "None") { + filteredItemss = Array.from(this._items.values()).filter(item => item.item.slotclass === typ); } - const datapoints: [string, string][] = []; - for (const entry of filterdItems) { - datapoints.push( - [entry.title, entry.name], - [entry.name, entry.name], - [entry.desc, entry.name], - ); - } + } + this._filteredItems= filteredItemss; + const datapoints: [string, string][] = []; + for (const entry of this._filteredItems) { + datapoints.push( + [entry.title, entry.name], + [entry.name, entry.name], + [entry.desc, entry.name], + ); + } - const haystack: string[] = datapoints.map((data) => data[0]); - this._datapoints = datapoints; - this._haystack = haystack; + const haystack: string[] = datapoints.map((data) => data[0]); + this._datapoints = datapoints; + this._haystack = haystack; + } + + performSearch() { + if (this._filter) { const uf = new uFuzzy({}); const [_idxs, info, order] = uf.search( @@ -113,7 +122,7 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) { })); } else { // return everything - this._searchResults = [...this._items.values()].map((st) => ({ + this._searchResults = [...this._filteredItems].map((st) => ({ entry: st, haystackEntry: st.title, ranges: [], @@ -130,7 +139,7 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) { ${this._searchResults.map((result) => { const imgSrc = `img/stationpedia/${result.entry.name}.png`; const img = html` - + `; return html`
@@ -144,7 +153,8 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) { } _handleClickNone() { - console.log("Clear Slot"); + window.VM.vm.removeDeviceSlotOccupant(this.deviceID, this.slotIndex); + this.hide(); } _handleClickItem(e: Event) { @@ -170,11 +180,13 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) { 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 template: SlotOccupantTemplate = { fields } window.VM.vm.setDeviceSlotOccupant(this.deviceID, this.slotIndex, template); + this.hide(); } @query("sl-dialog.slot-add-dialog") dialog: SlDialog; @@ -233,6 +245,8 @@ export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) { show(deviceID: number, slotIndex: number) { this.deviceID = deviceID; this.slotIndex = slotIndex; + this.setupSearch(); + this.performSearch(); this.dialog.show(); this.searchInput.select(); } diff --git a/www/src/ts/virtual_machine/index.ts b/www/src/ts/virtual_machine/index.ts index 9a5dfc1..dff3210 100644 --- a/www/src/ts/virtual_machine/index.ts +++ b/www/src/ts/virtual_machine/index.ts @@ -316,7 +316,7 @@ class VirtualMachine extends EventTarget { const device = this._devices.get(id); if (device) { try { - device.setSlotField(slot, field, val, false); + device.setSlotField(slot, field, val, force); this.updateDevice(device); return true; } catch (err) { @@ -411,6 +411,20 @@ class VirtualMachine extends EventTarget { return false; } + removeDeviceSlotOccupant(id: number, index: number): boolean { + const device = this._devices.get(id); + if (typeof device !== "undefined") { + try { + this.ic10vm.removeSlotOccupant(id, index); + this.updateDevice(device); + return true; + } catch (err) { + this.handleVmError(err); + } + } + return false; + } + saveVMState(): FrozenVM { return this.ic10vm.saveVMState(); }