mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Add virtual stacker and painter, fix css
This commit is contained in:
@@ -1,50 +1,50 @@
|
||||
#ingame_HUD_SandboxController {
|
||||
position: absolute;
|
||||
background: $ingameHudBg;
|
||||
@include S(padding, 5px);
|
||||
@include S(bottom, 10px);
|
||||
@include S(left, 10px);
|
||||
|
||||
@include SuperSmallText;
|
||||
color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> label {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.plusMinus {
|
||||
@include S(margin-top, 4px);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
align-items: center;
|
||||
@include S(grid-gap, 4px);
|
||||
|
||||
button {
|
||||
@include PlainText;
|
||||
@include S(padding, 0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@include S(width, 15px);
|
||||
@include S(height, 15px);
|
||||
@include IncreasedClickArea(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.additionalOptions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(margin-top, 10px);
|
||||
button {
|
||||
@include S(margin-bottom, 2px);
|
||||
@include IncreasedClickArea(0px);
|
||||
@include SuperSmallText;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ingame_HUD_SandboxController {
|
||||
position: absolute;
|
||||
background: $ingameHudBg;
|
||||
@include S(padding, 5px);
|
||||
@include S(bottom, 10px);
|
||||
@include S(left, 10px);
|
||||
|
||||
@include SuperSmallText;
|
||||
color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> label {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.sandboxHint {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.plusMinus {
|
||||
@include S(margin-top, 4px);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
align-items: center;
|
||||
@include S(grid-gap, 4px);
|
||||
|
||||
button {
|
||||
@include PlainText;
|
||||
@include S(padding, 0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@include S(width, 15px);
|
||||
@include S(height, 15px);
|
||||
@include IncreasedClickArea(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.additionalOptions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(margin-top, 10px);
|
||||
button {
|
||||
@include S(margin-bottom, 2px);
|
||||
@include IncreasedClickArea(0px);
|
||||
@include SuperSmallText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ export const enumVirtualProcessorVariants = {
|
||||
rotater: "rotater",
|
||||
unstacker: "unstacker",
|
||||
shapecompare: "shapecompare",
|
||||
stacker: "stacker",
|
||||
painter: "painter",
|
||||
};
|
||||
|
||||
/** @enum {string} */
|
||||
@@ -20,6 +22,8 @@ export const enumVariantToGate = {
|
||||
[enumVirtualProcessorVariants.rotater]: enumLogicGateType.rotater,
|
||||
[enumVirtualProcessorVariants.unstacker]: enumLogicGateType.unstacker,
|
||||
[enumVirtualProcessorVariants.shapecompare]: enumLogicGateType.shapecompare,
|
||||
[enumVirtualProcessorVariants.stacker]: enumLogicGateType.stacker,
|
||||
[enumVirtualProcessorVariants.painter]: enumLogicGateType.painter,
|
||||
};
|
||||
|
||||
export class MetaVirtualProcessorBuilding extends MetaBuilding {
|
||||
@@ -54,6 +58,8 @@ export class MetaVirtualProcessorBuilding extends MetaBuilding {
|
||||
enumVirtualProcessorVariants.rotater,
|
||||
enumVirtualProcessorVariants.unstacker,
|
||||
enumVirtualProcessorVariants.analyzer,
|
||||
enumVirtualProcessorVariants.stacker,
|
||||
enumVirtualProcessorVariants.painter,
|
||||
enumVirtualProcessorVariants.shapecompare,
|
||||
];
|
||||
}
|
||||
@@ -130,6 +136,27 @@ export class MetaVirtualProcessorBuilding extends MetaBuilding {
|
||||
]);
|
||||
break;
|
||||
}
|
||||
case enumLogicGateType.stacker:
|
||||
case enumLogicGateType.painter: {
|
||||
pinComp.setSlots([
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
direction: enumDirection.top,
|
||||
type: enumPinSlotType.logicalEjector,
|
||||
},
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
direction: enumDirection.bottom,
|
||||
type: enumPinSlotType.logicalAcceptor,
|
||||
},
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
direction: enumDirection.right,
|
||||
type: enumPinSlotType.logicalAcceptor,
|
||||
},
|
||||
]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assertAlways("unknown logic gate type: " + gateType);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ export const enumLogicGateType = {
|
||||
unstacker: "unstacker",
|
||||
cutter: "cutter",
|
||||
shapecompare: "shapecompare",
|
||||
stacker: "stacker",
|
||||
painter: "painter",
|
||||
};
|
||||
|
||||
export class LogicGateComponent extends Component {
|
||||
|
||||
@@ -1,158 +1,158 @@
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
import { blueprintShape, UPGRADES } from "../../upgrades";
|
||||
import { enumNotificationType } from "./notifications";
|
||||
import { tutorialGoals } from "../../tutorial_goals";
|
||||
|
||||
export class HUDSandboxController extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(
|
||||
parent,
|
||||
"ingame_HUD_SandboxController",
|
||||
[],
|
||||
`
|
||||
<label>Sandbox Options</label>
|
||||
<span class="hint">Use F6 to toggle this overlay</span>
|
||||
|
||||
<div class="buttons">
|
||||
<div class="levelToggle plusMinus">
|
||||
<label>Level</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="upgradesBelt plusMinus">
|
||||
<label>Upgrades → Belt</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="upgradesExtraction plusMinus">
|
||||
<label>Upgrades → Extraction</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="upgradesProcessing plusMinus">
|
||||
<label>Upgrades → Processing</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="upgradesPainting plusMinus">
|
||||
<label>Upgrades → Painting</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="additionalOptions">
|
||||
<button class="styledButton giveBlueprints">Fill blueprint shapes</button>
|
||||
<button class="styledButton maxOutAll">Max out all</button>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
);
|
||||
|
||||
const bind = (selector, handler) => this.trackClicks(this.element.querySelector(selector), handler);
|
||||
|
||||
bind(".giveBlueprints", this.giveBlueprints);
|
||||
bind(".maxOutAll", this.maxOutAll);
|
||||
bind(".levelToggle .minus", () => this.modifyLevel(-1));
|
||||
bind(".levelToggle .plus", () => this.modifyLevel(1));
|
||||
|
||||
bind(".upgradesBelt .minus", () => this.modifyUpgrade("belt", -1));
|
||||
bind(".upgradesBelt .plus", () => this.modifyUpgrade("belt", 1));
|
||||
|
||||
bind(".upgradesExtraction .minus", () => this.modifyUpgrade("miner", -1));
|
||||
bind(".upgradesExtraction .plus", () => this.modifyUpgrade("miner", 1));
|
||||
|
||||
bind(".upgradesProcessing .minus", () => this.modifyUpgrade("processors", -1));
|
||||
bind(".upgradesProcessing .plus", () => this.modifyUpgrade("processors", 1));
|
||||
|
||||
bind(".upgradesPainting .minus", () => this.modifyUpgrade("painting", -1));
|
||||
bind(".upgradesPainting .plus", () => this.modifyUpgrade("painting", 1));
|
||||
}
|
||||
|
||||
giveBlueprints() {
|
||||
if (!this.root.hubGoals.storedShapes[blueprintShape]) {
|
||||
this.root.hubGoals.storedShapes[blueprintShape] = 0;
|
||||
}
|
||||
this.root.hubGoals.storedShapes[blueprintShape] += 1e9;
|
||||
}
|
||||
|
||||
maxOutAll() {
|
||||
this.modifyUpgrade("belt", 100);
|
||||
this.modifyUpgrade("miner", 100);
|
||||
this.modifyUpgrade("processors", 100);
|
||||
this.modifyUpgrade("painting", 100);
|
||||
}
|
||||
|
||||
modifyUpgrade(id, amount) {
|
||||
const handle = UPGRADES[id];
|
||||
const maxLevel = handle.tiers.length;
|
||||
|
||||
this.root.hubGoals.upgradeLevels[id] = Math.max(
|
||||
0,
|
||||
Math.min(maxLevel, (this.root.hubGoals.upgradeLevels[id] || 0) + amount)
|
||||
);
|
||||
|
||||
// Compute improvement
|
||||
let improvement = 1;
|
||||
for (let i = 0; i < this.root.hubGoals.upgradeLevels[id]; ++i) {
|
||||
improvement += handle.tiers[i].improvement;
|
||||
}
|
||||
this.root.hubGoals.upgradeImprovements[id] = improvement;
|
||||
this.root.signals.upgradePurchased.dispatch(id);
|
||||
this.root.hud.signals.notification.dispatch(
|
||||
"Upgrade '" + id + "' is now at tier " + (this.root.hubGoals.upgradeLevels[id] + 1),
|
||||
enumNotificationType.upgrade
|
||||
);
|
||||
}
|
||||
|
||||
modifyLevel(amount) {
|
||||
const hubGoals = this.root.hubGoals;
|
||||
hubGoals.level = Math.max(1, hubGoals.level + amount);
|
||||
hubGoals.createNextGoal();
|
||||
|
||||
// Clear all shapes of this level
|
||||
hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;
|
||||
|
||||
this.root.hud.parts.pinnedShapes.rerenderFull();
|
||||
|
||||
// Compute gained rewards
|
||||
hubGoals.gainedRewards = {};
|
||||
for (let i = 0; i < hubGoals.level - 1; ++i) {
|
||||
if (i < tutorialGoals.length) {
|
||||
const reward = tutorialGoals[i].reward;
|
||||
hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.root.hud.signals.notification.dispatch(
|
||||
"Changed level to " + hubGoals.level,
|
||||
enumNotificationType.upgrade
|
||||
);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
// Allow toggling the controller overlay
|
||||
this.root.gameState.inputReciever.keydown.add(key => {
|
||||
if (key.keyCode === 117) {
|
||||
// F6
|
||||
this.toggle();
|
||||
}
|
||||
});
|
||||
|
||||
this.visible = !G_IS_DEV;
|
||||
this.domAttach = new DynamicDomAttach(this.root, this.element);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.visible = !this.visible;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.domAttach.update(this.visible);
|
||||
}
|
||||
}
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
import { blueprintShape, UPGRADES } from "../../upgrades";
|
||||
import { enumNotificationType } from "./notifications";
|
||||
import { tutorialGoals } from "../../tutorial_goals";
|
||||
|
||||
export class HUDSandboxController extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(
|
||||
parent,
|
||||
"ingame_HUD_SandboxController",
|
||||
[],
|
||||
`
|
||||
<label>Sandbox Options</label>
|
||||
<span class="sandboxHint">Use F6 to toggle this overlay</span>
|
||||
|
||||
<div class="buttons">
|
||||
<div class="levelToggle plusMinus">
|
||||
<label>Level</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="upgradesBelt plusMinus">
|
||||
<label>Upgrades → Belt</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="upgradesExtraction plusMinus">
|
||||
<label>Upgrades → Extraction</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="upgradesProcessing plusMinus">
|
||||
<label>Upgrades → Processing</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="upgradesPainting plusMinus">
|
||||
<label>Upgrades → Painting</label>
|
||||
<button class="styledButton minus">-</button>
|
||||
<button class="styledButton plus">+</button>
|
||||
</div>
|
||||
|
||||
<div class="additionalOptions">
|
||||
<button class="styledButton giveBlueprints">Fill blueprint shapes</button>
|
||||
<button class="styledButton maxOutAll">Max out all</button>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
);
|
||||
|
||||
const bind = (selector, handler) => this.trackClicks(this.element.querySelector(selector), handler);
|
||||
|
||||
bind(".giveBlueprints", this.giveBlueprints);
|
||||
bind(".maxOutAll", this.maxOutAll);
|
||||
bind(".levelToggle .minus", () => this.modifyLevel(-1));
|
||||
bind(".levelToggle .plus", () => this.modifyLevel(1));
|
||||
|
||||
bind(".upgradesBelt .minus", () => this.modifyUpgrade("belt", -1));
|
||||
bind(".upgradesBelt .plus", () => this.modifyUpgrade("belt", 1));
|
||||
|
||||
bind(".upgradesExtraction .minus", () => this.modifyUpgrade("miner", -1));
|
||||
bind(".upgradesExtraction .plus", () => this.modifyUpgrade("miner", 1));
|
||||
|
||||
bind(".upgradesProcessing .minus", () => this.modifyUpgrade("processors", -1));
|
||||
bind(".upgradesProcessing .plus", () => this.modifyUpgrade("processors", 1));
|
||||
|
||||
bind(".upgradesPainting .minus", () => this.modifyUpgrade("painting", -1));
|
||||
bind(".upgradesPainting .plus", () => this.modifyUpgrade("painting", 1));
|
||||
}
|
||||
|
||||
giveBlueprints() {
|
||||
if (!this.root.hubGoals.storedShapes[blueprintShape]) {
|
||||
this.root.hubGoals.storedShapes[blueprintShape] = 0;
|
||||
}
|
||||
this.root.hubGoals.storedShapes[blueprintShape] += 1e9;
|
||||
}
|
||||
|
||||
maxOutAll() {
|
||||
this.modifyUpgrade("belt", 100);
|
||||
this.modifyUpgrade("miner", 100);
|
||||
this.modifyUpgrade("processors", 100);
|
||||
this.modifyUpgrade("painting", 100);
|
||||
}
|
||||
|
||||
modifyUpgrade(id, amount) {
|
||||
const handle = UPGRADES[id];
|
||||
const maxLevel = handle.tiers.length;
|
||||
|
||||
this.root.hubGoals.upgradeLevels[id] = Math.max(
|
||||
0,
|
||||
Math.min(maxLevel, (this.root.hubGoals.upgradeLevels[id] || 0) + amount)
|
||||
);
|
||||
|
||||
// Compute improvement
|
||||
let improvement = 1;
|
||||
for (let i = 0; i < this.root.hubGoals.upgradeLevels[id]; ++i) {
|
||||
improvement += handle.tiers[i].improvement;
|
||||
}
|
||||
this.root.hubGoals.upgradeImprovements[id] = improvement;
|
||||
this.root.signals.upgradePurchased.dispatch(id);
|
||||
this.root.hud.signals.notification.dispatch(
|
||||
"Upgrade '" + id + "' is now at tier " + (this.root.hubGoals.upgradeLevels[id] + 1),
|
||||
enumNotificationType.upgrade
|
||||
);
|
||||
}
|
||||
|
||||
modifyLevel(amount) {
|
||||
const hubGoals = this.root.hubGoals;
|
||||
hubGoals.level = Math.max(1, hubGoals.level + amount);
|
||||
hubGoals.createNextGoal();
|
||||
|
||||
// Clear all shapes of this level
|
||||
hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;
|
||||
|
||||
this.root.hud.parts.pinnedShapes.rerenderFull();
|
||||
|
||||
// Compute gained rewards
|
||||
hubGoals.gainedRewards = {};
|
||||
for (let i = 0; i < hubGoals.level - 1; ++i) {
|
||||
if (i < tutorialGoals.length) {
|
||||
const reward = tutorialGoals[i].reward;
|
||||
hubGoals.gainedRewards[reward] = (hubGoals.gainedRewards[reward] || 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
this.root.hud.signals.notification.dispatch(
|
||||
"Changed level to " + hubGoals.level,
|
||||
enumNotificationType.upgrade
|
||||
);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
// Allow toggling the controller overlay
|
||||
this.root.gameState.inputReciever.keydown.add(key => {
|
||||
if (key.keyCode === 117) {
|
||||
// F6
|
||||
this.toggle();
|
||||
}
|
||||
});
|
||||
|
||||
this.visible = !G_IS_DEV;
|
||||
this.domAttach = new DynamicDomAttach(this.root, this.element);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.visible = !this.visible;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.domAttach.update(this.visible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,8 @@ export function initMetaBuildingRegistry() {
|
||||
registerBuildingVariant(44, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.rotater);
|
||||
registerBuildingVariant(45, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.unstacker);
|
||||
registerBuildingVariant(46, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.shapecompare);
|
||||
registerBuildingVariant(50, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.stacker);
|
||||
registerBuildingVariant(51, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.painter);
|
||||
|
||||
// Reader
|
||||
registerBuildingVariant(49, MetaReaderBuilding);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, isTruthyItem, BooleanItem }
|
||||
import { COLOR_ITEM_SINGLETONS, ColorItem } from "../items/color_item";
|
||||
import { ShapeDefinition } from "../shape_definition";
|
||||
import { ShapeItem } from "../items/shape_item";
|
||||
import { enumInvertedDirections } from "../../core/vector";
|
||||
|
||||
export class LogicGateSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
@@ -24,6 +25,8 @@ export class LogicGateSystem extends GameSystemWithFilter {
|
||||
[enumLogicGateType.cutter]: this.compute_CUT.bind(this),
|
||||
[enumLogicGateType.unstacker]: this.compute_UNSTACK.bind(this),
|
||||
[enumLogicGateType.shapecompare]: this.compute_SHAPECOMPARE.bind(this),
|
||||
[enumLogicGateType.stacker]: this.compute_STACKER.bind(this),
|
||||
[enumLogicGateType.painter]: this.compute_PAINTER.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -259,6 +262,58 @@ export class LogicGateSystem extends GameSystemWithFilter {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<BaseItem|null>} parameters
|
||||
* @returns {BaseItem}
|
||||
*/
|
||||
compute_STACKER(parameters) {
|
||||
const lowerItem = parameters[0];
|
||||
const upperItem = parameters[1];
|
||||
|
||||
if (!lowerItem || !upperItem) {
|
||||
// Empty
|
||||
return null;
|
||||
}
|
||||
|
||||
if (lowerItem.getItemType() !== "shape" || upperItem.getItemType() !== "shape") {
|
||||
// Bad type
|
||||
return null;
|
||||
}
|
||||
|
||||
const stackedShape = this.root.shapeDefinitionMgr.shapeActionStack(
|
||||
/** @type {ShapeItem} */ (lowerItem).definition,
|
||||
/** @type {ShapeItem} */ (upperItem).definition
|
||||
);
|
||||
|
||||
return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(stackedShape);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<BaseItem|null>} parameters
|
||||
* @returns {BaseItem}
|
||||
*/
|
||||
compute_PAINTER(parameters) {
|
||||
const shape = parameters[0];
|
||||
const color = parameters[1];
|
||||
|
||||
if (!shape || !color) {
|
||||
// Empty
|
||||
return null;
|
||||
}
|
||||
|
||||
if (shape.getItemType() !== "shape" || color.getItemType() !== "color") {
|
||||
// Bad type
|
||||
return null;
|
||||
}
|
||||
|
||||
const coloredShape = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
/** @type {ShapeItem} */ (shape).definition,
|
||||
/** @type {ColorItem} */ (color).color
|
||||
);
|
||||
|
||||
return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(coloredShape);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<BaseItem|null>} parameters
|
||||
* @returns {BaseItem}
|
||||
@@ -269,7 +324,7 @@ export class LogicGateSystem extends GameSystemWithFilter {
|
||||
|
||||
if (!itemA || !itemB) {
|
||||
// Empty
|
||||
return BOOL_FALSE_SINGLETON;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (itemA.getItemType() !== itemB.getItemType()) {
|
||||
|
||||
Reference in New Issue
Block a user