From bd5695c41552d835a96b0cc7dd396c0249bf727d Mon Sep 17 00:00:00 2001 From: Rachel Powers <508861+Ryex@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:18:03 -0700 Subject: [PATCH] make non derived readonly fields writable from the ui --- ic10emu/src/interpreter.rs | 92 ++++-- ic10emu/src/lib.rs | 414 +++++++++++++++++++++++---- ic10emu_wasm/src/lib.rs | 8 +- www/src/ts/virtual_machine/device.ts | 12 +- www/src/ts/virtual_machine/index.ts | 12 +- 5 files changed, 432 insertions(+), 106 deletions(-) diff --git a/ic10emu/src/interpreter.rs b/ic10emu/src/interpreter.rs index 1020fa5..0492472 100644 --- a/ic10emu/src/interpreter.rs +++ b/ic10emu/src/interpreter.rs @@ -112,6 +112,8 @@ pub enum ICError { BadNetworkId(u32), #[error("channel index out of range '{0}'")] ChannelIndexOutOfRange(usize), + #[error("slot has no occupant")] + SlotNotOccupied } impl ICError { @@ -2177,7 +2179,7 @@ impl IC { match device { Some(device) => { let val = val.as_value(this, inst, 1)?; - device.borrow_mut().set_field(lt, val, vm)?; + device.borrow_mut().set_field(lt, val, vm, false)?; vm.set_modified(device_id); Ok(()) } @@ -2197,7 +2199,7 @@ impl IC { Some(device) => { let lt = lt.as_logic_type(this, inst, 2)?; let val = val.as_value(this, inst, 3)?; - device.borrow_mut().set_field(lt, val, vm)?; + device.borrow_mut().set_field(lt, val, vm, false)?; vm.set_modified(device_id as u32); Ok(()) } @@ -2217,7 +2219,9 @@ impl IC { let index = index.as_value(this, inst, 2)?; let slt = slt.as_slot_logic_type(this, inst, 3)?; let val = val.as_value(this, inst, 4)?; - device.borrow_mut().set_slot_field(index, slt, val, vm)?; + device + .borrow_mut() + .set_slot_field(index, slt, val, vm, false)?; vm.set_modified(device_id); Ok(()) } @@ -2231,7 +2235,7 @@ impl IC { let prefab = prefab.as_value(this, inst, 1)?; let lt = lt.as_logic_type(this, inst, 2)?; let val = val.as_value(this, inst, 3)?; - vm.set_batch_device_field(this.device, prefab, lt, val)?; + vm.set_batch_device_field(this.device, prefab, lt, val, false)?; Ok(()) } oprs => Err(ICError::mismatch_operands(oprs.len(), 3)), @@ -2242,7 +2246,14 @@ impl IC { let index = index.as_value(this, inst, 2)?; let slt = slt.as_slot_logic_type(this, inst, 3)?; let val = val.as_value(this, inst, 4)?; - vm.set_batch_device_slot_field(this.device, prefab, index, slt, val)?; + vm.set_batch_device_slot_field( + this.device, + prefab, + index, + slt, + val, + false, + )?; Ok(()) } oprs => Err(ICError::mismatch_operands(oprs.len(), 4)), @@ -2253,7 +2264,7 @@ impl IC { let name = name.as_value(this, inst, 2)?; let lt = lt.as_logic_type(this, inst, 3)?; let val = val.as_value(this, inst, 4)?; - vm.set_batch_name_device_field(this.device, prefab, name, lt, val)?; + vm.set_batch_name_device_field(this.device, prefab, name, lt, val, false)?; Ok(()) } oprs => Err(ICError::mismatch_operands(oprs.len(), 4)), @@ -2282,14 +2293,21 @@ impl IC { this.set_register(indirection, target, val)?; return Ok(()); } - let device = vm.get_device_same_network(this.device, device_id); - match device { - Some(device) => { - let val = device.borrow().get_field(lt, vm)?; - this.set_register(indirection, target, val)?; - Ok(()) + if lt == LogicType::LineNumber && this.device == device_id { + // HACK: we can't use device.get_field as that will try to reborrow our + // ic which will panic + this.set_register(indirection, target, this.ip as f64)?; + Ok(()) + } else { + let device = vm.get_device_same_network(this.device, device_id); + match device { + Some(device) => { + let val = device.borrow().get_field(lt, vm)?; + this.set_register(indirection, target, val)?; + Ok(()) + } + None => Err(UnknownDeviceID(device_id as f64)), } - None => Err(UnknownDeviceID(device_id as f64)), } } oprs => Err(ICError::mismatch_operands(oprs.len(), 3)), @@ -2304,15 +2322,22 @@ impl IC { if device_id >= u16::MAX as f64 || device_id < u16::MIN as f64 { return Err(DeviceIndexOutOfRange(device_id)); } - let device = vm.get_device_same_network(this.device, device_id as u32); - match device { - Some(device) => { - let lt = lt.as_logic_type(this, inst, 3)?; - let val = device.borrow().get_field(lt, vm)?; - this.set_register(indirection, target, val)?; - Ok(()) + let lt = lt.as_logic_type(this, inst, 3)?; + if lt == LogicType::LineNumber && this.device == device_id as u32 { + // HACK: we can't use device.get_field as that will try to reborrow our + // ic which will panic + this.set_register(indirection, target, this.ip as f64)?; + Ok(()) + } else { + let device = vm.get_device_same_network(this.device, device_id as u32); + match device { + Some(device) => { + let val = device.borrow().get_field(lt, vm)?; + this.set_register(indirection, target, val)?; + Ok(()) + } + None => Err(UnknownDeviceID(device_id)), } - None => Err(UnknownDeviceID(device_id)), } } oprs => Err(ICError::mismatch_operands(oprs.len(), 3)), @@ -2326,16 +2351,23 @@ impl IC { let (Some(device_id), _connection) = dev.as_device(this, inst, 2)? else { return Err(DeviceNotSet); }; - let device = vm.get_device_same_network(this.device, device_id); - match device { - Some(device) => { - let index = index.as_value(this, inst, 3)?; - let slt = slt.as_slot_logic_type(this, inst, 4)?; - let val = device.borrow().get_slot_field(index, slt, vm)?; - this.set_register(indirection, target, val)?; - Ok(()) + let slt = slt.as_slot_logic_type(this, inst, 4)?; + if slt == SlotLogicType::LineNumber && this.device == device_id { + // HACK: we can't use device.get_slot_field as that will try to reborrow our + // ic which will panic + this.set_register(indirection, target, this.ip as f64)?; + Ok(()) + } else { + let device = vm.get_device_same_network(this.device, device_id); + match device { + Some(device) => { + let index = index.as_value(this, inst, 3)?; + let val = device.borrow().get_slot_field(index, slt, vm)?; + this.set_register(indirection, target, val)?; + Ok(()) + } + None => Err(UnknownDeviceID(device_id as f64)), } - None => Err(UnknownDeviceID(device_id as f64)), } } oprs => Err(ICError::mismatch_operands(oprs.len(), 4)), diff --git a/ic10emu/src/lib.rs b/ic10emu/src/lib.rs index 810f6c6..0953386 100644 --- a/ic10emu/src/lib.rs +++ b/ic10emu/src/lib.rs @@ -62,6 +62,7 @@ pub struct SlotOccupant { pub prefab_hash: i32, pub quantity: u32, pub max_quantity: u32, + pub sorting_class: SortingClass, pub damage: f64, fields: HashMap, } @@ -90,6 +91,10 @@ impl SlotOccupant { .remove(&SlotLogicType::Damage) .map(|field| field.value) .unwrap_or(0.0), + sorting_class: fields + .remove(&SlotLogicType::SortingClass) + .map(|field| (field.value as u32).into()) + .unwrap_or(SortingClass::Default), fields, } } @@ -109,6 +114,7 @@ impl SlotOccupant { quantity: 1, max_quantity: 1, damage: 0.0, + sorting_class: SortingClass::Default, fields: HashMap::new(), } } @@ -142,13 +148,54 @@ impl SlotOccupant { self.fields.clone() } - /// slot field operations don't fail - pub fn set_field(&mut self, field: SlotLogicType, val: f64) { + pub fn set_field( + &mut self, + field: SlotLogicType, + val: f64, + force: bool, + ) -> Result<(), ICError> { if let Some(logic) = self.fields.get_mut(&field) { match logic.field_type { - FieldType::ReadWrite | FieldType::Write => logic.value = val, - _ => {} + FieldType::ReadWrite | FieldType::Write => { + logic.value = val; + Ok(()) + } + _ => { + if force { + logic.value = val; + Ok(()) + } else { + Err(ICError::ReadOnlyField(field.to_string())) + } + } } + } else if force { + self.fields.insert( + field, + LogicField { + field_type: FieldType::ReadWrite, + value: val, + }, + ); + Ok(()) + } else { + Err(ICError::ReadOnlyField(field.to_string())) + } + } + + pub fn can_logic_read(&self, field: SlotLogicType) -> bool { + if let Some(logic) = self.fields.get(&field) { + matches!(logic.field_type, FieldType::Read | FieldType::ReadWrite) + } else { + false + } + } + + pub fn can_logic_write(&self, field: SlotLogicType) -> bool { + if let Some(logic) = self.fields.get(&field) { + matches!(logic.field_type, FieldType::Write | FieldType::ReadWrite) + } else { + false } } } @@ -185,17 +232,6 @@ impl Slot { .as_ref() .map(|occupant| occupant.get_fields()) .unwrap_or_default(); - copy.insert( - SlotLogicType::ReferenceId, - LogicField { - field_type: FieldType::Read, - value: self - .occupant - .as_ref() - .map(|occupant| occupant.id as f64) - .unwrap_or(0.0), - }, - ); copy.insert( SlotLogicType::Occupied, LogicField { @@ -214,17 +250,6 @@ impl Slot { .unwrap_or(0.0), }, ); - copy.insert( - SlotLogicType::PrefabHash, - LogicField { - field_type: FieldType::Read, - value: self - .occupant - .as_ref() - .map(|occupant| occupant.prefab_hash as f64) - .unwrap_or(0.0), - }, - ); copy.insert( SlotLogicType::Quantity, LogicField { @@ -236,6 +261,24 @@ impl Slot { .unwrap_or(0.0), }, ); + copy.insert( + SlotLogicType::Damage, + LogicField { + field_type: FieldType::Read, + value: self + .occupant + .as_ref() + .map(|occupant| occupant.damage) + .unwrap_or(0.0), + }, + ); + copy.insert( + SlotLogicType::Class, + LogicField { + field_type: FieldType::Read, + value: self.typ as u32 as f64, + }, + ); copy.insert( SlotLogicType::MaxQuantity, LogicField { @@ -248,16 +291,41 @@ impl Slot { }, ); copy.insert( - SlotLogicType::Class, + SlotLogicType::PrefabHash, LogicField { field_type: FieldType::Read, - value: self.typ as u32 as f64, + value: self + .occupant + .as_ref() + .map(|occupant| occupant.prefab_hash as f64) + .unwrap_or(0.0), + }, + ); + copy.insert( + SlotLogicType::SortingClass, + LogicField { + field_type: FieldType::Read, + value: self + .occupant + .as_ref() + .map(|occupant| occupant.sorting_class as u32 as f64) + .unwrap_or(0.0), + }, + ); + copy.insert( + SlotLogicType::ReferenceId, + LogicField { + field_type: FieldType::Read, + value: self + .occupant + .as_ref() + .map(|occupant| occupant.id as f64) + .unwrap_or(0.0), }, ); copy } - /// the game returns 0.0 for all slotlogic types if they are not set pub fn get_field(&self, field: SlotLogicType) -> f64 { let fields = self.get_fields(); fields @@ -269,9 +337,80 @@ impl Slot { .unwrap_or(0.0) } - pub fn set_field(&mut self, field: SlotLogicType, val: f64) { + pub fn can_logic_read(&self, field: SlotLogicType) -> bool { + match field { + SlotLogicType::Pressure | SlotLogicType::Temperature | SlotLogicType::Volume => { + matches!( + self.typ, + SlotType::GasCanister | SlotType::LiquidCanister | SlotType::LiquidBottle + ) + } + SlotLogicType::Charge | SlotLogicType::ChargeRatio => { + matches!(self.typ, SlotType::Battery) + } + SlotLogicType::Open => matches!( + self.typ, + SlotType::Helmet | SlotType::Tool | SlotType::Appliance + ), + SlotLogicType::Lock => matches!(self.typ, SlotType::Helmet), + SlotLogicType::FilterType => matches!(self.typ, SlotType::GasFilter), + _ => { + if let Some(occupant) = self.occupant.as_ref() { + occupant.can_logic_read(field) + } else { + false + } + } + } + } + + pub fn can_logic_write(&self, field: SlotLogicType) -> bool { + match field { + SlotLogicType::Open => matches!( + self.typ, + SlotType::Helmet + | SlotType::GasCanister + | SlotType::LiquidCanister + | SlotType::LiquidBottle + ), + SlotLogicType::On => matches!( + self.typ, + SlotType::Helmet | SlotType::Tool | SlotType::Appliance + ), + SlotLogicType::Lock => matches!(self.typ, SlotType::Helmet), + _ => { + if let Some(occupant) = self.occupant.as_ref() { + occupant.can_logic_write(field) + } else { + false + } + } + } + } + + pub fn set_field( + &mut self, + field: SlotLogicType, + val: f64, + force: bool, + ) -> Result<(), ICError> { + if matches!( + field, + SlotLogicType::Occupied + | SlotLogicType::OccupantHash + | SlotLogicType::Quantity + | SlotLogicType::MaxQuantity + | SlotLogicType::Class + | SlotLogicType::PrefabHash + | SlotLogicType::SortingClass + | SlotLogicType::ReferenceId + ) { + return Err(ICError::ReadOnlyField(field.to_string())); + } if let Some(occupant) = self.occupant.as_mut() { - occupant.set_field(field, val); + occupant.set_field(field, val, force) + } else { + Err(ICError::SlotNotOccupied) } } } @@ -349,6 +488,55 @@ impl Connection { } } +#[derive( + Debug, + Default, + Clone, + Copy, + PartialEq, + Eq, + Hash, + strum_macros::Display, + EnumString, + EnumIter, + AsRefStr, + Serialize, + Deserialize, +)] +#[strum(serialize_all = "PascalCase")] +pub enum SortingClass { + #[default] + Default = 0, + Kits = 1, + Tools = 2, + Resources, + Food = 4, + Clothing, + Appliances, + Atmospherics, + Storage = 8, + Ores, + Ices, +} + +impl From for SortingClass { + fn from(value: u32) -> Self { + match value { + 1 => Self::Kits, + 2 => Self::Tools, + 3 => Self::Resources, + 4 => Self::Food, + 5 => Self::Clothing, + 6 => Self::Appliances, + 7 => Self::Atmospherics, + 8 => Self::Storage, + 9 => Self::Ores, + 10 => Self::Ices, + _ => Self::Default, + } + } +} + #[derive( Debug, Default, @@ -631,13 +819,19 @@ impl Device { }, ); } - copy.insert( - LogicType::Power, - LogicField { - field_type: FieldType::Read, - value: if self.has_power() { 1.0 } else { 0.0 }, - }, - ); + if self.has_power_state() { + copy.insert( + LogicType::Power, + LogicField { + field_type: FieldType::Read, + value: if self.has_power_connection() { + 1.0 + } else { + 0.0 + }, + }, + ); + } copy.insert( LogicType::ReferenceId, LogicField { @@ -668,6 +862,55 @@ impl Device { } } + pub fn can_logic_read(&self, field: LogicType) -> bool { + match field { + LogicType::ReferenceId => true, + LogicType::LineNumber | LogicType::Error if self.ic.is_some() => true, + LogicType::Power if self.has_power_state() => true, + _ => { + if let Some(logic) = self.fields.get(&field) { + matches!(logic.field_type, FieldType::Read | FieldType::ReadWrite) + } else { + false + } + } + } + } + + pub fn can_logic_write(&self, field: LogicType) -> bool { + match field { + LogicType::ReferenceId => false, + LogicType::LineNumber if self.ic.is_some() => true, + _ => { + if let Some(logic) = self.fields.get(&field) { + matches!(logic.field_type, FieldType::Write | FieldType::ReadWrite) + } else { + false + } + } + } + } + + pub fn can_slot_logic_read(&self, field: SlotLogicType, slot: usize) -> bool { + if self.slots.is_empty() { + return false; + } + let Some(slot) = self.slots.get(slot) else { + return false; + }; + slot.can_logic_read(field) + } + + pub fn can_slot_logic_write(&self, field: SlotLogicType, slot: usize) -> bool { + if self.slots.is_empty() { + return false; + } + let Some(slot) = self.slots.get(slot) else { + return false; + }; + slot.can_logic_write(field) + } + pub fn get_field(&self, typ: LogicType, vm: &VM) -> Result { if typ == LogicType::LineNumber && self.ic.is_some() { if let Ok(ic) = vm @@ -678,8 +921,9 @@ impl Device { { Ok(ic.ip as f64) } else { - // FIXME: the game succeeds in getting the correct line number - // when reading it's own IC + // HACK: the game succeeds in getting the correct line number + // when reading it's own IC, but we'll panic trying to do it here + // this is worked around in internal_step so just return 0 here Ok(0.0) } } else if let Some(field) = self.get_fields(vm).get(&typ) { @@ -693,9 +937,20 @@ impl Device { } } - pub fn set_field(&mut self, typ: LogicType, val: f64, vm: &VM) -> Result<(), ICError> { - if typ == LogicType::LineNumber && self.ic.is_some() { - // try borrow to set ip, we shoudl only fail if the ic is in us aka is is *our* ic + pub fn set_field( + &mut self, + typ: LogicType, + val: f64, + vm: &VM, + force: bool, + ) -> Result<(), ICError> { + if typ == LogicType::ReferenceId + || (typ == LogicType::Error && self.ic.is_some()) + || (typ == LogicType::Power && self.has_power_state()) + { + Err(ICError::ReadOnlyField(typ.to_string())) + } else if typ == LogicType::LineNumber && self.ic.is_some() { + // try borrow to set ip, we should only fail if the ic is in use aka is is *our* ic // in game trying to set your own ic's LineNumber appears to be a Nop so this is fine. if let Ok(mut ic) = vm .ics @@ -707,12 +962,24 @@ impl Device { } Ok(()) } else if let Some(field) = self.fields.get_mut(&typ) { - if field.field_type == FieldType::Write || field.field_type == FieldType::ReadWrite { + if field.field_type == FieldType::Write + || field.field_type == FieldType::ReadWrite + || force + { field.value = val; Ok(()) } else { Err(ICError::ReadOnlyField(typ.to_string())) } + } else if force { + self.fields.insert( + typ, + LogicField { + field_type: FieldType::ReadWrite, + value: val, + }, + ); + Ok(()) } else { Err(ICError::DeviceHasNoField(typ.to_string())) } @@ -737,8 +1004,9 @@ impl Device { { Ok(ic.ip as f64) } else { - // FIXME: the game succeeds in getting the correct line number - // when reading it's own IC + // HACK: the game succeeds in getting the correct line number + // when reading it's own IC, but we'll panic trying to do it here + // this is worked around in internal_step so just return 0 here Ok(0.0) } } else { @@ -772,8 +1040,9 @@ impl Device { }, ); } else { - // FIXME: the game succeeds in getting the correct line number - // when reading it's own IC + // HACK: the game succeeds in getting the correct line number + // when reading it's own IC, but we'll panic trying to do it here + // this is worked around in internal_step so just return 0 here fields.insert( SlotLogicType::LineNumber, LogicField { @@ -792,6 +1061,7 @@ impl Device { typ: grammar::SlotLogicType, val: f64, vm: &VM, + force: bool, ) -> Result<(), ICError> { let slot = self .slots @@ -814,8 +1084,7 @@ impl Device { } Ok(()) } else { - slot.set_field(typ, val); - Ok(()) + slot.set_field(typ, val, force) } } @@ -839,17 +1108,27 @@ impl Device { self.name = Some(name.to_owned()); } - pub fn has_power(&self) -> bool { + pub fn has_power_state(&self) -> bool { self.connections.iter().any(|conn| { - if let Connection::CableNetwork { net, typ } = conn { - net.is_some() - && matches!( - typ, - CableConnectionType::Power | CableConnectionType::PowerAndData - ) - } else { - false - } + matches!( + conn, + Connection::CableNetwork { + typ: CableConnectionType::Power | CableConnectionType::PowerAndData, + .. + } + ) + }) + } + + pub fn has_power_connection(&self) -> bool { + self.connections.iter().any(|conn| { + matches!( + conn, + Connection::CableNetwork { + net: Some(_), + typ: CableConnectionType::Power | CableConnectionType::PowerAndData, + } + ) }) } } @@ -1541,11 +1820,14 @@ impl VM { prefab: f64, typ: LogicType, val: f64, + write_readonly: bool, ) -> Result<(), ICError> { self.batch_device(source, prefab, None) .map(|device| { self.set_modified(device.borrow().id); - device.borrow_mut().set_field(typ, val, self) + device + .borrow_mut() + .set_field(typ, val, self, write_readonly) }) .try_collect() } @@ -1557,11 +1839,14 @@ impl VM { index: f64, typ: SlotLogicType, val: f64, + write_readonly: bool, ) -> Result<(), ICError> { self.batch_device(source, prefab, None) .map(|device| { self.set_modified(device.borrow().id); - device.borrow_mut().set_slot_field(index, typ, val, self) + device + .borrow_mut() + .set_slot_field(index, typ, val, self, write_readonly) }) .try_collect() } @@ -1573,11 +1858,14 @@ impl VM { name: f64, typ: LogicType, val: f64, + write_readonly: bool, ) -> Result<(), ICError> { self.batch_device(source, prefab, Some(name)) .map(|device| { self.set_modified(device.borrow().id); - device.borrow_mut().set_field(typ, val, self) + device + .borrow_mut() + .set_field(typ, val, self, write_readonly) }) .try_collect() } diff --git a/ic10emu_wasm/src/lib.rs b/ic10emu_wasm/src/lib.rs index de859ea..3aecd67 100644 --- a/ic10emu_wasm/src/lib.rs +++ b/ic10emu_wasm/src/lib.rs @@ -288,18 +288,18 @@ impl DeviceRef { } #[wasm_bindgen(js_name = "setField")] - pub fn set_field(&self, field: &str, value: f64) -> Result<(), JsError> { + pub fn set_field(&self, field: &str, value: f64, force: bool) -> Result<(), JsError> { let logic_typ = LogicType::from_str(field)?; let mut device_ref = self.device.borrow_mut(); - device_ref.set_field(logic_typ, value, &self.vm.borrow())?; + device_ref.set_field(logic_typ, value, &self.vm.borrow(), force)?; Ok(()) } #[wasm_bindgen(js_name = "setSlotField")] - pub fn set_slot_field(&self, slot: f64, field: &str, value: f64) -> Result<(), JsError> { + pub fn set_slot_field(&self, slot: f64, field: &str, value: f64, force: bool) -> Result<(), JsError> { let logic_typ = SlotLogicType::from_str(field)?; let mut device_ref = self.device.borrow_mut(); - device_ref.set_slot_field(slot, logic_typ, value, &self.vm.borrow())?; + device_ref.set_slot_field(slot, logic_typ, value, &self.vm.borrow(), force)?; Ok(()) } diff --git a/www/src/ts/virtual_machine/device.ts b/www/src/ts/virtual_machine/device.ts index e2c206f..b450490 100644 --- a/www/src/ts/virtual_machine/device.ts +++ b/www/src/ts/virtual_machine/device.ts @@ -430,18 +430,22 @@ export class VMDeviceCard extends VMDeviceMixin(BaseElement) { _handleChangeField(e: CustomEvent) { const input = e.target as SlInput; - const field = input.getAttribute("key")!; + const field = input.getAttribute("key")! as LogicType; const val = parseNumber(input.value); - window.VM?.setDeviceField(this.deviceID, field, val); + if (!window.VM?.setDeviceField(this.deviceID, field, val, true)) { + input.value = this.fields.get(field).value.toString(); + } this.updateDevice(); } _handleChangeSlotField(e: CustomEvent) { const input = e.target as SlInput; const slot = parseInt(input.getAttribute("slotIndex")!); - const field = input.getAttribute("key")!; + const field = input.getAttribute("key")! as SlotLogicType; const val = parseNumber(input.value); - window.VM?.setDeviceSlotField(this.deviceID, slot, field, val); + if (!window.VM?.setDeviceSlotField(this.deviceID, slot, field, val, true)) { + input.value = this.device.getSlotField(slot, field).toString(); + } this.updateDevice(); } diff --git a/www/src/ts/virtual_machine/index.ts b/www/src/ts/virtual_machine/index.ts index 4116899..b81e8f9 100644 --- a/www/src/ts/virtual_machine/index.ts +++ b/www/src/ts/virtual_machine/index.ts @@ -1,4 +1,4 @@ -import { DeviceRef, DeviceTemplate, VM, init } from "ic10emu_wasm"; +import { DeviceRef, DeviceTemplate, LogicType, SlotLogicType, VM, init } from "ic10emu_wasm"; import { DeviceDB } from "./device_db"; import "./base_device"; @@ -273,11 +273,12 @@ class VirtualMachine extends EventTarget { return false; } - setDeviceField(id: number, field: string, val: number): boolean { + setDeviceField(id: number, field: LogicType, val: number, force?: boolean): boolean { + force = force ?? false; const device = this._devices.get(id); if (device) { try { - device.setField(field, val); + device.setField(field, val, force); this.updateDevice(device); return true; } catch (err) { @@ -287,11 +288,12 @@ class VirtualMachine extends EventTarget { return false; } - setDeviceSlotField(id: number, slot: number, field: string, val: number): boolean { + setDeviceSlotField(id: number, slot: number, field: SlotLogicType, val: number, force?: boolean): boolean { + force = force ?? false; const device = this._devices.get(id); if (device) { try { - device.setSlotField(slot, field, val); + device.setSlotField(slot, field, val, false); this.updateDevice(device); return true; } catch (err) {