make non derived readonly fields writable from the ui
This commit is contained in:
@@ -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)),
|
||||
|
||||
@@ -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<SlotLogicType, LogicField>,
|
||||
}
|
||||
@@ -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<u32> 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<f64, ICError> {
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user