refactor(vm, frontend): memory instructions, baseObject
- parse and map meory instructions - use FrozenObjectFull to propogate data out of VM to componates (far less wasm calls)
This commit is contained in:
@@ -328,9 +328,23 @@ interface TypedEventTargetInterface<EventMap> extends EventTarget {
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
): void;
|
||||
|
||||
removeEventListener<K extends keyof EventMap>(
|
||||
type: K,
|
||||
callback: (
|
||||
event: EventMap[K] extends Event ? EventMap[K] : never,
|
||||
) => EventMap[K] extends Event ? void : never,
|
||||
options?: boolean | AddEventListenerOptions,
|
||||
): void;
|
||||
|
||||
addEventListener(
|
||||
type: string,
|
||||
callback: EventListenerOrEventListenerObject | null,
|
||||
options?: EventListenerOptions | boolean,
|
||||
): void;
|
||||
|
||||
removeEventListener(
|
||||
type: string,
|
||||
callback: EventListenerOrEventListenerObject | null,
|
||||
options?: EventListenerOptions | boolean,
|
||||
): void;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ import type {
|
||||
ObjectID,
|
||||
TemplateDatabase,
|
||||
FrozenObjectFull,
|
||||
Class,
|
||||
LogicSlotType,
|
||||
SlotOccupantInfo,
|
||||
ICState,
|
||||
} from "ic10emu_wasm";
|
||||
import { crc32, structuralEqual } from "utils";
|
||||
import { LitElement, PropertyValueMap } from "lit";
|
||||
@@ -24,20 +28,22 @@ export declare class VMObjectMixinInterface {
|
||||
nameHash: number | null;
|
||||
prefabName: string | null;
|
||||
prefabHash: number | null;
|
||||
fields: Map<LogicType, LogicField>;
|
||||
slots: Slot[];
|
||||
reagents: Map<number, number>;
|
||||
connections: Connection[];
|
||||
icIP: number;
|
||||
icOpCount: number;
|
||||
icState: string;
|
||||
errors: ICError[];
|
||||
logicFields: Map<LogicType, LogicField> | null;
|
||||
slots: VmObjectSlotInfo[] | null;
|
||||
slotsCount: number | null;
|
||||
reagents: Map<number, number> | null;
|
||||
connections: Connection[] | null;
|
||||
icIP: number | null;
|
||||
icOpCount: number | null;
|
||||
icState: string | null;
|
||||
errors: ICError[] | null;
|
||||
registers: number[] | null;
|
||||
memory: number[] | null;
|
||||
aliases: Map<string, Operand> | null;
|
||||
defines: Map<string, string> | null;
|
||||
defines: Map<string, number> | null;
|
||||
numPins: number | null;
|
||||
pins: Map<number, ObjectID> | null;
|
||||
visibleDevices: ObjectID[] | null;
|
||||
_handleDeviceModified(e: CustomEvent): void;
|
||||
updateDevice(): void;
|
||||
updateIC(): void;
|
||||
@@ -54,23 +60,34 @@ export type VMObjectMixinSubscription =
|
||||
| "slots-count"
|
||||
| "reagents"
|
||||
| "connections"
|
||||
| "memory"
|
||||
| "ic"
|
||||
| "active-ic"
|
||||
| { field: LogicType }
|
||||
| { slot: number }
|
||||
| "visible-devices";
|
||||
|
||||
export interface VmObjectSlotInfo {
|
||||
parent: ObjectID;
|
||||
index: number;
|
||||
name: string;
|
||||
typ: Class;
|
||||
logicFields: Map<LogicSlotType, LogicField>;
|
||||
quantity: number;
|
||||
occupant: FrozenObjectFull | undefined;
|
||||
}
|
||||
|
||||
export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
superClass: T,
|
||||
) => {
|
||||
class VMObjectMixinClass extends superClass {
|
||||
private _deviceID: number;
|
||||
get deviceID() {
|
||||
return this._deviceID;
|
||||
private _objectID: number;
|
||||
get objectID() {
|
||||
return this._objectID;
|
||||
}
|
||||
@property({ type: Number })
|
||||
set deviceID(val: number) {
|
||||
this._deviceID = val;
|
||||
set objectID(val: number) {
|
||||
this._objectID = val;
|
||||
this.updateDevice();
|
||||
}
|
||||
|
||||
@@ -93,40 +110,42 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
|
||||
@state() name: string | null = null;
|
||||
@state() nameHash: number | null = null;
|
||||
@state() prefabName: string | null;
|
||||
@state() prefabHash: number | null;
|
||||
@state() fields: Map<LogicType, LogicField>;
|
||||
@state() slots: Slot[];
|
||||
@state() reagents: Map<number, number>;
|
||||
@state() connections: Connection[];
|
||||
@state() icIP: number;
|
||||
@state() icOpCount: number;
|
||||
@state() icState: string;
|
||||
@state() errors: ICError[];
|
||||
@state() registers: number[] | null;
|
||||
@state() memory: number[] | null;
|
||||
@state() aliases: Map<string, Operand> | null;
|
||||
@state() defines: Map<string, string> | null;
|
||||
@state() numPins: number | null;
|
||||
@state() pins: Map<number, ObjectID> | null;
|
||||
@state() prefabName: string | null = null;
|
||||
@state() prefabHash: number | null = null;
|
||||
@state() logicFields: Map<LogicType, LogicField> | null = null;
|
||||
@state() slots: VmObjectSlotInfo[] | null = null;
|
||||
@state() slotsCount: number | null = null;
|
||||
@state() reagents: Map<number, number> | null = null;
|
||||
@state() connections: Connection[] | null = null;
|
||||
@state() icIP: number | null = null;
|
||||
@state() icOpCount: number | null = null;
|
||||
@state() icState: ICState | null = null;
|
||||
@state() errors: ICError[] | null = null;
|
||||
@state() registers: number[] | null = null;
|
||||
@state() memory: number[] | null = null;
|
||||
@state() aliases: Map<string, Operand> | null = null;
|
||||
@state() defines: Map<string, number> | null = null;
|
||||
@state() numPins: number | null = null;
|
||||
@state() pins: Map<number, ObjectID> | null = null;
|
||||
@state() visibleDevices: ObjectID[] | null = null;
|
||||
|
||||
connectedCallback(): void {
|
||||
const root = super.connectedCallback();
|
||||
window.VM.get().then((vm) => {
|
||||
vm.addEventListener(
|
||||
"vm-device-modified",
|
||||
"vm-objects-modified",
|
||||
this._handleDeviceModified.bind(this),
|
||||
);
|
||||
vm.addEventListener(
|
||||
"vm-devices-update",
|
||||
"vm-objects-update",
|
||||
this._handleDevicesModified.bind(this),
|
||||
);
|
||||
vm.addEventListener(
|
||||
"vm-device-id-change",
|
||||
"vm-object-id-change",
|
||||
this._handleDeviceIdChange.bind(this),
|
||||
);
|
||||
vm.addEventListener(
|
||||
"vm-devices-removed",
|
||||
"vm-objects-removed",
|
||||
this._handleDevicesRemoved.bind(this),
|
||||
);
|
||||
});
|
||||
@@ -137,19 +156,19 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
disconnectedCallback(): void {
|
||||
window.VM.get().then((vm) => {
|
||||
vm.removeEventListener(
|
||||
"vm-device-modified",
|
||||
"vm-objects-modified",
|
||||
this._handleDeviceModified.bind(this),
|
||||
);
|
||||
vm.removeEventListener(
|
||||
"vm-devices-update",
|
||||
"vm-objects-update",
|
||||
this._handleDevicesModified.bind(this),
|
||||
);
|
||||
vm.removeEventListener(
|
||||
"vm-device-id-change",
|
||||
"vm-object-id-change",
|
||||
this._handleDeviceIdChange.bind(this),
|
||||
);
|
||||
vm.removeEventListener(
|
||||
"vm-devices-removed",
|
||||
"vm-objects-removed",
|
||||
this._handleDevicesRemoved.bind(this),
|
||||
);
|
||||
});
|
||||
@@ -158,7 +177,7 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
async _handleDeviceModified(e: CustomEvent) {
|
||||
const id = e.detail;
|
||||
const activeIcId = window.App.app.session.activeIC;
|
||||
if (this.deviceID === id) {
|
||||
if (this.objectID === id) {
|
||||
this.updateDevice();
|
||||
} else if (
|
||||
id === activeIcId &&
|
||||
@@ -168,7 +187,7 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
this.requestUpdate();
|
||||
} else if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = await window.VM.vm.visibleDeviceIds(
|
||||
this.deviceID,
|
||||
this.objectID,
|
||||
);
|
||||
if (visibleDevices.includes(id)) {
|
||||
this.updateDevice();
|
||||
@@ -180,7 +199,7 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
async _handleDevicesModified(e: CustomEvent<number[]>) {
|
||||
const activeIcId = window.App.app.session.activeIC;
|
||||
const ids = e.detail;
|
||||
if (ids.includes(this.deviceID)) {
|
||||
if (ids.includes(this.objectID)) {
|
||||
this.updateDevice();
|
||||
if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
this.requestUpdate();
|
||||
@@ -193,7 +212,7 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
this.requestUpdate();
|
||||
} else if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = await window.VM.vm.visibleDeviceIds(
|
||||
this.deviceID,
|
||||
this.objectID,
|
||||
);
|
||||
if (ids.some((id) => visibleDevices.includes(id))) {
|
||||
this.updateDevice();
|
||||
@@ -203,11 +222,11 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
}
|
||||
|
||||
async _handleDeviceIdChange(e: CustomEvent<{ old: number; new: number }>) {
|
||||
if (this.deviceID === e.detail.old) {
|
||||
this.deviceID = e.detail.new;
|
||||
if (this.objectID === e.detail.old) {
|
||||
this.objectID = e.detail.new;
|
||||
} else if (this.objectSubscriptions.includes("visible-devices")) {
|
||||
const visibleDevices = await window.VM.vm.visibleDeviceIds(
|
||||
this.deviceID,
|
||||
this.objectID,
|
||||
);
|
||||
if (
|
||||
visibleDevices.some(
|
||||
@@ -227,48 +246,86 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
}
|
||||
|
||||
updateDevice() {
|
||||
this.obj = window.VM.vm.objects.get(this.deviceID)!;
|
||||
this.obj = window.VM.vm.objects.get(this.objectID)!;
|
||||
|
||||
if (typeof this.obj === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
let newFields: Map<LogicType, LogicField> | null = null;
|
||||
if (
|
||||
this.objectSubscriptions.includes("slots") ||
|
||||
this.objectSubscriptions.includes("slots-count")
|
||||
this.objectSubscriptions.some(
|
||||
(sub) =>
|
||||
sub === "fields" || (typeof sub === "object" && "field" in sub),
|
||||
)
|
||||
) {
|
||||
const slotsOccupantInfo = this.obj.obj_info.slots;
|
||||
const logicValues =
|
||||
this.obj.obj_info.logic_values ?? new Map<LogicType, number>();
|
||||
const logicTemplate =
|
||||
"logic" in this.obj.template ? this.obj.template.logic : null;
|
||||
newFields = new Map(
|
||||
Array.from(logicTemplate?.logic_types.entries() ?? []).map(
|
||||
([lt, access]) => {
|
||||
let field: LogicField = {
|
||||
field_type: access,
|
||||
value: logicValues.get(lt) ?? 0,
|
||||
};
|
||||
return [lt, field];
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const visibleDevices = this.obj.obj_info.visible_devices ?? [];
|
||||
if (!structuralEqual(this.visibleDevices, visibleDevices)) {
|
||||
this.visibleDevices = visibleDevices;
|
||||
}
|
||||
|
||||
let newSlots: VmObjectSlotInfo[] | null = null;
|
||||
if (
|
||||
this.objectSubscriptions.some(
|
||||
(sub) =>
|
||||
sub === "slots" || (typeof sub === "object" && "slot" in sub),
|
||||
)
|
||||
) {
|
||||
const slotsOccupantInfo =
|
||||
this.obj.obj_info.slots ?? new Map<number, SlotOccupantInfo>();
|
||||
const slotsLogicValues =
|
||||
this.obj.obj_info.slot_logic_values ??
|
||||
new Map<number, Map<LogicSlotType, number>>();
|
||||
const logicTemplate =
|
||||
"logic" in this.obj.template ? this.obj.template.logic : null;
|
||||
const slotsTemplate =
|
||||
"slots" in this.obj.template ? this.obj.template.slots : [];
|
||||
let slots: Slot[] | null = null;
|
||||
if (slotsOccupantInfo.size !== 0) {
|
||||
slots = slotsTemplate.map((template, index) => {
|
||||
let slot = {
|
||||
parent: this.obj.obj_info.id,
|
||||
index: index,
|
||||
name: template.name,
|
||||
typ: template.typ,
|
||||
readable_logic: Array.from(
|
||||
logicTemplate?.logic_slot_types.get(index)?.entries() ?? [],
|
||||
)
|
||||
.filter(([_, val]) => val === "Read" || val === "ReadWrite")
|
||||
.map(([key, _]) => key),
|
||||
writeable_logic: Array.from(
|
||||
logicTemplate?.logic_slot_types.get(index)?.entries() ?? [],
|
||||
)
|
||||
.filter(([_, val]) => val === "Write" || val === "ReadWrite")
|
||||
.map(([key, _]) => key),
|
||||
occupant: slotsOccupantInfo.get(index),
|
||||
};
|
||||
return slot;
|
||||
});
|
||||
}
|
||||
|
||||
if (!structuralEqual(this.slots, slots)) {
|
||||
this.slots = slots;
|
||||
}
|
||||
newSlots = slotsTemplate.map((template, index) => {
|
||||
const fieldEntryInfos = Array.from(
|
||||
logicTemplate?.logic_slot_types.get(index).entries() ?? [],
|
||||
);
|
||||
const logicFields = new Map(
|
||||
fieldEntryInfos.map(([slt, access]) => {
|
||||
let field: LogicField = {
|
||||
field_type: access,
|
||||
value: slotsLogicValues.get(index)?.get(slt) ?? 0,
|
||||
};
|
||||
return [slt, field];
|
||||
}),
|
||||
);
|
||||
let occupantInfo = slotsOccupantInfo.get(index);
|
||||
let occupant =
|
||||
typeof occupantInfo !== "undefined"
|
||||
? window.VM.vm.objects.get(occupantInfo.id)
|
||||
: null;
|
||||
let slot: VmObjectSlotInfo = {
|
||||
parent: this.obj.obj_info.id,
|
||||
index: index,
|
||||
name: template.name,
|
||||
typ: template.typ,
|
||||
logicFields: logicFields,
|
||||
occupant: occupant,
|
||||
quantity: occupantInfo?.quantity ?? 0,
|
||||
};
|
||||
return slot;
|
||||
});
|
||||
}
|
||||
|
||||
for (const sub of this.objectSubscriptions) {
|
||||
@@ -293,22 +350,20 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
this.prefabHash = crc32(prefabName);
|
||||
}
|
||||
} else if (sub === "fields") {
|
||||
const fields = this.obj.obj_info.logic_values ?? null;
|
||||
const logicTemplate =
|
||||
"logic" in this.obj.template ? this.obj.template.logic : null;
|
||||
let logic_fields: Map<LogicType, LogicField> | null = null;
|
||||
if (fields !== null) {
|
||||
logic_fields = new Map();
|
||||
for (const [lt, val] of fields) {
|
||||
const access = logicTemplate?.logic_types.get(lt) ?? "Read";
|
||||
logic_fields.set(lt, {
|
||||
value: val,
|
||||
field_type: access,
|
||||
});
|
||||
}
|
||||
if (!structuralEqual(this.logicFields, newFields)) {
|
||||
this.logicFields = newFields;
|
||||
}
|
||||
if (!structuralEqual(this.fields, logic_fields)) {
|
||||
this.fields = logic_fields;
|
||||
} else if (sub === "slots") {
|
||||
if (!structuralEqual(this.slots, newSlots)) {
|
||||
this.slots = newSlots;
|
||||
this.slotsCount = newSlots.length;
|
||||
}
|
||||
} else if (sub === "slots-count") {
|
||||
const slotsTemplate =
|
||||
"slots" in this.obj.template ? this.obj.template.slots : [];
|
||||
const slotsCount = slotsTemplate.length;
|
||||
if (this.slotsCount !== slotsCount) {
|
||||
this.slotsCount = slotsCount;
|
||||
}
|
||||
} else if (sub === "reagents") {
|
||||
const reagents = this.obj.obj_info.reagents;
|
||||
@@ -367,10 +422,15 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
if (!structuralEqual(this.connections, connections)) {
|
||||
this.connections = connections;
|
||||
}
|
||||
} else if (sub === "memory") {
|
||||
const stack = this.obj.obj_info.memory ?? null;
|
||||
if (!structuralEqual(this.memory, stack)) {
|
||||
this.memory = stack;
|
||||
}
|
||||
} else if (sub === "ic") {
|
||||
if (
|
||||
typeof this.obj.obj_info.circuit !== "undefined" ||
|
||||
this.obj.obj_info.socketed_ic !== "undefined"
|
||||
typeof this.obj.obj_info.socketed_ic !== "undefined"
|
||||
) {
|
||||
this.updateIC();
|
||||
}
|
||||
@@ -382,21 +442,19 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
}
|
||||
} else {
|
||||
if ("field" in sub) {
|
||||
const fields = this.obj.obj_info.logic_values;
|
||||
if (this.fields.get(sub.field) !== fields.get(sub.field)) {
|
||||
this.fields = fields;
|
||||
if (this.logicFields.get(sub.field) !== newFields.get(sub.field)) {
|
||||
this.logicFields = newFields;
|
||||
}
|
||||
} else if ("slot" in sub) {
|
||||
const slots = this.obj.slots;
|
||||
if (
|
||||
typeof this.slots === "undefined" ||
|
||||
this.slots.length < sub.slot
|
||||
) {
|
||||
this.slots = slots;
|
||||
this.slots = newSlots;
|
||||
} else if (
|
||||
!structuralEqual(this.slots[sub.slot], slots[sub.slot])
|
||||
!structuralEqual(this.slots[sub.slot], newSlots[sub.slot])
|
||||
) {
|
||||
this.slots = slots;
|
||||
this.slots = newSlots;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -404,41 +462,42 @@ export const VMObjectMixin = <T extends Constructor<LitElement>>(
|
||||
}
|
||||
|
||||
updateIC() {
|
||||
const ip = this.obj.ip!;
|
||||
const ip = this.obj.obj_info.circuit?.instruction_pointer ?? null;
|
||||
if (this.icIP !== ip) {
|
||||
this.icIP = ip;
|
||||
}
|
||||
const opCount = this.obj.instructionCount!;
|
||||
const opCount =
|
||||
this.obj.obj_info.circuit?.yield_instruciton_count ?? null;
|
||||
if (this.icOpCount !== opCount) {
|
||||
this.icOpCount = opCount;
|
||||
}
|
||||
const state = this.obj.state!;
|
||||
const state = this.obj.obj_info.circuit?.state ?? null;
|
||||
if (this.icState !== state) {
|
||||
this.icState = state;
|
||||
}
|
||||
const errors = this.obj.program?.errors ?? null;
|
||||
const errors = this.obj.obj_info.compile_errors ?? null;
|
||||
if (!structuralEqual(this.errors, errors)) {
|
||||
this.errors = errors;
|
||||
}
|
||||
const registers = this.obj.registers ?? null;
|
||||
const registers = this.obj.obj_info.circuit?.registers ?? null;
|
||||
if (!structuralEqual(this.registers, registers)) {
|
||||
this.registers = registers;
|
||||
}
|
||||
const stack = this.obj.stack ?? null;
|
||||
if (!structuralEqual(this.memory, stack)) {
|
||||
this.memory = stack;
|
||||
}
|
||||
const aliases = this.obj.aliases ?? null;
|
||||
const aliases = this.obj.obj_info.circuit?.aliases ?? null;
|
||||
if (!structuralEqual(this.aliases, aliases)) {
|
||||
this.aliases = aliases;
|
||||
}
|
||||
const defines = this.obj.defines ?? null;
|
||||
const defines = this.obj.obj_info.circuit?.defines ?? null;
|
||||
if (!structuralEqual(this.defines, defines)) {
|
||||
this.defines = defines;
|
||||
}
|
||||
const pins = this.obj.pins ?? null;
|
||||
const pins = this.obj.obj_info.device_pins ?? new Map<number, number>();
|
||||
if (!structuralEqual(this.pins, pins)) {
|
||||
this.pins = pins;
|
||||
this.numPins =
|
||||
"device" in this.obj.template
|
||||
? this.obj.template.device.device_pins_length
|
||||
: Math.max(...Array.from(this.pins?.keys() ?? [0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -492,16 +551,16 @@ export const VMActiveICMixin = <T extends Constructor<LitElement>>(
|
||||
return VMActiveICMixinClass as Constructor<VMObjectMixinInterface> & T;
|
||||
};
|
||||
|
||||
export declare class VMDeviceDBMixinInterface {
|
||||
export declare class VMTemplateDBMixinInterface {
|
||||
templateDB: TemplateDatabase;
|
||||
_handleDeviceDBLoad(e: CustomEvent): void;
|
||||
postDBSetUpdate(): void;
|
||||
}
|
||||
|
||||
export const VMDeviceDBMixin = <T extends Constructor<LitElement>>(
|
||||
export const VMTemplateDBMixin = <T extends Constructor<LitElement>>(
|
||||
superClass: T,
|
||||
) => {
|
||||
class VMDeviceDBMixinClass extends superClass {
|
||||
class VMTemplateDBMixinClass extends superClass {
|
||||
connectedCallback(): void {
|
||||
const root = super.connectedCallback();
|
||||
window.VM.vm.addEventListener(
|
||||
@@ -540,5 +599,5 @@ export const VMDeviceDBMixin = <T extends Constructor<LitElement>>(
|
||||
}
|
||||
}
|
||||
|
||||
return VMDeviceDBMixinClass as Constructor<VMDeviceDBMixinInterface> & T;
|
||||
return VMTemplateDBMixinClass as Constructor<VMTemplateDBMixinInterface> & T;
|
||||
};
|
||||
|
||||
@@ -12,11 +12,11 @@ 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 { VMDeviceDBMixin } from "virtual_machine/base_device";
|
||||
import { VMTemplateDBMixin } from "virtual_machine/base_device";
|
||||
|
||||
|
||||
@customElement("vm-add-device-button")
|
||||
export class VMAddDeviceButton extends VMDeviceDBMixin(BaseElement) {
|
||||
export class VMAddDeviceButton extends VMTemplateDBMixin(BaseElement) {
|
||||
static styles = [
|
||||
...defaultCss,
|
||||
css`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { html, css, HTMLTemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMDeviceDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import { VMTemplateDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";
|
||||
import { parseIntWithHexOrBinary, parseNumber } from "utils";
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
|
||||
@@ -15,7 +15,9 @@ import { repeat } from "lit/directives/repeat.js";
|
||||
export type CardTab = "fields" | "slots" | "reagents" | "networks" | "pins";
|
||||
|
||||
@customElement("vm-device-card")
|
||||
export class VMDeviceCard extends VMDeviceDBMixin(VMObjectMixin(BaseElement)) {
|
||||
export class VMDeviceCard extends VMTemplateDBMixin(
|
||||
VMObjectMixin(BaseElement),
|
||||
) {
|
||||
image_err: boolean;
|
||||
|
||||
@property({ type: Boolean }) open: boolean;
|
||||
@@ -141,7 +143,17 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMObjectMixin(BaseElement)) {
|
||||
badges.push(html`<sl-badge variant="primary" pill pulse>db</sl-badge>`);
|
||||
}
|
||||
const activeIc = window.VM.vm.activeIC;
|
||||
activeIc?.pins?.forEach((id, index) => {
|
||||
|
||||
const numPins =
|
||||
"device" in activeIc?.template
|
||||
? activeIc.template.device.device_pins_length
|
||||
: Math.max(
|
||||
...Array.from(activeIc?.obj_info.device_pins?.keys() ?? [0]),
|
||||
);
|
||||
const pins = new Array(numPins)
|
||||
.fill(true)
|
||||
.map((_, index) => this.pins.get(index));
|
||||
pins.forEach((id, index) => {
|
||||
if (this.objectID == id) {
|
||||
badges.push(
|
||||
html`<sl-badge variant="success" pill>d${index}</sl-badge>`,
|
||||
@@ -150,31 +162,71 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMObjectMixin(BaseElement)) {
|
||||
}, this);
|
||||
return html`
|
||||
<sl-tooltip content="${this.prefabName}">
|
||||
<img class="image me-2" src="img/stationpedia/${this.prefabName}.png"
|
||||
onerror="this.src = '${VMDeviceCard.transparentImg}'" />
|
||||
<img
|
||||
class="image me-2"
|
||||
src="img/stationpedia/${this.prefabName}.png"
|
||||
onerror="this.src = '${VMDeviceCard.transparentImg}'"
|
||||
/>
|
||||
</sl-tooltip>
|
||||
<div class="header-name">
|
||||
<sl-input id="vmDeviceCard${this.objectID}Id" class="device-id me-1" size="small" pill value=${this.objectID}
|
||||
@sl-change=${this._handleChangeID}>
|
||||
<sl-input
|
||||
id="vmDeviceCard${this.objectID}Id"
|
||||
class="device-id me-1"
|
||||
size="small"
|
||||
pill
|
||||
value=${this.objectID.toString()}
|
||||
@sl-change=${this._handleChangeID}
|
||||
>
|
||||
<span slot="prefix">Id</span>
|
||||
<sl-copy-button slot="suffix" .value=${this.objectID}></sl-copy-button>
|
||||
<sl-copy-button
|
||||
slot="suffix"
|
||||
.value=${this.objectID.toString()}
|
||||
></sl-copy-button>
|
||||
</sl-input>
|
||||
<sl-input id="vmDeviceCard${this.objectID}Name" class="device-name me-1" size="small" pill
|
||||
placeholder=${this.prefabName} value=${this.name} @sl-change=${this._handleChangeName}>
|
||||
<sl-input
|
||||
id="vmDeviceCard${this.objectID}Name"
|
||||
class="device-name me-1"
|
||||
size="small"
|
||||
pill
|
||||
placeholder=${this.prefabName}
|
||||
value=${this.name}
|
||||
@sl-change=${this._handleChangeName}
|
||||
>
|
||||
<span slot="prefix">Name</span>
|
||||
<sl-copy-button slot="suffix" from="vmDeviceCard${this.objectID}Name.value"></sl-copy-button>
|
||||
<sl-copy-button
|
||||
slot="suffix"
|
||||
from="vmDeviceCard${this.objectID}Name.value"
|
||||
></sl-copy-button>
|
||||
</sl-input>
|
||||
<sl-input id="vmDeviceCard${this.objectID}NameHash" size="small" pill class="device-name-hash me-1"
|
||||
value="${this.nameHash}" readonly>
|
||||
<sl-input
|
||||
id="vmDeviceCard${this.objectID}NameHash"
|
||||
size="small"
|
||||
pill
|
||||
class="device-name-hash me-1"
|
||||
value="${this.nameHash.toString()}"
|
||||
readonly
|
||||
>
|
||||
<span slot="prefix">Hash</span>
|
||||
<sl-copy-button slot="suffix" from="vmDeviceCard${this.objectID}NameHash.value"></sl-copy-button>
|
||||
<sl-copy-button
|
||||
slot="suffix"
|
||||
from="vmDeviceCard${this.objectID}NameHash.value"
|
||||
></sl-copy-button>
|
||||
</sl-input>
|
||||
${badges.map((badge) => badge)}
|
||||
</div>
|
||||
<div class="ms-auto mt-auto mb-auto me-2">
|
||||
<sl-tooltip content=${thisIsActiveIc ? "Removing the selected Active IC is disabled" : "Remove Device" }>
|
||||
<sl-icon-button class="remove-button" name="trash" label="Remove Device" ?disabled=${thisIsActiveIc}
|
||||
@click=${this._handleDeviceRemoveButton}></sl-icon-button>
|
||||
<sl-tooltip
|
||||
content=${thisIsActiveIc
|
||||
? "Removing the selected Active IC is disabled"
|
||||
: "Remove Device"}
|
||||
>
|
||||
<sl-icon-button
|
||||
class="remove-button"
|
||||
name="trash"
|
||||
label="Remove Device"
|
||||
?disabled=${thisIsActiveIc}
|
||||
@click=${this._handleDeviceRemoveButton}
|
||||
></sl-icon-button>
|
||||
</sl-tooltip>
|
||||
</div>
|
||||
`;
|
||||
@@ -199,13 +251,14 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMObjectMixin(BaseElement)) {
|
||||
"slots",
|
||||
html`
|
||||
<div class="flex flex-row flex-wrap">
|
||||
${repeat(this.slots,
|
||||
(slot, index) => slot.typ + index.toString(),
|
||||
(_slot, index) => html`
|
||||
${repeat(
|
||||
this.slots,
|
||||
(slot, index) => slot.typ + index.toString(),
|
||||
(_slot, index) => html`
|
||||
<vm-device-slot .deviceID=${this.objectID} .slotIndex=${index} class-"flex flex-row max-w-lg mr-2 mb-2">
|
||||
</vm-device-slot>
|
||||
`,
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
`,
|
||||
);
|
||||
@@ -219,15 +272,26 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMObjectMixin(BaseElement)) {
|
||||
const vmNetworks = window.VM.vm.networks;
|
||||
const networks = this.connections.map((connection, index, _conns) => {
|
||||
const conn =
|
||||
typeof connection === "object" ? connection.CableNetwork : null;
|
||||
typeof connection === "object" && "CableNetwork" in connection
|
||||
? connection.CableNetwork
|
||||
: null;
|
||||
return html`
|
||||
<sl-select hoist placement="top" clearable key=${index} value=${conn?.net} ?disabled=${conn===null}
|
||||
@sl-change=${this._handleChangeConnection}>
|
||||
<sl-select
|
||||
hoist
|
||||
placement="top"
|
||||
clearable
|
||||
key=${index}
|
||||
value=${conn?.net}
|
||||
?disabled=${conn === null}
|
||||
@sl-change=${this._handleChangeConnection}
|
||||
>
|
||||
<span slot="prefix">Connection:${index} </span>
|
||||
${vmNetworks.map(
|
||||
(net) =>
|
||||
html`<sl-option value=${net.toString()}>Network ${net}</sl-option>`,
|
||||
)}
|
||||
(net) =>
|
||||
html`<sl-option value=${net.toString()}
|
||||
>Network ${net}</sl-option
|
||||
>`,
|
||||
)}
|
||||
<span slot="prefix"> ${conn?.typ} </span>
|
||||
</sl-select>
|
||||
`;
|
||||
@@ -243,7 +307,7 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMObjectMixin(BaseElement)) {
|
||||
"pins",
|
||||
html`<div class="pins">
|
||||
<vm-device-pins .deviceID=${this.objectID}></vm-device-pins>
|
||||
</div>`
|
||||
</div>`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -295,7 +359,9 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMObjectMixin(BaseElement)) {
|
||||
<sl-tab slot="nav" panel="slots">Slots</sl-tab>
|
||||
<sl-tab slot="nav" panel="reagents" disabled>Reagents</sl-tab>
|
||||
<sl-tab slot="nav" panel="networks">Networks</sl-tab>
|
||||
<sl-tab slot="nav" panel="pins" ?disabled=${!this.obj.pins}>Pins</sl-tab>
|
||||
<sl-tab slot="nav" panel="pins" ?disabled=${!this.numPins}
|
||||
>Pins</sl-tab
|
||||
>
|
||||
|
||||
<sl-tab-panel name="fields" active>
|
||||
${until(this.renderFields(), html`<sl-spinner></sl-spinner>`)}
|
||||
@@ -309,21 +375,37 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMObjectMixin(BaseElement)) {
|
||||
<sl-tab-panel name="networks">
|
||||
${until(this.renderNetworks(), html`<sl-spinner></sl-spinner>`)}
|
||||
</sl-tab-panel>
|
||||
<sl-tab-panel name="pins">${until(this.renderPins(), html`<sl-spinner></sl-spinner>`)} </sl-tab-panel>
|
||||
<sl-tab-panel name="pins"
|
||||
>${until(this.renderPins(), html`<sl-spinner></sl-spinner>`)}
|
||||
</sl-tab-panel>
|
||||
</sl-tab-group>
|
||||
</ic10-details>
|
||||
<sl-dialog class="remove-device-dialog" no-header @sl-request-close=${this._preventOverlayClose}>
|
||||
<sl-dialog
|
||||
class="remove-device-dialog"
|
||||
no-header
|
||||
@sl-request-close=${this._preventOverlayClose}
|
||||
>
|
||||
<div class="remove-dialog-body">
|
||||
<img class="dialog-image mt-auto mb-auto me-2" src="img/stationpedia/${this.prefabName}.png"
|
||||
onerror="this.src = '${VMDeviceCard.transparentImg}'" />
|
||||
<img
|
||||
class="dialog-image mt-auto mb-auto me-2"
|
||||
src="img/stationpedia/${this.prefabName}.png"
|
||||
onerror="this.src = '${VMDeviceCard.transparentImg}'"
|
||||
/>
|
||||
<div class="flex-g">
|
||||
<p><strong>Are you sure you want to remove this device?</strong></p>
|
||||
<span>Id ${this.objectID} : ${this.name ?? this.prefabName}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<sl-button variant="primary" autofocus @click=${this._closeRemoveDialog}>Close</sl-button>
|
||||
<sl-button variant="danger" @click=${this._removeDialogRemove}>Remove</sl-button>
|
||||
<sl-button
|
||||
variant="primary"
|
||||
autofocus
|
||||
@click=${this._closeRemoveDialog}
|
||||
>Close</sl-button
|
||||
>
|
||||
<sl-button variant="danger" @click=${this._removeDialogRemove}
|
||||
>Remove</sl-button
|
||||
>
|
||||
</div>
|
||||
</sl-dialog>
|
||||
`;
|
||||
@@ -387,5 +469,4 @@ export class VMDeviceCard extends VMDeviceDBMixin(VMObjectMixin(BaseElement)) {
|
||||
);
|
||||
this.updateDevice();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -151,11 +151,11 @@ export class VMDeviceList extends BaseElement {
|
||||
for (const device_id of this.devices) {
|
||||
const device = window.VM.vm.objects.get(device_id);
|
||||
if (device) {
|
||||
if (typeof device.name !== "undefined") {
|
||||
datapoints.push([device.name, device.id]);
|
||||
if (typeof device.obj_info.name !== "undefined") {
|
||||
datapoints.push([device.obj_info.name, device.obj_info.id]);
|
||||
}
|
||||
if (typeof device.prefabName !== "undefined") {
|
||||
datapoints.push([device.prefabName, device.id]);
|
||||
if (typeof device.obj_info.prefab !== "undefined") {
|
||||
datapoints.push([device.obj_info.prefab, device.obj_info.id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { html, css } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMDeviceDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import { VMTemplateDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import { displayNumber, parseNumber } from "utils";
|
||||
import type { LogicType } from "ic10emu_wasm";
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
|
||||
|
||||
@customElement("vm-device-fields")
|
||||
export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
export class VMDeviceSlot extends VMObjectMixin(VMTemplateDBMixin(BaseElement)) {
|
||||
constructor() {
|
||||
super();
|
||||
this.subscribe("fields");
|
||||
}
|
||||
|
||||
render() {
|
||||
const fields = Array.from(this.fields.entries());
|
||||
const fields = Array.from(this.logicFields.entries());
|
||||
const inputIdBase = `vmDeviceCard${this.objectID}Field`;
|
||||
return html`
|
||||
${fields.map(([name, field], _index, _fields) => {
|
||||
@@ -34,7 +34,7 @@ export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
const val = parseNumber(input.value);
|
||||
window.VM.get().then((vm) => {
|
||||
if (!vm.setObjectField(this.objectID, field, val, true)) {
|
||||
input.value = this.fields.get(field).value.toString();
|
||||
input.value = this.logicFields.get(field).value.toString();
|
||||
}
|
||||
this.updateDevice();
|
||||
});
|
||||
|
||||
@@ -5,11 +5,11 @@ import "./add_device"
|
||||
import "./slot_add_dialog"
|
||||
import "./slot"
|
||||
|
||||
import { VmDeviceTemplate } from "./template";
|
||||
import { VmObjectTemplate } from "./template";
|
||||
import { VMDeviceCard } from "./card";
|
||||
import { VMDeviceList } from "./device_list";
|
||||
import { VMAddDeviceButton } from "./add_device";
|
||||
import { VMSlotAddDialog } from "./slot_add_dialog";
|
||||
|
||||
export { VMDeviceCard, VmDeviceTemplate, VMDeviceList, VMAddDeviceButton, VMSlotAddDialog };
|
||||
export { VMDeviceCard, VmObjectTemplate as VmDeviceTemplate, VMDeviceList, VMAddDeviceButton, VMSlotAddDialog };
|
||||
|
||||
|
||||
@@ -1,34 +1,42 @@
|
||||
|
||||
import { html, css } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMDeviceDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import { VMTemplateDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";
|
||||
import { ObjectID } from "ic10emu_wasm";
|
||||
|
||||
@customElement("vm-device-pins")
|
||||
export class VMDevicePins extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
export class VMDevicePins extends VMObjectMixin(VMTemplateDBMixin(BaseElement)) {
|
||||
constructor() {
|
||||
super();
|
||||
this.subscribe("ic", "visible-devices");
|
||||
}
|
||||
|
||||
render() {
|
||||
const pins = this.pins;
|
||||
const visibleDevices = window.VM.vm.visibleDevices(this.objectID);
|
||||
const pins = new Array(this.numPins ?? 0)
|
||||
.fill(true)
|
||||
.map((_, index) => this.pins.get(index));
|
||||
const visibleDevices = (this.visibleDevices ?? []).map((id) => window.VM.vm.objects.get(id));
|
||||
const pinsHtml = pins?.map(
|
||||
(pin, index) =>
|
||||
html`
|
||||
<sl-select hoist placement="top" clearable key=${index} value=${pin} @sl-change=${this._handleChangePin}>
|
||||
<span slot="prefix">d${index}</span>
|
||||
${visibleDevices.map(
|
||||
(device, _index) =>
|
||||
html`
|
||||
<sl-option value=${device.id}>
|
||||
Device ${device.id} : ${device.name ?? device.prefabName}
|
||||
</sl-option>
|
||||
`,
|
||||
html` <sl-select
|
||||
hoist
|
||||
placement="top"
|
||||
clearable
|
||||
key=${index}
|
||||
value=${pin}
|
||||
@sl-change=${this._handleChangePin}
|
||||
>
|
||||
<span slot="prefix">d${index}</span>
|
||||
${visibleDevices.map(
|
||||
(device, _index) => html`
|
||||
<sl-option value=${device.obj_info.id.toString()}>
|
||||
Device ${device.obj_info.id} :
|
||||
${device.obj_info.name ?? device.obj_info.prefab}
|
||||
</sl-option>
|
||||
`,
|
||||
)}
|
||||
</sl-select>`,
|
||||
</sl-select>`,
|
||||
);
|
||||
return pinsHtml;
|
||||
}
|
||||
@@ -40,5 +48,4 @@ export class VMDevicePins extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
window.VM.get().then((vm) => vm.setDevicePin(this.objectID, pin, val));
|
||||
this.updateDevice();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import { html, css } from "lit";
|
||||
import { customElement, property} from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMDeviceDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import { VMTemplateDBMixin, VMObjectMixin } from "virtual_machine/base_device";
|
||||
import {
|
||||
clamp,
|
||||
crc32,
|
||||
displayNumber,
|
||||
parseNumber,
|
||||
} from "utils";
|
||||
import {
|
||||
LogicField,
|
||||
LogicSlotType,
|
||||
SlotType,
|
||||
SlotInfo,
|
||||
Class as SlotType,
|
||||
TemplateDatabase,
|
||||
} from "ic10emu_wasm";
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
|
||||
import { VMDeviceCard } from "./card";
|
||||
@@ -21,7 +25,7 @@ export interface SlotModifyEvent {
|
||||
}
|
||||
|
||||
@customElement("vm-device-slot")
|
||||
export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
export class VMDeviceSlot extends VMObjectMixin(VMTemplateDBMixin(BaseElement)) {
|
||||
private _slotIndex: number;
|
||||
|
||||
get slotIndex() {
|
||||
@@ -72,8 +76,7 @@ export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
slotOccupantImg(): string {
|
||||
const slot = this.slots[this.slotIndex];
|
||||
if (typeof slot.occupant !== "undefined") {
|
||||
const hashLookup = (this.templateDB ?? {}).names_by_hash ?? {};
|
||||
const prefabName = hashLookup[slot.occupant.prefab_hash] ?? "UnknownHash";
|
||||
const prefabName = slot.occupant.obj_info.prefab;
|
||||
return `img/stationpedia/${prefabName}.png`;
|
||||
} else {
|
||||
return `img/stationpedia/SlotIcon_${slot.typ}.png`;
|
||||
@@ -83,18 +86,16 @@ export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
slotOccupantPrefabName(): string {
|
||||
const slot = this.slots[this.slotIndex];
|
||||
if (typeof slot.occupant !== "undefined") {
|
||||
const hashLookup = (this.templateDB ?? {}).names_by_hash ?? {};
|
||||
const prefabName = hashLookup[slot.occupant.prefab_hash] ?? "UnknownHash";
|
||||
const prefabName = slot.occupant.obj_info.prefab;
|
||||
return prefabName;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
slotOcccupantTemplate(): { name: string; typ: SlotType } | undefined {
|
||||
if (this.templateDB) {
|
||||
const entry = this.templateDB.db[this.prefabName];
|
||||
return entry?.slots[this.slotIndex];
|
||||
slotOcccupantTemplate(): SlotInfo | undefined {
|
||||
if ("slots" in this.obj.template) {
|
||||
return this.obj.template.slots[this.slotIndex];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
@@ -139,10 +140,11 @@ export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
class="absolute bottom-0 right-0 mr-1 mb-1 text-xs
|
||||
text-neutral-200/90 font-mono bg-neutral-500/40 rounded pl-1 pr-1"
|
||||
>
|
||||
<small
|
||||
>${slot.occupant.quantity}/${slot.occupant
|
||||
.max_quantity}</small
|
||||
>
|
||||
<small>
|
||||
${slot.quantity}/${"item" in slot.occupant.template
|
||||
? slot.occupant.template.item.max_quantity
|
||||
: 1}
|
||||
</small>
|
||||
</div>`,
|
||||
)}
|
||||
<div></div>
|
||||
@@ -170,13 +172,20 @@ export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
? html` <sl-input
|
||||
type="number"
|
||||
size="small"
|
||||
.value=${slot.occupant.quantity.toString()}
|
||||
.value=${slot.quantity.toString()}
|
||||
.min=${1}
|
||||
.max=${slot.occupant.max_quantity}
|
||||
.max=${"item" in slot.occupant.template
|
||||
? slot.occupant.template.item.max_quantity
|
||||
: 1}
|
||||
@sl-change=${this._handleSlotQuantityChange}
|
||||
>
|
||||
<div slot="help-text">
|
||||
<span>Max Quantity: ${slot.occupant.max_quantity}</span>
|
||||
<span>
|
||||
Max Quantity:
|
||||
${"item" in slot.occupant.template
|
||||
? slot.occupant.template.item.max_quantity
|
||||
: 1}
|
||||
</span>
|
||||
</div>
|
||||
</sl-input>`
|
||||
: ""}
|
||||
@@ -218,7 +227,13 @@ export class VMDeviceSlot extends VMObjectMixin(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);
|
||||
const val = clamp(
|
||||
input.valueAsNumber,
|
||||
1,
|
||||
"item" in slot.occupant.template
|
||||
? slot.occupant.template.item.max_quantity
|
||||
: 1,
|
||||
);
|
||||
if (
|
||||
!window.VM.vm.setObjectSlotField(
|
||||
this.objectID,
|
||||
@@ -228,15 +243,15 @@ export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
true,
|
||||
)
|
||||
) {
|
||||
input.value = this.obj
|
||||
.getSlotField(this.slotIndex, "Quantity")
|
||||
.toString();
|
||||
input.value = this.slots[this.slotIndex].quantity.toString();
|
||||
}
|
||||
}
|
||||
|
||||
renderFields() {
|
||||
const inputIdBase = `vmDeviceSlot${this.objectID}Slot${this.slotIndex}Field`;
|
||||
const _fields = this.obj.getSlotFields(this.slotIndex);
|
||||
const _fields =
|
||||
this.slots[this.slotIndex].logicFields ??
|
||||
new Map<LogicSlotType, LogicField>();
|
||||
const fields = Array.from(_fields.entries());
|
||||
|
||||
return html`
|
||||
@@ -269,14 +284,23 @@ export class VMDeviceSlot extends VMObjectMixin(VMDeviceDBMixin(BaseElement)) {
|
||||
let val = parseNumber(input.value);
|
||||
if (field === "Quantity") {
|
||||
const slot = this.slots[this.slotIndex];
|
||||
val = clamp(input.valueAsNumber, 1, slot.occupant.max_quantity);
|
||||
val = clamp(
|
||||
input.valueAsNumber,
|
||||
1,
|
||||
"item" in slot.occupant.template
|
||||
? slot.occupant.template.item.max_quantity
|
||||
: 1,
|
||||
);
|
||||
}
|
||||
window.VM.get().then((vm) => {
|
||||
if (
|
||||
!vm.setObjectSlotField(this.objectID, this.slotIndex, field, val, true)
|
||||
) {
|
||||
input.value = this.obj
|
||||
.getSlotField(this.slotIndex, field)
|
||||
input.value = (
|
||||
this.slots[this.slotIndex].logicFields ??
|
||||
new Map<LogicSlotType, LogicField>()
|
||||
)
|
||||
.get(field)
|
||||
.toString();
|
||||
}
|
||||
this.updateDevice();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { html, css } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
import { VMDeviceDBMixin } from "virtual_machine/base_device";
|
||||
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";
|
||||
@@ -11,7 +11,7 @@ import uFuzzy from "@leeoniya/ufuzzy";
|
||||
import { LogicField, LogicSlotType, SlotOccupantTemplate } from "ic10emu_wasm";
|
||||
|
||||
@customElement("vm-slot-add-dialog")
|
||||
export class VMSlotAddDialog extends VMDeviceDBMixin(BaseElement) {
|
||||
export class VMSlotAddDialog extends VMTemplateDBMixin(BaseElement) {
|
||||
static styles = [
|
||||
...defaultCss,
|
||||
css`
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
import type {
|
||||
Connection,
|
||||
DeviceTemplate,
|
||||
ObjectTemplate,
|
||||
LogicField,
|
||||
LogicType,
|
||||
Slot,
|
||||
SlotTemplate,
|
||||
ConnectionCableNetwork,
|
||||
} from "ic10emu_wasm";
|
||||
import { html, css, HTMLTemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import { BaseElement, defaultCss } from "components";
|
||||
|
||||
import type { DeviceDB, DeviceDBEntry } from "virtual_machine/device_db";
|
||||
import { connectionFromDeviceDBConnection } from "./dbutils";
|
||||
import { displayNumber, parseNumber } from "utils";
|
||||
import { crc32, displayNumber, parseNumber } from "utils";
|
||||
import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js";
|
||||
import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js";
|
||||
import { VMDeviceCard } from "./card";
|
||||
import { VMDeviceDBMixin } from "virtual_machine/base_device";
|
||||
import { VMTemplateDBMixin } from "virtual_machine/base_device";
|
||||
|
||||
@customElement("vm-device-template")
|
||||
export class VmDeviceTemplate extends VMDeviceDBMixin(BaseElement) {
|
||||
export class VmObjectTemplate extends VMTemplateDBMixin(BaseElement) {
|
||||
|
||||
static styles = [
|
||||
...defaultCss,
|
||||
@@ -56,14 +53,14 @@ export class VmDeviceTemplate extends VMDeviceDBMixin(BaseElement) {
|
||||
|
||||
@state() fields: { [key in LogicType]?: LogicField };
|
||||
@state() slots: SlotTemplate[];
|
||||
@state() template: DeviceTemplate;
|
||||
@state() template: ObjectTemplate;
|
||||
@state() device_id: number | undefined;
|
||||
@state() device_name: string | undefined;
|
||||
@state() connections: Connection[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.templateDB = window.VM.vm.db;
|
||||
this.templateDB = window.VM.vm.templateDB;
|
||||
}
|
||||
|
||||
private _prefab_name: string;
|
||||
@@ -78,27 +75,27 @@ export class VmDeviceTemplate extends VMDeviceDBMixin(BaseElement) {
|
||||
this.setupState();
|
||||
}
|
||||
|
||||
get dbDevice(): DeviceDBEntry {
|
||||
return this.templateDB.db[this.prefab_name];
|
||||
get dbTemplate(): ObjectTemplate {
|
||||
return this.templateDB.get( crc32(this.prefab_name));
|
||||
}
|
||||
|
||||
setupState() {
|
||||
|
||||
this.fields = Object.fromEntries(
|
||||
Object.entries(this.dbDevice?.logic ?? {}).map(([lt, ft]) => {
|
||||
const value = lt === "PrefabHash" ? this.dbDevice.hash : 0.0;
|
||||
Object.entries(this.dbTemplate?.logic ?? {}).map(([lt, ft]) => {
|
||||
const value = lt === "PrefabHash" ? this.dbTemplate.prefab.prefab_hash : 0.0;
|
||||
return [lt, { field_type: ft, value } as LogicField];
|
||||
}),
|
||||
);
|
||||
|
||||
this.slots = (this.dbDevice?.slots ?? []).map(
|
||||
this.slots = (this.dbTemplate?.slots ?? []).map(
|
||||
(slot, _index) =>
|
||||
({
|
||||
typ: slot.typ,
|
||||
}) as SlotTemplate,
|
||||
);
|
||||
|
||||
const connections = Object.entries(this.dbDevice?.conn ?? {}).map(
|
||||
const connections = Object.entries(this.dbTemplate?.conn ?? {}).map(
|
||||
([index, conn]) =>
|
||||
[index, connectionFromDeviceDBConnection(conn)] as const,
|
||||
);
|
||||
@@ -203,7 +200,7 @@ export class VmDeviceTemplate extends VMDeviceDBMixin(BaseElement) {
|
||||
}
|
||||
|
||||
render() {
|
||||
const device = this.dbDevice;
|
||||
const device = this.dbTemplate;
|
||||
return html`
|
||||
<sl-card class="template-card">
|
||||
<div class="header h-20 w-96" slot="header">
|
||||
|
||||
@@ -116,8 +116,15 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
async updateObjects() {
|
||||
let updateFlag = false;
|
||||
const removedObjects = [];
|
||||
const objectIds = await this.ic10vm.objects;
|
||||
const frozenObjects = await this.ic10vm.freezeObjects(objectIds);
|
||||
let objectIds;
|
||||
let frozenObjects;
|
||||
try {
|
||||
objectIds = await this.ic10vm.objects;
|
||||
frozenObjects = await this.ic10vm.freezeObjects(objectIds);
|
||||
} catch (e) {
|
||||
this.handleVmError(e);
|
||||
return;
|
||||
}
|
||||
const updatedObjects = [];
|
||||
|
||||
for (const [index, id] of objectIds.entries()) {
|
||||
@@ -270,7 +277,14 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
if (save) this.app.session.save();
|
||||
}
|
||||
|
||||
updateDevice(id: number, save: boolean = true) {
|
||||
async updateDevice(id: number, save: boolean = true) {
|
||||
let frozen;
|
||||
try {
|
||||
frozen = await this.ic10vm.freezeObject(id);
|
||||
this._objects.set(id, frozen);
|
||||
} catch (e) {
|
||||
this.handleVmError(e);
|
||||
}
|
||||
const device = this._objects.get(id);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("vm-device-modified", { detail: device.obj_info.id }),
|
||||
@@ -528,7 +542,7 @@ class VirtualMachine extends (EventTarget as TypedEventTarget<VirtualMachinEvent
|
||||
}
|
||||
}
|
||||
|
||||
getPrograms() : [number, string][] {
|
||||
getPrograms(): [number, string][] {
|
||||
const programs: [number, string][] = Array.from(
|
||||
this._circuitHolders.entries(),
|
||||
).map(([id, ic]) => [id, ic.obj_info.source_code]);
|
||||
|
||||
Reference in New Issue
Block a user