mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-12-13 10:11:50 +00:00
Add notification blocks mod example
This commit is contained in:
parent
2ac34665b3
commit
27db6fe693
312
mod_examples/notification_blocks.js
Normal file
312
mod_examples/notification_blocks.js
Normal file
File diff suppressed because one or more lines are too long
BIN
res/ui/icons/notification_error.png
Normal file
BIN
res/ui/icons/notification_error.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
res/ui/icons/notification_info.png
Normal file
BIN
res/ui/icons/notification_info.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
res/ui/icons/notification_warning.png
Normal file
BIN
res/ui/icons/notification_warning.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
@ -61,7 +61,8 @@ $buildingsAndVariants: belt, balancer, underground_belt, underground_belt-tier2,
|
||||
background-image: uiResource("res/ui/building_tutorials/virtual_processor-cutter.png") !important;
|
||||
}
|
||||
|
||||
$icons: notification_saved, notification_success, notification_upgrade;
|
||||
$icons: notification_saved, notification_success, notification_upgrade, notification_info,
|
||||
notification_warning, notification_error;
|
||||
@each $icon in $icons {
|
||||
[data-icon="icons/#{$icon}.png"] {
|
||||
/* @load-async */
|
||||
|
||||
@ -90,6 +90,8 @@ export class GameHUD {
|
||||
this.parts[partId] = new part(this.root);
|
||||
}
|
||||
|
||||
MOD_SIGNALS.hudInitializer.dispatch(this.root);
|
||||
|
||||
const frag = document.createDocumentFragment();
|
||||
for (const key in this.parts) {
|
||||
MOD_SIGNALS.hudElementInitialized.dispatch(this.parts[key]);
|
||||
|
||||
@ -1,7 +1,19 @@
|
||||
import { THIRDPARTY_URLS } from "../../../core/config";
|
||||
import { DialogWithForm } from "../../../core/modal_dialog_elements";
|
||||
import { FormElementInput, FormElementItemChooser } from "../../../core/modal_dialog_forms";
|
||||
import { STOP_PROPAGATION } from "../../../core/signal";
|
||||
import { fillInLinkIntoTranslation } from "../../../core/utils";
|
||||
import { Vector } from "../../../core/vector";
|
||||
import { T } from "../../../translations";
|
||||
import { BaseItem } from "../../base_item";
|
||||
import { enumMouseButton } from "../../camera";
|
||||
import { Entity } from "../../entity";
|
||||
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../../items/boolean_item";
|
||||
import { COLOR_ITEM_SINGLETONS } from "../../items/color_item";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import trim from "trim";
|
||||
import { enumColors } from "../../colors";
|
||||
import { ShapeDefinition } from "../../shape_definition";
|
||||
|
||||
export class HUDConstantSignalEdit extends BaseHUDPart {
|
||||
initialize() {
|
||||
@ -23,7 +35,7 @@ export class HUDConstantSignalEdit extends BaseHUDPart {
|
||||
const constantComp = contents.components.ConstantSignal;
|
||||
if (constantComp) {
|
||||
if (button === enumMouseButton.left) {
|
||||
this.root.systemMgr.systems.constantSignal.editConstantSignal(contents, {
|
||||
this.editConstantSignal(contents, {
|
||||
deleteOnCancel: false,
|
||||
});
|
||||
return STOP_PROPAGATION;
|
||||
@ -31,4 +43,171 @@ export class HUDConstantSignalEdit extends BaseHUDPart {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the entity to enter a valid signal code
|
||||
* @param {Entity} entity
|
||||
* @param {object} param0
|
||||
* @param {boolean=} param0.deleteOnCancel
|
||||
*/
|
||||
editConstantSignal(entity, { deleteOnCancel = true }) {
|
||||
if (!entity.components.ConstantSignal) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ok, query, but also save the uid because it could get stale
|
||||
const uid = entity.uid;
|
||||
|
||||
const signal = entity.components.ConstantSignal.signal;
|
||||
const signalValueInput = new FormElementInput({
|
||||
id: "signalValue",
|
||||
label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer),
|
||||
placeholder: "",
|
||||
defaultValue: signal ? signal.getAsCopyableKey() : "",
|
||||
validator: val => this.parseSignalCode(entity, val),
|
||||
});
|
||||
|
||||
const items = [...Object.values(COLOR_ITEM_SINGLETONS)];
|
||||
|
||||
if (entity.components.WiredPins) {
|
||||
items.unshift(BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON);
|
||||
items.push(
|
||||
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(
|
||||
this.root.gameMode.getBlueprintShapeKey()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// producer which can produce virtually anything
|
||||
const shapes = ["CuCuCuCu", "RuRuRuRu", "WuWuWuWu", "SuSuSuSu"];
|
||||
items.unshift(
|
||||
...shapes.reverse().map(key => this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key))
|
||||
);
|
||||
}
|
||||
|
||||
if (this.root.gameMode.hasHub()) {
|
||||
items.push(
|
||||
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
|
||||
this.root.hubGoals.currentGoal.definition
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.root.hud.parts.pinnedShapes) {
|
||||
items.push(
|
||||
...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
|
||||
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const itemInput = new FormElementItemChooser({
|
||||
id: "signalItem",
|
||||
label: null,
|
||||
items,
|
||||
});
|
||||
|
||||
const dialog = new DialogWithForm({
|
||||
app: this.root.app,
|
||||
title: T.dialogs.editConstantProducer.title,
|
||||
desc: T.dialogs.editSignal.descItems,
|
||||
formElements: [itemInput, signalValueInput],
|
||||
buttons: ["cancel:bad:escape", "ok:good:enter"],
|
||||
closeButton: false,
|
||||
});
|
||||
this.root.hud.parts.dialogs.internalShowDialog(dialog);
|
||||
|
||||
// When confirmed, set the signal
|
||||
const closeHandler = () => {
|
||||
if (!this.root || !this.root.entityMgr) {
|
||||
// Game got stopped
|
||||
return;
|
||||
}
|
||||
|
||||
const entityRef = this.root.entityMgr.findByUid(uid, false);
|
||||
if (!entityRef) {
|
||||
// outdated
|
||||
return;
|
||||
}
|
||||
|
||||
const constantComp = entityRef.components.ConstantSignal;
|
||||
if (!constantComp) {
|
||||
// no longer interesting
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemInput.chosenItem) {
|
||||
constantComp.signal = itemInput.chosenItem;
|
||||
} else {
|
||||
constantComp.signal = this.parseSignalCode(entity, signalValueInput.getValue());
|
||||
}
|
||||
};
|
||||
|
||||
dialog.buttonSignals.ok.add(() => {
|
||||
closeHandler();
|
||||
});
|
||||
dialog.valueChosen.add(() => {
|
||||
dialog.closeRequested.dispatch();
|
||||
closeHandler();
|
||||
});
|
||||
|
||||
// When cancelled, destroy the entity again
|
||||
if (deleteOnCancel) {
|
||||
dialog.buttonSignals.cancel.add(() => {
|
||||
if (!this.root || !this.root.entityMgr) {
|
||||
// Game got stopped
|
||||
return;
|
||||
}
|
||||
|
||||
const entityRef = this.root.entityMgr.findByUid(uid, false);
|
||||
if (!entityRef) {
|
||||
// outdated
|
||||
return;
|
||||
}
|
||||
|
||||
const constantComp = entityRef.components.ConstantSignal;
|
||||
if (!constantComp) {
|
||||
// no longer interesting
|
||||
return;
|
||||
}
|
||||
|
||||
this.root.logic.tryDeleteBuilding(entityRef);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse a signal code
|
||||
* @param {Entity} entity
|
||||
* @param {string} code
|
||||
* @returns {BaseItem}
|
||||
*/
|
||||
parseSignalCode(entity, code) {
|
||||
if (!this.root || !this.root.shapeDefinitionMgr) {
|
||||
// Stale reference
|
||||
return null;
|
||||
}
|
||||
|
||||
code = trim(code);
|
||||
const codeLower = code.toLowerCase();
|
||||
|
||||
if (enumColors[codeLower]) {
|
||||
return COLOR_ITEM_SINGLETONS[codeLower];
|
||||
}
|
||||
|
||||
if (entity.components.WiredPins) {
|
||||
if (code === "1" || codeLower === "true") {
|
||||
return BOOL_TRUE_SINGLETON;
|
||||
}
|
||||
|
||||
if (code === "0" || codeLower === "false") {
|
||||
return BOOL_FALSE_SINGLETON;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShapeDefinition.isValidShortKey(code)) {
|
||||
return this.root.shapeDefinitionMgr.getShapeItemFromShortKey(code);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,9 @@ export const enumNotificationType = {
|
||||
saved: "saved",
|
||||
upgrade: "upgrade",
|
||||
success: "success",
|
||||
info: "info",
|
||||
warning: "warning",
|
||||
error: "error",
|
||||
};
|
||||
|
||||
const notificationDuration = 3;
|
||||
@ -17,14 +20,14 @@ export class HUDNotifications extends BaseHUDPart {
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.root.hud.signals.notification.add(this.onNotification, this);
|
||||
this.root.hud.signals.notification.add(this.internalShowNotification, this);
|
||||
|
||||
/** @type {Array<{ element: HTMLElement, expireAt: number}>} */
|
||||
this.notificationElements = [];
|
||||
|
||||
// Automatic notifications
|
||||
this.root.signals.gameSaved.add(() =>
|
||||
this.onNotification(T.ingame.notifications.gameSaved, enumNotificationType.saved)
|
||||
this.internalShowNotification(T.ingame.notifications.gameSaved, enumNotificationType.saved)
|
||||
);
|
||||
}
|
||||
|
||||
@ -32,7 +35,7 @@ export class HUDNotifications extends BaseHUDPart {
|
||||
* @param {string} message
|
||||
* @param {enumNotificationType} type
|
||||
*/
|
||||
onNotification(message, type) {
|
||||
internalShowNotification(message, type) {
|
||||
const element = makeDiv(this.element, null, ["notification", "type-" + type], message);
|
||||
element.setAttribute("data-icon", "icons/notification_" + type + ".png");
|
||||
|
||||
|
||||
@ -1,25 +1,16 @@
|
||||
import trim from "trim";
|
||||
import { THIRDPARTY_URLS } from "../../core/config";
|
||||
import { DialogWithForm } from "../../core/modal_dialog_elements";
|
||||
import { FormElementInput, FormElementItemChooser } from "../../core/modal_dialog_forms";
|
||||
import { fillInLinkIntoTranslation } from "../../core/utils";
|
||||
import { T } from "../../translations";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { enumColors } from "../colors";
|
||||
import { ConstantSignalComponent } from "../components/constant_signal";
|
||||
import { Entity } from "../entity";
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
|
||||
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
||||
import { ShapeDefinition } from "../shape_definition";
|
||||
|
||||
export class ConstantSignalSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
super(root, [ConstantSignalComponent]);
|
||||
|
||||
this.root.signals.entityManuallyPlaced.add(entity =>
|
||||
this.editConstantSignal(entity, { deleteOnCancel: true })
|
||||
);
|
||||
this.root.signals.entityManuallyPlaced.add(entity => {
|
||||
const editorHud = this.root.hud.parts.constantSignalEdit;
|
||||
if (editorHud) {
|
||||
editorHud.editConstantSignal(entity, { deleteOnCancel: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
@ -34,171 +25,4 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the entity to enter a valid signal code
|
||||
* @param {Entity} entity
|
||||
* @param {object} param0
|
||||
* @param {boolean=} param0.deleteOnCancel
|
||||
*/
|
||||
editConstantSignal(entity, { deleteOnCancel = true }) {
|
||||
if (!entity.components.ConstantSignal) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ok, query, but also save the uid because it could get stale
|
||||
const uid = entity.uid;
|
||||
|
||||
const signal = entity.components.ConstantSignal.signal;
|
||||
const signalValueInput = new FormElementInput({
|
||||
id: "signalValue",
|
||||
label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer),
|
||||
placeholder: "",
|
||||
defaultValue: signal ? signal.getAsCopyableKey() : "",
|
||||
validator: val => this.parseSignalCode(entity, val),
|
||||
});
|
||||
|
||||
const items = [...Object.values(COLOR_ITEM_SINGLETONS)];
|
||||
|
||||
if (entity.components.WiredPins) {
|
||||
items.unshift(BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON);
|
||||
items.push(
|
||||
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(
|
||||
this.root.gameMode.getBlueprintShapeKey()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// producer which can produce virtually anything
|
||||
const shapes = ["CuCuCuCu", "RuRuRuRu", "WuWuWuWu", "SuSuSuSu"];
|
||||
items.unshift(
|
||||
...shapes.reverse().map(key => this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key))
|
||||
);
|
||||
}
|
||||
|
||||
if (this.root.gameMode.hasHub()) {
|
||||
items.push(
|
||||
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(
|
||||
this.root.hubGoals.currentGoal.definition
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.root.hud.parts.pinnedShapes) {
|
||||
items.push(
|
||||
...this.root.hud.parts.pinnedShapes.pinnedShapes.map(key =>
|
||||
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(key)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const itemInput = new FormElementItemChooser({
|
||||
id: "signalItem",
|
||||
label: null,
|
||||
items,
|
||||
});
|
||||
|
||||
const dialog = new DialogWithForm({
|
||||
app: this.root.app,
|
||||
title: T.dialogs.editConstantProducer.title,
|
||||
desc: T.dialogs.editSignal.descItems,
|
||||
formElements: [itemInput, signalValueInput],
|
||||
buttons: ["cancel:bad:escape", "ok:good:enter"],
|
||||
closeButton: false,
|
||||
});
|
||||
this.root.hud.parts.dialogs.internalShowDialog(dialog);
|
||||
|
||||
// When confirmed, set the signal
|
||||
const closeHandler = () => {
|
||||
if (!this.root || !this.root.entityMgr) {
|
||||
// Game got stopped
|
||||
return;
|
||||
}
|
||||
|
||||
const entityRef = this.root.entityMgr.findByUid(uid, false);
|
||||
if (!entityRef) {
|
||||
// outdated
|
||||
return;
|
||||
}
|
||||
|
||||
const constantComp = entityRef.components.ConstantSignal;
|
||||
if (!constantComp) {
|
||||
// no longer interesting
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemInput.chosenItem) {
|
||||
constantComp.signal = itemInput.chosenItem;
|
||||
} else {
|
||||
constantComp.signal = this.parseSignalCode(entity, signalValueInput.getValue());
|
||||
}
|
||||
};
|
||||
|
||||
dialog.buttonSignals.ok.add(() => {
|
||||
closeHandler();
|
||||
});
|
||||
dialog.valueChosen.add(() => {
|
||||
dialog.closeRequested.dispatch();
|
||||
closeHandler();
|
||||
});
|
||||
|
||||
// When cancelled, destroy the entity again
|
||||
if (deleteOnCancel) {
|
||||
dialog.buttonSignals.cancel.add(() => {
|
||||
if (!this.root || !this.root.entityMgr) {
|
||||
// Game got stopped
|
||||
return;
|
||||
}
|
||||
|
||||
const entityRef = this.root.entityMgr.findByUid(uid, false);
|
||||
if (!entityRef) {
|
||||
// outdated
|
||||
return;
|
||||
}
|
||||
|
||||
const constantComp = entityRef.components.ConstantSignal;
|
||||
if (!constantComp) {
|
||||
// no longer interesting
|
||||
return;
|
||||
}
|
||||
|
||||
this.root.logic.tryDeleteBuilding(entityRef);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse a signal code
|
||||
* @param {Entity} entity
|
||||
* @param {string} code
|
||||
* @returns {BaseItem}
|
||||
*/
|
||||
parseSignalCode(entity, code) {
|
||||
if (!this.root || !this.root.shapeDefinitionMgr) {
|
||||
// Stale reference
|
||||
return null;
|
||||
}
|
||||
|
||||
code = trim(code);
|
||||
const codeLower = code.toLowerCase();
|
||||
|
||||
if (enumColors[codeLower]) {
|
||||
return COLOR_ITEM_SINGLETONS[codeLower];
|
||||
}
|
||||
|
||||
if (entity.components.WiredPins) {
|
||||
if (code === "1" || codeLower === "true") {
|
||||
return BOOL_TRUE_SINGLETON;
|
||||
}
|
||||
|
||||
if (code === "0" || codeLower === "false") {
|
||||
return BOOL_FALSE_SINGLETON;
|
||||
}
|
||||
}
|
||||
|
||||
if (ShapeDefinition.isValidShortKey(code)) {
|
||||
return this.root.shapeDefinitionMgr.getShapeItemFromShortKey(code);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ import { KEYMAPPINGS } from "../game/key_action_mapper";
|
||||
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
||||
import { THEMES } from "../game/theme";
|
||||
import { ModMetaBuilding } from "./mod_meta_building";
|
||||
import { BaseHUDPart } from "../game/hud/base_hud_part";
|
||||
|
||||
export class ModInterface {
|
||||
/**
|
||||
@ -416,4 +417,15 @@ export class ModInterface {
|
||||
extendClass(classHandle, extender) {
|
||||
this.extendObject(classHandle.prototype, extender);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @param {new (...args) => BaseHUDPart} element
|
||||
*/
|
||||
registerHudElement(id, element) {
|
||||
this.modLoader.signals.hudInitializer.add(root => {
|
||||
root.hud.parts[id] = new element(root);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +19,8 @@ export const MOD_SIGNALS = {
|
||||
hudElementInitialized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()),
|
||||
hudElementFinalized: /** @type {TypedSignal<[BaseHUDPart]>} */ (new Signal()),
|
||||
|
||||
hudInitializer: /** @type {TypedSignal<[GameRoot]>} */ (new Signal()),
|
||||
|
||||
gameInitialized: /** @type {TypedSignal<[GameRoot]>} */ (new Signal()),
|
||||
gameLoadingStageEntered: /** @type {TypedSignal<[InGameState, string]>} */ (new Signal()),
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user