From 9e276786f3e953c9acd1783c2e0f59e9c916eb59 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 18 Feb 2024 20:37:14 +1200 Subject: [PATCH 01/31] :bug: Fix scripts sometimes having blank screen when switching back and forth tabs --- src/riotTags/app-view.tag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/riotTags/app-view.tag b/src/riotTags/app-view.tag index 0498c4da6..3b75de847 100644 --- a/src/riotTags/app-view.tag +++ b/src/riotTags/app-view.tag @@ -159,7 +159,7 @@ app-view.flexcol window.hotkeys.push(tab); } else { // The current tab is an asset - if (['room', 'template', 'behavior'].includes(tab.type)) { + if (['room', 'template', 'behavior', 'script'].includes(tab.type)) { window.orders.trigger('forceCodeEditorLayout'); } window.hotkeys.push(tab.uid); From 49c77dce6b7e8b4f93899a587d799a3db07d51a8 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Fri, 23 Feb 2024 18:19:50 +1200 Subject: [PATCH 02/31] :zap: Refactor copy creation off base classes, move CopyAnimatedSprite, CopyText, CopyContainer prototypes into separate pixi.js-based classes. --- .../templateBaseClasses/PixiAnimatedSprite.ts | 22 +++ .../templateBaseClasses/PixiButton.ts | 2 +- .../templateBaseClasses/PixiContainer.ts | 13 ++ .../templateBaseClasses/PixiNineSlicePlane.ts | 38 +++++ .../PixiScrollingTexture.ts | 6 +- .../templateBaseClasses/PixiText.ts | 45 +++++ .../templateBaseClasses/PixiTextBox.ts | 3 +- src/ct.release/templateBaseClasses/index.ts | 80 +++++++++ src/ct.release/templates.ts | 157 +----------------- src/ct.release/u.ts | 3 +- .../exporter/_exporterContracts.ts | 7 +- src/node_requires/resources/rooms/IRoom.d.ts | 1 + .../resources/rooms/defaultRoom.ts | 1 + 13 files changed, 222 insertions(+), 156 deletions(-) create mode 100644 src/ct.release/templateBaseClasses/PixiAnimatedSprite.ts create mode 100644 src/ct.release/templateBaseClasses/PixiContainer.ts create mode 100644 src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts create mode 100644 src/ct.release/templateBaseClasses/PixiText.ts create mode 100644 src/ct.release/templateBaseClasses/index.ts diff --git a/src/ct.release/templateBaseClasses/PixiAnimatedSprite.ts b/src/ct.release/templateBaseClasses/PixiAnimatedSprite.ts new file mode 100644 index 000000000..1a38f6176 --- /dev/null +++ b/src/ct.release/templateBaseClasses/PixiAnimatedSprite.ts @@ -0,0 +1,22 @@ +import res from '../res'; +import {ExportedTemplate} from '../../node_requires/exporter/_exporterContracts'; + +import type * as pixiMod from 'node_modules/pixi.js'; +declare var PIXI: typeof pixiMod; + +export default class PixiAnimateSprite extends PIXI.AnimatedSprite { + constructor(t: ExportedTemplate, exts: Record) { + if (t?.baseClass !== 'AnimatedSprite') { + throw new Error('Don\'t call PixiButton class directly! Use templates.copy to create an instance instead.'); + } + const textures = res.getTexture(t.texture); + super(textures); + this.anchor.x = t.anchorX ?? textures[0].defaultAnchor.x ?? 0; + this.anchor.y = t.anchorY ?? textures[0].defaultAnchor.y ?? 0; + this.scale.set( + (exts.scaleX as number) ?? 1, + (exts.scaleY as number) ?? 1 + ); + return this; + } +} diff --git a/src/ct.release/templateBaseClasses/PixiButton.ts b/src/ct.release/templateBaseClasses/PixiButton.ts index 7780e3b3e..cf85ac85c 100644 --- a/src/ct.release/templateBaseClasses/PixiButton.ts +++ b/src/ct.release/templateBaseClasses/PixiButton.ts @@ -2,7 +2,7 @@ import stylesLib from '../styles'; import {ExportedTemplate} from '../../node_requires/exporter/_exporterContracts'; import resLib from '../res'; import uLib from '../u'; -import {CopyButton} from 'templates'; +import {CopyButton} from '../templateBaseClasses'; import type * as pixiMod from 'node_modules/pixi.js'; declare var PIXI: typeof pixiMod; diff --git a/src/ct.release/templateBaseClasses/PixiContainer.ts b/src/ct.release/templateBaseClasses/PixiContainer.ts new file mode 100644 index 000000000..7309df551 --- /dev/null +++ b/src/ct.release/templateBaseClasses/PixiContainer.ts @@ -0,0 +1,13 @@ +import type * as pixiMod from 'node_modules/pixi.js'; +declare var PIXI: typeof pixiMod; + +export default class PixiContainer extends PIXI.Container { + shape: textureShape; + constructor() { + super(); + this.shape = { + type: 'point' + }; + return this; + } +} diff --git a/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts b/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts new file mode 100644 index 000000000..0f31ed421 --- /dev/null +++ b/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts @@ -0,0 +1,38 @@ +import {ExportedTemplate} from '../../node_requires/exporter/_exporterContracts'; +import resLib from '../res'; +import uLib from '../u'; +import {CopyPanel} from '../templateBaseClasses'; + +import type * as pixiMod from 'node_modules/pixi.js'; +declare var PIXI: typeof pixiMod; + +export default class PixiPanel extends PIXI.NineSlicePlane { + /** + * Whether to automatically update the collision shape of this panel + * when it changes its size. + */ + updateNineSliceShape: boolean; + constructor(t: ExportedTemplate, exts: Record) { + if (t?.baseClass !== 'NineSlicePlane') { + throw new Error('Don\'t call PixiPanel class directly! Use templates.copy to create an instance instead.'); + } + const tex = resLib.getTexture(t.texture, 0); + super( + tex, + t.nineSliceSettings?.left ?? 16, + t.nineSliceSettings?.top ?? 16, + t.nineSliceSettings?.right ?? 16, + t.nineSliceSettings?.bottom ?? 16 + ); + this.updateNineSliceShape = t.nineSliceSettings.autoUpdate; + const baseWidth = this.width, + baseHeight = this.height; + if ('scaleX' in exts) { + this.width = baseWidth * (exts.scaleX as number); + } + if ('scaleY' in exts) { + this.height = baseHeight * (exts.scaleY as number); + } + uLib.reshapeNinePatch(this as CopyPanel); + } +} diff --git a/src/ct.release/templateBaseClasses/PixiScrollingTexture.ts b/src/ct.release/templateBaseClasses/PixiScrollingTexture.ts index 49fd0d969..4dbf7bf2a 100644 --- a/src/ct.release/templateBaseClasses/PixiScrollingTexture.ts +++ b/src/ct.release/templateBaseClasses/PixiScrollingTexture.ts @@ -1,8 +1,8 @@ import {ExportedTemplate} from '../../node_requires/exporter/_exporterContracts'; import resLib from '../res'; -import uLib from 'u'; -import roomsLib from 'rooms'; -import {BasicCopy} from 'templates'; +import uLib from '../u'; +import roomsLib from '../rooms'; +import {BasicCopy} from '../templates'; import type * as pixiMod from 'node_modules/pixi.js'; declare var PIXI: typeof pixiMod; diff --git a/src/ct.release/templateBaseClasses/PixiText.ts b/src/ct.release/templateBaseClasses/PixiText.ts new file mode 100644 index 000000000..198238614 --- /dev/null +++ b/src/ct.release/templateBaseClasses/PixiText.ts @@ -0,0 +1,45 @@ +import {ExportedStyle, ExportedTemplate} from '../../node_requires/exporter/_exporterContracts'; +import uLib from '../u'; +import {CopyText} from '.'; + +import type * as pixiMod from 'node_modules/pixi.js'; +import stylesLib from '../styles'; +declare var PIXI: typeof pixiMod; + +export default class PixiText extends PIXI.Text { + constructor(t: ExportedTemplate, exts: Record) { + if (t?.baseClass !== 'Text') { + throw new Error('Don\'t call PixiPanel class directly! Use templates.copy to create an instance instead.'); + } + let style: ExportedStyle; + if (t.textStyle && t.textStyle !== -1) { + style = stylesLib.get(t.textStyle, true); + } else { + style = {} as ExportedStyle; + } + if (exts.customWordWrap) { + style.wordWrap = true; + style.wordWrapWidth = Number(exts.customWordWrap); + } + if (exts.customSize) { + style.fontSize = Number(exts.customSize); + } + super( + (exts.customText as string) || t.defaultText || '', + style as unknown as Partial + ); + if (exts.customAnchor) { + const anchor = exts.customAnchor as { + x?: number, + y?: number + }; + (this as CopyText).anchor.set(anchor?.x ?? 0, anchor?.y ?? 0); + } + (this as CopyText).shape = uLib.getRectShape(this); + (this as CopyText).scale.set( + (exts.scaleX as number) ?? 1, + (exts.scaleY as number) ?? 1 + ); + return this; + } +} diff --git a/src/ct.release/templateBaseClasses/PixiTextBox.ts b/src/ct.release/templateBaseClasses/PixiTextBox.ts index 29d0eef7d..a1ef8c199 100644 --- a/src/ct.release/templateBaseClasses/PixiTextBox.ts +++ b/src/ct.release/templateBaseClasses/PixiTextBox.ts @@ -2,7 +2,8 @@ import stylesLib from '../styles'; import {ExportedTemplate} from '../../node_requires/exporter/_exporterContracts'; import resLib from '../res'; import uLib from '../u'; -import {BasicCopy, CopyTextBox} from 'templates'; +import {BasicCopy} from 'templates'; +import {CopyTextBox} from 'templateBaseClasses'; import {setFocusedElement} from '../templates'; import {pixiApp, settings as settingsLib} from 'index'; diff --git a/src/ct.release/templateBaseClasses/index.ts b/src/ct.release/templateBaseClasses/index.ts new file mode 100644 index 000000000..53a240f43 --- /dev/null +++ b/src/ct.release/templateBaseClasses/index.ts @@ -0,0 +1,80 @@ +import PixiButton from './PixiButton'; +import PixiSpritedCounter from './PixiSpritedCounter'; +import PixiScrollingTexture from './PixiScrollingTexture'; +import PixiTextBox from './PixiTextBox'; +import PixiScrollBox from './PixiScrollBox'; +import PixiPanel from './PixiNineSlicePlane'; +import PixiText from './PixiText'; +import PixiContainer from './PixiContainer'; +import PixiAnimatedSprite from './PixiAnimatedSprite'; + +import {ICopy} from '../templates'; + +import type * as pixiMod from 'node_modules/pixi.js'; +import {BaseClass} from '../../node_requires/exporter/_exporterContracts'; + +// eslint-disable-next-line @typescript-eslint/ban-types +type Constructor = Function & { prototype: T }; +export const baseClassToPixiClass: Record> = { + AnimatedSprite: PixiAnimatedSprite, + Button: PixiButton, + Container: PixiContainer, + NineSlicePlane: PixiPanel, + RepeatingTexture: PixiScrollingTexture, + // ScrollBox: PixiScrollBox, + SpritedCounter: PixiSpritedCounter, + Text: PixiText, + TextBox: PixiTextBox +}; + +// Record allows ct.js users to write any properties to their copies +// without typescript complaining. +/* eslint-disable @typescript-eslint/no-explicit-any */ +/** + * An instance of a ct.js template with Animated Sprite as its base class. + * It has functionality of both PIXI.AnimatedSprite and ct.js Copies. + */ +export type CopyAnimatedSprite = Record & pixiMod.AnimatedSprite & ICopy; +/** + * An instance of a ct.js template with Panel as its base class. + * It has functionality of both PIXI.NineSlicePlane and ct.js Copies. + */ +export type CopyPanel = Record & PixiPanel & ICopy; +/** + * An instance of a ct.js template with Text as its base class. + * It has functionality of both PIXI.Text and ct.js Copies. + */ +export type CopyText = Record & PixiText & ICopy; +/** + * An instance of a ct.js template with Container as its base class. + * It has functionality of both PIXI.Container and ct.js Copies, and though by itself it doesn't + * display anything, you can add other copies and pixi.js classes with `this.addChild(copy)`. + */ +export type CopyContainer = Record & PixiContainer & ICopy; +/** + * An instance of a ct.js template with button logic. + * It has functionality of both PIXI.Container and ct.js Copies. + */ +export type CopyButton = Record & PixiButton & ICopy; +/** + * An instance of a ct.js template with text box logic. + * It has functionality of both PIXI.Container and ct.js Copies. + */ +export type CopyTextBox = Record & PixiTextBox & ICopy; +/** + * An instance of a ct.js template with repeating texture logic. + * The texture can expand in any direction and can be animated by scrolling. + */ +export type CopyRepeatingTexture = Record & PixiScrollingTexture & ICopy; +/** + * An instance of a ct.js template with a sprited counter logic. + * This copy displays a number of identical sprites in a row, similar to sprited healthbars. + */ +export type CopySpritedCounter = Record & PixiSpritedCounter & ICopy; +/** + * An instance of a ct.js template with Container as its base class. + * It has functionality of both PIXI.Container and ct.js Copies, and implements a scrollbox that + * has a scrollbar and clips its contents. + */ +export type CopyScrollBox = Record & PixiScrollBox & ICopy; +/* eslint-enable @typescript-eslint/no-explicit-any */ diff --git a/src/ct.release/templates.ts b/src/ct.release/templates.ts index 704de0a41..bb584fc32 100644 --- a/src/ct.release/templates.ts +++ b/src/ct.release/templates.ts @@ -5,17 +5,17 @@ import {Tilemap} from './tilemaps'; import roomsLib, {Room} from './rooms'; import {runBehaviors} from './behaviors'; import {copyTypeSymbol, stack} from '.'; -import stylesLib from 'styles'; import uLib from './u'; import type * as pixiMod from 'node_modules/pixi.js'; declare var PIXI: typeof pixiMod; -import type {ExportedRoom, ExportedStyle, ExportedTemplate, TextureShape} from '../node_requires/exporter/_exporterContracts'; +import type {ExportedRoom, ExportedTemplate, TextureShape} from '../node_requires/exporter/_exporterContracts'; +import {CopyAnimatedSprite, CopyButton, CopyPanel, baseClassToPixiClass} from './templateBaseClasses'; let uid = 0; -interface ICopy { +export interface ICopy { uid: number; /** The name of the template from which the copy was created */ template: string | null; @@ -139,62 +139,15 @@ export const setFocusedElement = (elt: IFocusableElement) => { focusedElement = elt; }; -import PixiButton from './templateBaseClasses/PixiButton'; -import PixiSpritedCounter from './templateBaseClasses/PixiSpritedCounter'; -import PixiScrollingTexture from './templateBaseClasses/PixiScrollingTexture'; -import PixiTextBox from './templateBaseClasses/PixiTextBox'; - // Record allows ct.js users to write any properties to their copies // without typescript complaining. /* eslint-disable @typescript-eslint/no-explicit-any */ - /** * An instance of a ct.js template. Ct.js cannot predict the base class * of a template here, so you may need to add `as Copy...` to further narrow down * the class. */ export type BasicCopy = Record & pixiMod.DisplayObject & ICopy; -/** - * An instance of a ct.js template with Animated Sprite as its base class. - * It has functionality of both PIXI.AnimatedSprite and ct.js Copies. - */ -export type CopyAnimatedSprite = Record & pixiMod.AnimatedSprite & ICopy; -/** - * An instance of a ct.js template with Panel as its base class. - * It has functionality of both PIXI.NineSlicePlane and ct.js Copies. - */ -export type CopyPanel = Record & pixiMod.NineSlicePlane & ICopy; -/** - * An instance of a ct.js template with Text as its base class. - * It has functionality of both PIXI.Text and ct.js Copies. - */ -export type CopyText = Record & pixiMod.Text & ICopy; -/** - * An instance of a ct.js template with Container as its base class. - * It has functionality of both PIXI.Container and ct.js Copies, and though by itself it doesn't - * display anything, you can add other copies and pixi.js classes with `this.addChild(copy)`. - */ -export type CopyContainer = Record & pixiMod.Container & ICopy; -/** - * An instance of a ct.js template with button logic. - * It has functionality of both PIXI.Container and ct.js Copies. - */ -export type CopyButton = Record & PixiButton & ICopy; -/** - * An instance of a ct.js template with text box logic. - * It has functionality of both PIXI.Container and ct.js Copies. - */ -export type CopyTextBox = Record & PixiTextBox & ICopy; -/** - * An instance of a ct.js template with repeating texture logic. - * The texture can expand in any direction and can be animated by scrolling. - */ -export type CopyRepeatingTexture = Record & PixiScrollingTexture & ICopy; -/** - * An instance of a ct.js template with a sprited counter logic. - * This copy displays a number of identical sprites in a row, similar to sprited healthbars. - */ -export type CopySpritedCounter = Record & PixiSpritedCounter & ICopy; /* eslint-enable @typescript-eslint/no-explicit-any */ export const CopyProto: Partial = { @@ -441,107 +394,13 @@ export const makeCopy = ( throw new Error(`[ct.templates] An attempt to create a copy of a non-existent template \`${template}\` detected. A typo?`); } const t: ExportedTemplate = templatesLib.templates[template]; - - // TODO: Refactor these into templateBaseClasses - if (t.baseClass === 'Container') { - const copy = new PIXI.Container() as CopyContainer; - copy.shape = { - type: 'point' - }; - mix(copy, x, y, t, parent, exts); - return copy; - } - if (t.baseClass === 'Text') { - let style: ExportedStyle; - if (t.textStyle && t.textStyle !== -1) { - style = stylesLib.get(t.textStyle, true); - } - if (exts.customWordWrap) { - style.wordWrap = true; - style.wordWrapWidth = Number(exts.customWordWrap); - } - if (exts.customSize) { - style.fontSize = Number(exts.customSize); - } - const copy = new PIXI.Text( - (exts.customText as string) || t.defaultText || '', - style as unknown as Partial - ) as CopyText; - if (exts.customAnchor) { - const anchor = exts.customAnchor as { - x?: number, - y?: number - }; - copy.anchor.set(anchor?.x ?? 0, anchor?.y ?? 0); - } - mix(copy, x, y, t, parent, exts); - copy.shape = uLib.getRectShape(copy); - copy.scale.set( - (exts.scaleX as number) ?? 1, - (exts.scaleY as number) ?? 1 - ); - return copy; - } - - let textures: pixiMod.Texture[] = [PIXI.Texture.EMPTY]; - if (t.texture && t.texture !== '-1') { - textures = resLib.getTexture(t.texture); - } - - if (t.baseClass === 'NineSlicePlane') { - const copy = new PIXI.NineSlicePlane( - textures[0], - t.nineSliceSettings?.left ?? 16, - t.nineSliceSettings?.top ?? 16, - t.nineSliceSettings?.right ?? 16, - t.nineSliceSettings?.bottom ?? 16 - ) as CopyPanel; - copy.updateNineSliceShape = t.nineSliceSettings.autoUpdate; - mix(copy, x, y, t, parent, exts); - const baseWidth = copy.width, - baseHeight = copy.height; - if ('scaleX' in exts) { - copy.width = baseWidth * (exts.scaleX as number); - } - if ('scaleY' in exts) { - copy.height = baseHeight * (exts.scaleY as number); - } - uLib.reshapeNinePatch(copy); - return copy; - } - if (t.baseClass === 'Button') { - const copy = new PixiButton(t, exts) as CopyButton; - mix(copy, x, y, t, parent, exts); - return copy; - } - if (t.baseClass === 'RepeatingTexture') { - const copy = new PixiScrollingTexture(t, exts) as CopyRepeatingTexture; - mix(copy, x, y, t, parent, exts); - return copy; - } - if (t.baseClass === 'SpritedCounter') { - const copy = new PixiSpritedCounter(t, exts) as CopySpritedCounter; - mix(copy, x, y, t, parent, exts); - return copy; - } - if (t.baseClass === 'AnimatedSprite') { - const copy = new PIXI.AnimatedSprite(textures) as CopyAnimatedSprite; - copy.anchor.x = t.anchorX ?? textures[0].defaultAnchor.x ?? 0; - copy.anchor.y = t.anchorY ?? textures[0].defaultAnchor.y ?? 0; - copy.scale.set( - (exts.scaleX as number) ?? 1, - (exts.scaleY as number) ?? 1 - ); - mix(copy, x, y, t, parent, exts); - return copy; - } - if (t.baseClass === 'TextBox') { - const copy = new PixiTextBox(t, exts) as CopyTextBox; - mix(copy, x, y, t, parent, exts); - return copy; + if (!(t.baseClass in baseClassToPixiClass)) { + throw new Error(`[internal -> makeCopy] Unknown base class \`${(t as any).baseClass}\` for template \`${template}\`.`); } + const copy = new baseClassToPixiClass[t.baseClass](t, exts) as BasicCopy; + mix(copy, x, y, t, parent, exts); + return copy; // eslint-disable-next-line @typescript-eslint/no-explicit-any - throw new Error(`[internal -> makeCopy] Unknown base class \`${(t as any).baseClass}\` for template \`${template}\`.`); }; const onCreateModifier = function () { diff --git a/src/ct.release/u.ts b/src/ct.release/u.ts index 56ceb1df2..6a675dab7 100644 --- a/src/ct.release/u.ts +++ b/src/ct.release/u.ts @@ -1,6 +1,7 @@ import type {CtjsTexture} from 'res'; import type {TextureShape} from '../node_requires/exporter/_exporterContracts'; -import type {BasicCopy, CopyButton, CopyPanel, CopyTextBox} from './templates'; +import type {BasicCopy} from './templates'; +import type {CopyButton, CopyPanel, CopyTextBox} from './templateBaseClasses'; import timerLib, {CtTimer} from './timer'; import {canvasCssPosition} from './fittoscreen'; import mainCamera from './camera'; diff --git a/src/node_requires/exporter/_exporterContracts.ts b/src/node_requires/exporter/_exporterContracts.ts index c014c7e12..866f92a52 100644 --- a/src/node_requires/exporter/_exporterContracts.ts +++ b/src/node_requires/exporter/_exporterContracts.ts @@ -124,6 +124,7 @@ export type ExportedRoom = { bindings: Record void>; } +export type BaseClass = TemplateBaseClass; export type ExportedTemplate = { name: string; anchorX?: number; @@ -175,7 +176,7 @@ export type ExportedTemplate = { spriteCount: number; texture: string; } | { - baseClass: 'TextBox', + baseClass: 'TextBox'; nineSliceSettings: ITemplate['nineSliceSettings']; texture: string; hoverTexture?: string; @@ -186,6 +187,10 @@ export type ExportedTemplate = { defaultText: string; fieldType: ITemplate['fieldType']; maxTextLength: number; +} | { + baseClass: 'ScrollBox'; + nineSliceSettings: ITemplate['nineSliceSettings']; + texture: string; }); export type ExportedMeta = { diff --git a/src/node_requires/resources/rooms/IRoom.d.ts b/src/node_requires/resources/rooms/IRoom.d.ts index 8962fadb8..e054dba0d 100644 --- a/src/node_requires/resources/rooms/IRoom.d.ts +++ b/src/node_requires/resources/rooms/IRoom.d.ts @@ -108,6 +108,7 @@ interface IRoom extends IScriptableBehaviors { gridX: number; gridY: number; diagonalGrid: boolean; + disableGrid: boolean; simulate: boolean; restrictCamera?: boolean; restrictMinX?: number; diff --git a/src/node_requires/resources/rooms/defaultRoom.ts b/src/node_requires/resources/rooms/defaultRoom.ts index f4534a22a..63f1ba5eb 100644 --- a/src/node_requires/resources/rooms/defaultRoom.ts +++ b/src/node_requires/resources/rooms/defaultRoom.ts @@ -9,6 +9,7 @@ const room = { gridX: 64, gridY: 64, diagonalGrid: false, + disableGrid: false, simulate: true, width: 1280, height: 720, From 2e818c63d5ab8165d343777292f3eff0ab032d75 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Fri, 23 Feb 2024 18:26:04 +1200 Subject: [PATCH 03/31] :bug: Fix buttons skipping their pointer events after being disabled and enabled back --- src/ct.release/templateBaseClasses/PixiButton.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ct.release/templateBaseClasses/PixiButton.ts b/src/ct.release/templateBaseClasses/PixiButton.ts index cf85ac85c..b703a3e04 100644 --- a/src/ct.release/templateBaseClasses/PixiButton.ts +++ b/src/ct.release/templateBaseClasses/PixiButton.ts @@ -27,7 +27,7 @@ export default class PixiButton extends PIXI.Container { this.eventMode = 'none'; } else { this.panel.texture = this.normalTexture; - this.eventMode = 'auto'; + this.eventMode = 'dynamic'; } } From fc1d1c9cb3476781032d1ee4afeba5f87e275305 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Fri, 23 Feb 2024 18:42:11 +1200 Subject: [PATCH 04/31] :bug: Comment out still non-existing class --- src/ct.release/templateBaseClasses/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ct.release/templateBaseClasses/index.ts b/src/ct.release/templateBaseClasses/index.ts index 53a240f43..326e530fb 100644 --- a/src/ct.release/templateBaseClasses/index.ts +++ b/src/ct.release/templateBaseClasses/index.ts @@ -2,7 +2,7 @@ import PixiButton from './PixiButton'; import PixiSpritedCounter from './PixiSpritedCounter'; import PixiScrollingTexture from './PixiScrollingTexture'; import PixiTextBox from './PixiTextBox'; -import PixiScrollBox from './PixiScrollBox'; +// import PixiScrollBox from './PixiScrollBox'; import PixiPanel from './PixiNineSlicePlane'; import PixiText from './PixiText'; import PixiContainer from './PixiContainer'; @@ -76,5 +76,5 @@ export type CopySpritedCounter = Record & PixiSpritedCounter & ICop * It has functionality of both PIXI.Container and ct.js Copies, and implements a scrollbox that * has a scrollbar and clips its contents. */ -export type CopyScrollBox = Record & PixiScrollBox & ICopy; +// export type CopyScrollBox = Record & PixiScrollBox & ICopy; /* eslint-enable @typescript-eslint/no-explicit-any */ From 1c9006f7fef5f0f7af84016534d37989cb9d03c2 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Fri, 23 Feb 2024 19:01:09 +1200 Subject: [PATCH 05/31] :bug: Remove deleted behaviors from opened template editors, and deleted behaviors and templates links from rooms' properties panel --- .../editors/room-editor/room-editor.tag | 20 +++++++++++++++++++ src/riotTags/editors/template-editor.tag | 6 ++++++ 2 files changed, 26 insertions(+) diff --git a/src/riotTags/editors/room-editor/room-editor.tag b/src/riotTags/editors/room-editor/room-editor.tag index d39243d79..58f8fd422 100644 --- a/src/riotTags/editors/room-editor/room-editor.tag +++ b/src/riotTags/editors/room-editor/room-editor.tag @@ -210,12 +210,30 @@ room-editor.aPanel.aView } this.gridOn = !this.gridOn; }; + const checkRefs = deleted => { + let cleaned = false; + if (this.asset.follow === deleted) { + this.asset.follow = -1; + cleaned = true; + console.log(`Removed a template with ID ${deleted} from a room ${this.asset.name}.`); + } + if (this.asset.behaviors.find(b => b === deleted)) { + this.asset.behaviors = this.asset.behaviors.filter(b => b !== deleted); + cleaned = true; + console.log(`Removed a behavior with ID ${deleted} from a room ${this.asset.name}.`); + } + if (cleaned) { + this.update(); + } + }; this.on('mount', () => { window.hotkeys.push('roomEditor'); window.hotkeys.on('Control+g', gridToggleListener); document.addEventListener('keydown', modifiersDownListener); document.addEventListener('keyup', modifiersUpListener); window.addEventListener('blur', blurListener); + window.signals.on('templateRemoved', checkRefs); + window.signals.on('behaviorRemoved', checkRefs); }); this.on('unmount', () => { window.hotkeys.exit('roomEditor'); @@ -223,6 +241,8 @@ room-editor.aPanel.aView document.removeEventListener('keydown', modifiersDownListener); document.removeEventListener('keyup', modifiersUpListener); window.addEventListener('blur', blurListener); + window.signals.off('templateRemoved', checkRefs); + window.signals.off('behaviorRemoved', checkRefs); }); this.lockableTypes = [{ diff --git a/src/riotTags/editors/template-editor.tag b/src/riotTags/editors/template-editor.tag index 10d354883..dc7238cac 100644 --- a/src/riotTags/editors/template-editor.tag +++ b/src/riotTags/editors/template-editor.tag @@ -450,15 +450,21 @@ template-editor.aPanel.aView.flexrow cleaned = true; } } + if (this.asset.behaviors.find(b => b === deleted)) { + this.asset.behaviors = this.asset.behaviors.filter(b => b !== deleted); + cleaned = true; + } if (cleaned) { this.update(); } }; window.signals.on('textureRemoved', checkRefs); window.signals.on('styleRemoved', checkRefs); + window.signals.on('behaviorRemoved', checkRefs); this.on('unmount', () => { window.signals.off('textureRemoved', checkRefs); window.signals.off('styleRemoved', checkRefs); + window.signals.off('behaviorRemoved', checkRefs); }); this.minimizeProps = localStorage.minimizeTemplatesProps === 'yes'; From e68ada8654a29f30a369a2b582d0911c534b6216 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Fri, 23 Feb 2024 19:06:06 +1200 Subject: [PATCH 06/31] :bug: Fix black screen in-game when using a NineSlicePlane --- src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts b/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts index 0f31ed421..54b189610 100644 --- a/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts +++ b/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts @@ -12,6 +12,7 @@ export default class PixiPanel extends PIXI.NineSlicePlane { * when it changes its size. */ updateNineSliceShape: boolean; + baseClass = 'NineSlicePlane'; constructor(t: ExportedTemplate, exts: Record) { if (t?.baseClass !== 'NineSlicePlane') { throw new Error('Don\'t call PixiPanel class directly! Use templates.copy to create an instance instead.'); From 9ebe2303962d423c2df609a3e0dd62336ef5d70c Mon Sep 17 00:00:00 2001 From: emaoshushu Date: Fri, 23 Feb 2024 18:09:30 +0800 Subject: [PATCH 07/31] :globe_with_meridians: Update Chinese Simplified translations (#500 by @emaoshushu) --- app/data/i18n/Chinese Simplified.json | 364 ++++++++++++++++++++++++-- 1 file changed, 336 insertions(+), 28 deletions(-) diff --git a/app/data/i18n/Chinese Simplified.json b/app/data/i18n/Chinese Simplified.json index d5d58a329..646a95609 100644 --- a/app/data/i18n/Chinese Simplified.json +++ b/app/data/i18n/Chinese Simplified.json @@ -118,12 +118,50 @@ "骨骼精灵图形", "骨骼精灵图形", "骨骼精灵图形" + ], + "behavior": [ + "行为", + "行为", + "行为" + ], + "script": [ + "脚本", + "脚本", + "脚本" ] }, "next": "下一个", "previous": "上一个", "undo": "撤销", - "redo": "恢复" + "redo": "恢复", + "addBehavior": "添加一个行为", + "copyNamesList": "将名字复制成一个列表", + "copyNamesArray": "将名称复制为数组元素", + "create": "创建", + "discard": "丢弃", + "experimentalFeature": "这是一个实验性的功能.", + "goBack": "返回", + "sortByDate": "按日期排序", + "sortByName": "按名称排序", + "sortByType": "按类型排序", + "reimportSourceMissing": "无法重新导入: 源文件现在丢失了.", + "dropToImport": "拖到这里导入", + "reimport": "重新导入", + "alignModes": { + "left": "靠左", + "right": "靠右", + "top": "靠上", + "bottom": "靠下", + "center": "居中", + "topLeft": "居左上", + "topCenter": "居中上", + "topRight": "居右上", + "bottomLeft": "居左下", + "bottomCenter": "居中下", + "bottomRight": "居右下", + "fill": "填满", + "Scale": "缩放" + } }, "colorPicker": { "current": "新建", @@ -172,14 +210,23 @@ "downloadAndroidStudio": "下载 Android Studio", "requiresInternetNotice": "此操作需要Internet连接来设置每个项目", "noJdkFound": "没有找到JDK 17 (JAVA HOME环境变量没有设置或没有指向JDK 17). 你可以在这里获得JDK 17:", - "downloadJDK": "下载 JDK 17" + "downloadJDK": "下载 JDK 17", + "goodToGo": "准备好了! 👍", + "nodeJsNotFound": "在您的系统中找不到Node.js.", + "nodeJsDownloadPage": "下载 Node.js", + "nodeJsIcons": "Node.js是可选的,但需要修补Windows可执行文件以添加元数据和图标. 如果你没有安装node.js,你的游戏在Windows上就不会有合适的图标." }, "intro": { "loading": "请稍等: 小猫正在聚集光速!", "newProject": { "input": "项目名称 (字母和数字)", "selectProjectFolder": "选择存储项目的文件夹", - "nameError": "项目名称错误" + "nameError": "项目名称错误", + "header": "新增", + "projectName": "名称:", + "language": "编程语言:", + "saveFolder": "项目文件夹:", + "languageError": "您必须指定一种编程语言" }, "recovery": { "message": "

恢复

ct.js已找到恢复文件. 可能是您的项目未正确保存或在紧急情况下ct.js已关闭. 这是这些文件的最后修改时间:

您选择的文件:{0} {1}
恢复文件: {2} {3}

ct.js应该打开什么文件?

", @@ -209,7 +256,8 @@ "templatesInfo": "你可以通过使用这些模板之一来启动游戏开发.它们只包含占位符图像,但具有有效的机制. 选择项目将为新项目打开一个保存目录选择器", "boosty": "在 Boosty 上支持 ct.js!", "sponsoredBy": "自豪地赞助 $1!", - "supportedBy": "支持了 $1!" + "supportedBy": "支持了 $1!", + "nothingToShowFiller": "这里没有什么可展示的! 尝试下面的示例或创建您自己的项目." }, "onboarding": { "hoorayHeader": "赞! 您成功创建了一个项目!", @@ -263,7 +311,11 @@ "splashScreen": "启动画面:", "splashScreenNotice": "该图像将用于移动端构建. 它应该至少是1920x1920像素大小, 并将调整大小和裁剪图像和横向屏幕方向, 所以确保所有重要的东西都在画面中心", "forceSmoothIcons": "不论渲染设置如何使用平滑的图标", - "forceSmoothSplashScreen": "不管渲染设置如何使用平滑的启动画面图像" + "forceSmoothSplashScreen": "不管渲染设置如何使用平滑的启动画面图像", + "alternativeCtjsLogo": "使用替代的 ct.js 标志", + "alternativeCtjsNotice": "将启动屏幕上的 \"Made with ct.js\" 替换为常规的ct.js标志. 当你的经销商是个混蛋时很有用.", + "customLoadingText": "自定义加载文本", + "customLoadingTextHint": "如果想隐藏加载文本, 可以使用空格(不是空字符串)." }, "modules": { "heading": "Cat模组" @@ -283,13 +335,30 @@ }, "pixelatedRender": "在此处和导出的项目中禁用图像平滑 (保留清晰像素)", "usePixiLegacy": "添加一个传统的, 基于画布的渲染器来支持旧的浏览器和显卡(你的游戏增加~20kb)", - "useTransparent": "", + "useTransparent": "使用透明的PIXI背景", "mobileBuilds": "移动端构建", "screenOrientation": "屏幕方向:", "screenOrientations": { "unspecified": "任何", "landscape": "横向", "portrait": "纵向" + }, + "viewportMode": "视口模式", + "viewportModes": { + "asIs": "不受控", + "fastScale": "快速缩放", + "fastScaleInteger": "按整数快速缩放", + "expand": "扩展", + "scaleFit": "带黑边质量缩放", + "scaleFill": "无黑边质量缩放" + }, + "viewportModesDescriptions": { + "asIs": "禁用任何视口管理; 渲染的画布将被放置在左上角.", + "fastScale": "在不改变分辨率的情况下, 视口将按比例填充屏幕.", + "fastScaleInteger": "视口将位于屏幕中间, 并按整数(x2, x3, x4等)进行缩放.", + "expand": "视口将填满整个屏幕. 相机将扩大以适应新的区域.", + "scaleFit": "视口将按比例填充屏幕, 在基本视口周围留下黑边. 将分辨率更改为与屏幕匹配.", + "scaleFill": "视口填满了屏幕, 扩展了相机以避免黑边. 将分辨率更改为与屏幕匹配." } }, "scripts": { @@ -310,7 +379,12 @@ "none": "无", "minify": "最小化亚索", "obfuscate": "模糊混淆" - } + }, + "assetTree": "资产树", + "assetTreeNote": "你可以在游戏运行时中将资产树导出为res.tree, 但它也会显示你的项目结构, 并为导出的项目增加一些权重.", + "exportAssetTree": "导出资产树", + "exportAssetTypes": "只导出这些资产类型:", + "autocloseDesktop": "当用户按下 \"关闭\" 按钮时退出应用程序." }, "catmodsSettings": "Cat模组设置", "content": { @@ -333,9 +407,15 @@ "confirmDeletionMessage": "您确定要删除此内容类型吗? 它是不可逆的, 并且也将删除该内容类型的所有条目.", "gotoEntries": "进入条目", "entries": "条目", - "array": "数组" + "array": "数组", + "fixedLength": "固定长度" }, - "contentTypes": "内容类型" + "contentTypes": "内容类型", + "main": { + "heading": "主要设置", + "miscHeading": "其他设置", + "backups": "需要保留的备份文件数量:" + } }, "modules": { "author": "这个cat模组的作者", @@ -438,7 +518,8 @@ "spawnType": "形态类型:", "spawnShapes": { "rectangle": "矩形", - "star": "星形" + "star": "星形", + "torus": "环形" }, "width": "宽度:", "height": "高度:", @@ -454,7 +535,32 @@ "alreadyHasAnImportingTexture": "你已经有了一个名为 $1 的纹理。要么删除,要么重命名;尽管你必须导入与之前添加的相同的纹理 :)", "changeGrid": "设置网格大小", "newGridSize": "新的网格大小:", - "setPreviewTexture": "设置预览纹理" + "setPreviewTexture": "设置预览纹理", + "textureMethod": "使用多个帧作为:", + "textureMethods": { + "random": "随机", + "animated": "动画" + }, + "animatedFramerate": "帧率:", + "easingHeader": "缓冲", + "easing": { + "none": "阶梯式", + "linear": "线性", + "smooth": "平滑" + }, + "movementType": "运动类型:", + "movementTypes": { + "linear": "线性", + "accelerated": "使用重力" + }, + "rotateTexture": "沿着运动方向旋转纹理", + "rotationMethod": "纹理旋转:", + "rotationMethods": { + "static": "固定的", + "dynamic": "动态的" + }, + "burstSpacing": "光线之间的间距, 以度为单位:", + "burstStart": "起始方向, 以度为单位:" }, "rooms": { "create": "添加新的", @@ -465,7 +571,8 @@ "global": "全局记事本", "backToHome": "回到文档主页", "modulesPages": "模组文档", - "helpPages": "帮助" + "helpPages": "帮助", + "docsAndNotes": "文档 & 笔记" }, "patreon": { "aboutPatrons": "赞助人是以经常性捐赠的形式表示对Patreon的ComigoGames的支持. 并不是每个人都来自ct.js. 有些是正在使用ComigoGames的其他应用程序. 提示: 如果您是创作者, 并通过Patreon捐赠给ComigoGames, 您将在此处找到指向您页面的链接 — 这就是我对您的创作的小小帮助 :)", @@ -508,7 +615,10 @@ "systemInfoWait": "等一下, 我正在收集数据…", "systemInfoDone": "完成!", "disableAcceleration": "禁用图形加速(需要重启)", - "postAnIssue": "在Github上发布issue" + "postAnIssue": "在Github上发布issue", + "disableVulkan": "禁用Vulkan支持", + "disableVulkanSDHint": "修复了SteamDeck和其他一些Linux系统上 \"不支持WebGL\" 的问题. 需要重启才能生效.", + "restartMessage": "请重新启动应用程序以应用更改." }, "deploy": { "exportDesktop": "导出到桌面", @@ -553,13 +663,15 @@ "project": { "heading": "项目", "startScreen": "返回启动画面", - "startNewWindow": "", + "startNewWindow": "打开新项目窗口", "successZipProject": "已成功将项目压缩到{0}.", "save": "保存项目", "zipProject": "将项目打包到.zip", "openIncludeFolder": "打开\"include\"文件夹", "openProject": "打开项目…", - "openExample": "打开示例项目…" + "openExample": "打开示例项目…", + "convertToJs": "将项目转换为JavaScript", + "confirmationConvertToJs": "这将自动将项目中的所有事件转换为JavaScript. 这种行为是不可逆转的. (但如果在脚本中发现错误, 它将回滚.) 您确定要将这个CoffeeScript项目转换为JavaScript吗?" }, "meta": { "license": "许可证", @@ -608,7 +720,14 @@ "tabMainMenuMeta": "如果以后需要帮助, 您可以在元数据面板中找到所有的官方hub.", "helpPanelReminder": "还有别忘了内置的文档! 我们建议开始您自己的项目前先完成官方教程.", "buttonStartTutorial": "打开教程" - } + }, + "assets": "资产", + "applyAssetsQuestion": "启动前应用资产吗?", + "applyAssetsExplanation": "有些资产没有被应用, 它们的变化不会反映在导出的项目中. 你想现在就申请吗?", + "unsavedAssets": "未保存的资产:", + "runWithoutApplying": "依然启动", + "applyAndRun": "应用并运行", + "cantAddEditor": "不能添加其他编辑器. 请关闭一些带有空间, 样式或串联编辑器的选项卡." }, "docsPanel": { "documentation": "文档", @@ -660,7 +779,16 @@ "folderNotWritable": "您没有权限写入这个文件夹. 选择其他试试", "complete": "文件夹已设置, 一切正常✅" }, - "assetViewer": {}, + "assetViewer": { + "root": "根", + "toggleFolderTree": "切换文件夹树可见性", + "addNewFolder": "新建文件夹", + "newFolderName": "新文件夹", + "unwrapFolder": "打开文件夹", + "confirmDeleteFolder": "您确定要删除此文件夹吗? 它的所有内容也将被删除.", + "confirmUnwrapFolder": "您确定要打开此文件夹吗? 它的所有内容都将放在当前文件夹中.", + "exportBehavior": "导出这个行为" + }, "soundRecorder": { "recorderHeading": "录音机", "record": "开始录音", @@ -687,7 +815,10 @@ "show": "显示图层", "hide": "隐藏图层", "findTileset": "查找图块集", - "addTileLayer": "添加一个平铺层" + "addTileLayer": "添加一个平铺层", + "addTileLayerFirst": "首先在左侧面板添加一个贴图层!", + "cacheLayer": "缓存这个图层", + "cacheLayerWarning": "缓存极大地加快了贴图层的渲染速度. 只有当你需要在游戏过程中动态改变这个贴图层时, 你才应该禁用这个选项." }, "roomView": { "name": "名称:", @@ -760,9 +891,55 @@ "addCopies": "添加副本", "addTiles": "添加图块集", "manageBackgrounds": "管理背景", - "roomProperties": "房间属性" + "roomProperties": "房间属性", + "uiTools": "UI 工具" }, - "resetView": "重置视角" + "resetView": "重置视角", + "viewportHeading": "视口", + "followTemplate": "遵循一个模板:", + "followCodeHint": "了解如何用代码进一步调整此特性", + "sendToBack": "发送到后面", + "sendToFront": "发送到前面", + "emptyTextFiller": "<空>", + "uiTools": { + "noSelectionNotice": "选择副本以对其对齐或更改其文本标签.", + "textSettings": "文本设置", + "customText": "文本", + "customTextSize": "字体大小", + "wordWrapWidth": "以这个宽度环绕", + "textAlignment": "对齐方式", + "alignmentSettings": "自动对齐", + "enableAutoAlignment": "启用自动对齐", + "autoAlignHint": "当打开时, 此元素将自动对齐自己相对于房间的指定部分的边界, 定义为 \"框架\" . 当使用扩展和质量缩放而不使用信箱渲染模式时, 它是有用的, 因为相机会在这些视口模式中改变比例.", + "frame": "帧位置,以%为单位:", + "framePadding": "帧填充, 以像素为单位:", + "innerFrameMarker": "内框", + "outerFrameMarker": "外框", + "constrains": "约束:", + "constrainsTooltips": { + "left": "将间隙的大小锁定在框架的左侧", + "right": "将间隙的大小锁定在框架的右侧", + "top": "将间隙的大小锁定在框架的顶部", + "bottom": "将间隙的大小锁定在框架的底部", + "centerVertical": "放置垂直相对于中心的框架", + "centerHorizontal": "放置水平相对于中心的框架" + }, + "bindings": "绑定", + "bindingsHelp": "您可以在这里编写JavaScript表达式, 它将在每一帧中求值, 并将更新副本的相应属性.", + "bindingNames": { + "text": "文本:", + "disabled": "禁用:", + "visible": "可见性:", + "tex": "纹理:", + "tint": "色调:", + "count": "精灵数量:" + }, + "bindingTypes": { + "string": "字符串值", + "boolean": "布尔值(true或false)", + "number": "数字" + } + } }, "styleView": { "active": "激活", @@ -839,7 +1016,11 @@ "marginY": "边距 Y:", "offX": "位移 X:", "offY": "位移 Y:", - "blankTextureNotice": "将图像导出为透明矩形, 因此在游戏中不可见. 用于为ct.js编辑器设置占位符, 同时保持包的大小较轻." + "blankTextureNotice": "将图像导出为透明矩形, 因此在游戏中不可见. 用于为ct.js编辑器设置占位符, 同时保持包的大小较轻.", + "slicing": "切片", + "viewSettings": "查看设置", + "exportSettings": "导出设置", + "axisExplanation": "定义哪个位置被计数为副本的(0;0)位置, 并影响它相对于网格的位置, 以及它围绕哪个点旋转." }, "fontView": { "italic": "是否斜体?", @@ -898,7 +1079,42 @@ "screen": "遮蔽 (变亮)" }, "animationFPS": "动画 FPS:", - "loopAnimation": "循环动画" + "loopAnimation": "循环动画", + "hoverTexture": "悬停时纹理", + "pressedTexture": "按下时纹理", + "disabledTexture": "禁用时纹理", + "defaultText": "默认文本:", + "textStyle": "文本样式", + "fieldType": "字段类型", + "fieldTypes": { + "text": "文本", + "number": "数字", + "email": "邮件", + "password": "密码" + }, + "useCustomSelectionColor": "为选定的文本使用自定义颜色", + "maxLength": "最大长度:", + "baseClass": { + "AnimatedSprite": "动画精灵", + "Text": "文本", + "NineSlicePlane": "面板", + "Container": "容器", + "Button": "按钮", + "RepeatingTexture": "重复纹理", + "SpritedCounter": "精灵计数器", + "TextBox": "文本盒子" + }, + "nineSliceTop": "顶部切片, 以像素为单位", + "nineSliceRight": "右侧切片, 以像素为单位", + "nineSliceBottom": "底部切片, 以像素为单位", + "nineSliceLeft": "左侧切片, 以像素为单位", + "autoUpdateNineSlice": "自动更新碰撞形状", + "autoUpdateNineSliceHint": "如果一个面板改变了它的大小, 它将自动更新它的碰撞形状. 对于纯粹的装饰性元素, 或者那些在创建后从未改变其大小的元素, 通常不需要这样做. 你仍然可以在任何时候用 u.reshapeNinePatch(this) 调用更新它的碰撞形状.", + "panelHeading": "纹理切片设置", + "scrollSpeedX": "X轴滚动速度:", + "scrollSpeedY": "Y轴滚动速度:", + "isUi": "使用UI时间", + "defaultCount": "默认精灵数:" }, "assetInput": { "changeAsset": "切换资产", @@ -933,7 +1149,9 @@ "pointer": "指针事件", "misc": "杂项", "animation": "动画", - "timers": "计时器" + "timers": "计时器", + "input": "输入", + "app": "应用" }, "coreEvents": { "OnCreate": "创建时", @@ -956,7 +1174,11 @@ "OnFrameChange": "帧改变", "OnAnimationLoop": "动画循环", "OnAnimationComplete": "动画完成", - "Timer": "计时器 $1" + "Timer": "计时器 $1", + "OnAppFocus": "应用运行中", + "OnAppBlur": "应用在后台", + "OnTextChange": "文本改变时", + "OnTextInput": "文本输入时" }, "coreParameterizedNames": { "OnActionPress": "当 %%action%% 按下", @@ -976,7 +1198,9 @@ }, "coreEventsLocals": { "OnActionDown_value": "当前动作的值", - "OnActionPress_value": "当前动作的值" + "OnActionPress_value": "当前动作的值", + "OnTextChange_value": "新的文本值", + "OnTextInput_value": "新的文本值" }, "coreEventsDescriptions": { "OnCreate": "在创建副本时触发.", @@ -990,7 +1214,91 @@ "OnActionDown": "如果动作的输入是活动的, 则运行每一帧.", "OnAnimationLoop": "每次动画重新启动时触发.", "OnAnimationComplete": "非循环动画结束时触发.", - "Timer": "用这个设置定时器的持续时间, 单位为秒. 定时器$1 = 3;" - } + "Timer": "用这个设置定时器的持续时间, 单位为秒. 定时器$1 = 3;", + "OnAppFocus": "当用户返回到您的应用程序时触发.", + "OnAppBlur": "当用户从你的游戏切换到其他 — 通过切换标签, 切换到另一个窗口, 或最小化游戏时触发.", + "OnTextChange": "当用户通过单击该字段外部或按 Enter 键完成对该字段的编辑时触发.", + "OnTextInput": "每当用户更改此字段的值时触发." + }, + "jumpToProblem": "跳转到问题处", + "staticEventWarning": "此事件使此行为成为静态的. 你将无法在游戏中动态添加或删除它的行为API, 但除此之外, 它将是完全可用的.", + "restrictedEventWarning": "此事件只适用于具有以下基类的模板: $1. 当应用于其他基类的模板时, 此事件将不起作用.", + "baseClassWarning": "此事件不适用于当前基类" + }, + "assetConfirm": { + "confirmHeading": "选择动作", + "confirmParagraph": "$1 资产有未应用的更改. 你想用它做什么?" + }, + "behaviorEditor": { + "customFields": "自定义字段", + "customFieldsDescription": "自定义字段可用于向资产编辑器添加额外的输入. 这些字段将在属性面板中可用, 并且可以作为 \"this\" 对象的属性使用. 例如, 如果您创建了一个名称为 \"sausage\" 的字段, 您可以在事件中将其读取为 \"this.sausage\"." + }, + "createAsset": { + "newAsset": "新的资产", + "placeholderTexture": "占位纹理", + "assetGallery": "内置资产库", + "behaviorTemplate": "模板可用行为", + "behaviorRoom": "房间可用行为", + "behaviorImport": "从文件导入", + "behaviorMissingCatmods": "您的项目缺少这些catmods: $1. 首先启用它们.", + "formatError": "打开的文件格式不正确, 或者可能是为不兼容的ct.js版本制作的." + }, + "exporterError": { + "exporterErrorHeader": "导出项目时发生错误", + "errorSource": "$1 调用 $2", + "clueSolutions": { + "syntax": "这是代码中的语法错误. 转到资产并修复它 — 代码编辑器将突出显示有问题的地方.", + "eventConfiguration": "其中一个事件配置错误, 字段为空. 转到资源并编辑其事件的参数.", + "emptySound": "您的一个声音没有附加任何声音文件. 将声音文件导入到它或删除此空声音.", + "emptyEmitter": "你的一个粒子系统发射器的纹理丢失了. 你需要为它设置纹理或者移除发射器.", + "windowsFileLock": "这是一个windows特有的锁定文件问题. 确保你已经关闭了所有启动游戏的外部浏览器, 然后再次尝试导出. 如果没有帮助, 重新启动ct.js.", + "noTemplateTexture": "其中一个模板缺少纹理. 你需要为它设置纹理." + }, + "stacktrace": "调用栈", + "jumpToProblem": "跳转到问题处", + "saveAndQuit": "保存和退出" + }, + "folderEditor": { + "title": "文件夹设置", + "icon": "图表:", + "color": "颜色:" + }, + "languageSelector": { + "chooseLanguageHeader": "选择你的编程语言", + "chooseLanguageExplanation": "这是你编写事件来描述游戏玩法逻辑的语言. 以前, 所有的项目都使用JavaScript+TypeScript. 请注意, 您只能将CoffeeScript项目转换为JavaScript, 反过来则不行, 因此请谨慎选择!", + "coffeeScriptDescription": "语法简单, 适合初学者的好语言. 如果您之前没有代码编程的经验, 或者您喜欢Python, 请选择这种语言.", + "pickCoffeeScript": "我选择CoffeeScript!", + "jsAndTs": "JavaScript (以及 TypeScript)", + "jsTsDescription": "web语言. 它的语法更复杂, 但它有编辑器内的错误高亮显示和代码建议. 如果你以前使用过JS, C#或Java代码, 请选择它.", + "pickJsTs": "我选择JavaScript!", + "acceptAndSpecifyDirectory": "接受并选择项目文件夹" + }, + "newAssetPrompt": { + "heading": "创建新资产", + "selectNewName": "新资产名称:" + }, + "scriptView": { + "runAutomatically": "在游戏开始时执行", + "language": "语言:", + "convertToJavaScript": "转换为JavaScript" + }, + "soundView": { + "variants": "变奏", + "addVariant": "从文件中添加…", + "preload": "游戏开始时加载", + "volume": "音量", + "pitch": "音高", + "distortion": "失真", + "effects": "效果", + "reverb": "混响", + "reverbDuration": "时长", + "reverbDecay": "衰变", + "reverseReverb": "反转", + "equalizer": "均衡器", + "hertz": "赫兹", + "positionalAudio": "3D 音效", + "falloff": "衰减:", + "refDistance": "衰减开始:", + "positionalAudioHint": "这只影响 sounds.playAt 方法. 衰减设置了声音消失的速度, 0表示没有衰减, 大值表示几乎立即下降到沉默. 衰减开始指的是声音开始消失的距离, 1为屏幕的一半." } -} +} \ No newline at end of file From 44fc41b897f5c6f4761ab8f2711d12d903777c0a Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Fri, 23 Feb 2024 22:42:40 +1200 Subject: [PATCH 08/31] :zap: Move properties assignment for AnimatedSprite from the Copy mixin into the pixi.js-based class. --- .../templateBaseClasses/PixiAnimatedSprite.ts | 6 ++++++ .../templateBaseClasses/PixiNineSlicePlane.ts | 1 + src/ct.release/templates.ts | 13 +------------ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/ct.release/templateBaseClasses/PixiAnimatedSprite.ts b/src/ct.release/templateBaseClasses/PixiAnimatedSprite.ts index 1a38f6176..cac25c237 100644 --- a/src/ct.release/templateBaseClasses/PixiAnimatedSprite.ts +++ b/src/ct.release/templateBaseClasses/PixiAnimatedSprite.ts @@ -17,6 +17,12 @@ export default class PixiAnimateSprite extends PIXI.AnimatedSprite { (exts.scaleX as number) ?? 1, (exts.scaleY as number) ?? 1 ); + this.blendMode = t.blendMode || PIXI.BLEND_MODES.NORMAL; + this.loop = t.loopAnimation; + this.animationSpeed = t.animationFPS / 60; + if (t.playAnimationOnStart) { + this.play(); + } return this; } } diff --git a/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts b/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts index 54b189610..f6b1d9829 100644 --- a/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts +++ b/src/ct.release/templateBaseClasses/PixiNineSlicePlane.ts @@ -35,5 +35,6 @@ export default class PixiPanel extends PIXI.NineSlicePlane { this.height = baseHeight * (exts.scaleY as number); } uLib.reshapeNinePatch(this as CopyPanel); + this.blendMode = t.blendMode || PIXI.BLEND_MODES.NORMAL; } } diff --git a/src/ct.release/templates.ts b/src/ct.release/templates.ts index bb584fc32..cfb4e9b79 100644 --- a/src/ct.release/templates.ts +++ b/src/ct.release/templates.ts @@ -277,18 +277,7 @@ const Copy = function ( this.baseClass = template.baseClass; // Early linking so that `this.parent` is available in OnCreate events this.parent = container; - if (template.baseClass === 'AnimatedSprite') { - this._tex = template.texture; - const me = this as CopyAnimatedSprite; - me.blendMode = template.blendMode || PIXI.BLEND_MODES.NORMAL; - me.loop = template.loopAnimation; - me.animationSpeed = template.animationFPS / 60; - if (template.playAnimationOnStart) { - me.play(); - } - } else if (template.baseClass === 'NineSlicePlane') { - const me = this as CopyPanel; - me.blendMode = template.blendMode || PIXI.BLEND_MODES.NORMAL; + if (template.baseClass === 'AnimatedSprite' || template.baseClass === 'NineSlicePlane') { this._tex = template.texture; } (this as Mutable).behaviors = [...template.behaviors]; From 6859d7b577612223c8fd7472ced65940fb704730 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Fri, 23 Feb 2024 22:43:23 +1200 Subject: [PATCH 09/31] :bug: Fix nine slice panes' tint being reset on click and not saved in the room-editor --- src/node_requires/roomEditor/entityClasses/Copy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_requires/roomEditor/entityClasses/Copy.ts b/src/node_requires/roomEditor/entityClasses/Copy.ts index ee549d466..578219586 100644 --- a/src/node_requires/roomEditor/entityClasses/Copy.ts +++ b/src/node_requires/roomEditor/entityClasses/Copy.ts @@ -155,8 +155,8 @@ class Copy extends PIXI.Container { } get tint(): PIXI.ColorSource { return this.sprite?.tint || - this.text?.tint || this.nineSlicePlane?.tint || + this.text?.tint || this.tilingSprite?.tint || this.#tint; } From e60557b79e6576a7193c088e1a9184ccf4a2fe30 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Fri, 23 Feb 2024 22:57:03 +1200 Subject: [PATCH 10/31] :bug: Fix not being able to set tint with this.tint or the room editor to buttons. --- src/ct.release/templateBaseClasses/PixiButton.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ct.release/templateBaseClasses/PixiButton.ts b/src/ct.release/templateBaseClasses/PixiButton.ts index b703a3e04..bdccfdf05 100644 --- a/src/ct.release/templateBaseClasses/PixiButton.ts +++ b/src/ct.release/templateBaseClasses/PixiButton.ts @@ -38,6 +38,16 @@ export default class PixiButton extends PIXI.Container { this.textLabel.text = val; } + /** + * The color of the button's texture. + */ + get tint(): pixiMod.ColorSource { + return this.panel.tint; + } + set tint(val: pixiMod.ColorSource) { + this.panel.tint = val; + } + constructor(t: ExportedTemplate, exts: Record) { if (t?.baseClass !== 'Button') { throw new Error('Don\'t call PixiButton class directly! Use templates.copy to create an instance instead.'); From 8f5fb39b4847ff17a5d54323d30af77cc1f63e53 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Fri, 23 Feb 2024 22:57:54 +1200 Subject: [PATCH 11/31] :bug: Remove a now unused variable from ct.release/templates --- src/ct.release/templates.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ct.release/templates.ts b/src/ct.release/templates.ts index cfb4e9b79..2989c2697 100644 --- a/src/ct.release/templates.ts +++ b/src/ct.release/templates.ts @@ -11,7 +11,7 @@ import type * as pixiMod from 'node_modules/pixi.js'; declare var PIXI: typeof pixiMod; import type {ExportedRoom, ExportedTemplate, TextureShape} from '../node_requires/exporter/_exporterContracts'; -import {CopyAnimatedSprite, CopyButton, CopyPanel, baseClassToPixiClass} from './templateBaseClasses'; +import {CopyButton, CopyPanel, baseClassToPixiClass} from './templateBaseClasses'; let uid = 0; From e8417f5d4bc36d1978e67d3d167e4ea092c86659 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 00:33:46 +1200 Subject: [PATCH 12/31] :bug: Fix lost typings for base classes --- src/node_requires/events/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node_requires/events/index.ts b/src/node_requires/events/index.ts index db6595e25..6e36779b2 100644 --- a/src/node_requires/events/index.ts +++ b/src/node_requires/events/index.ts @@ -251,7 +251,7 @@ const getArgumentsTypeScript = (event: IEventDeclaration): string => { }; import {baseClassToTS} from '../resources/templates'; -const baseTypes = `import {BasicCopy, ${Object.values(baseClassToTS).join(', ')}} from 'src/ct.release/templates';`; +const baseTypes = `import {BasicCopy} from 'src/ct.release/templates';import {${Object.values(baseClassToTS).join(', ')}} from 'src/ct.release/templateBaseClasses/index';`; const importEventsFromCatmod = (manifest: ICatmodManifest, catmodName: string): void => { if (manifest.events) { From df5c9509545dc4321bddc39e4ddd21861fd22e43 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 00:45:39 +1200 Subject: [PATCH 13/31] :bug: Fix not being able to deselect items after sorting *and* moving them --- src/node_requires/roomEditor/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/node_requires/roomEditor/index.ts b/src/node_requires/roomEditor/index.ts index d09a1064c..12c352df9 100644 --- a/src/node_requires/roomEditor/index.ts +++ b/src/node_requires/roomEditor/index.ts @@ -657,6 +657,7 @@ class RoomEditor extends PIXI.Application { beforeTileLayers, afterTileLayers }); + this.transformer.setup(); } drawSelection(entities: Iterable): void { this.selectionOverlay.clear(); From cad2776a328ba7336d4ad001614e4e085c597ce6 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 01:12:34 +1200 Subject: [PATCH 14/31] :zap: Remember whether the grid was disabled in a specific room --- src/riotTags/editors/room-editor/room-editor.tag | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/riotTags/editors/room-editor/room-editor.tag b/src/riotTags/editors/room-editor/room-editor.tag index 58f8fd422..8f3ef8ef9 100644 --- a/src/riotTags/editors/room-editor/room-editor.tag +++ b/src/riotTags/editors/room-editor/room-editor.tag @@ -209,6 +209,7 @@ room-editor.aPanel.aView return; } this.gridOn = !this.gridOn; + this.asset.disableGrid = !this.gridOn; }; const checkRefs = deleted => { let cleaned = false; @@ -391,13 +392,14 @@ room-editor.aPanel.aView this.pixiEditor.simulate = !this.pixiEditor.simulate; }; - this.gridOn = true; + this.gridOn = !this.asset.disableGrid; this.gridMenu = { opened: false, items: [{ label: this.voc.gridOff, click: () => { this.gridOn = !this.gridOn; + this.asset.disableGrid = !this.gridOn; }, type: 'checkbox', checked: () => !this.gridOn, From ff3c21cf9759df582afa0029ea26919e7f167f7b Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 01:43:59 +1200 Subject: [PATCH 15/31] :zap: Add a menu item to duplicate emitter tandems Closes #498 --- src/node_requires/resources/emitterTandems/index.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/node_requires/resources/emitterTandems/index.ts b/src/node_requires/resources/emitterTandems/index.ts index 644785e8e..0fec888d3 100644 --- a/src/node_requires/resources/emitterTandems/index.ts +++ b/src/node_requires/resources/emitterTandems/index.ts @@ -1,3 +1,5 @@ +import generateGUID from '../../generateGUID'; +import {IAssetContextItem, addAsset} from '..'; import {promptName} from '../promptName'; const getThumbnail = function getThumbnail(): string { @@ -32,6 +34,17 @@ const createNewTandem = async (): Promise => { export const areThumbnailsIcons = true; +export const assetContextMenuItems: IAssetContextItem[] = [{ + icon: 'copy', + vocPath: 'common.duplicate', + action: (asset: ITandem, collection, folder): void => { + const newTandem = structuredClone(asset) as ITandem & {uid: string}; + newTandem.uid = generateGUID(); + newTandem.name += `_${newTandem.uid.slice(0, 4)}`; + addAsset(newTandem, folder); + } +}]; + export { getThumbnail, defaultEmitter, From 86f52143165315d33435641c34a5122661feb9cf Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 03:05:13 +1200 Subject: [PATCH 16/31] :bug: Fix broken QWERTY and Shift+S hotkeys in room editors --- .../editors/room-editor/room-editor.tag | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/riotTags/editors/room-editor/room-editor.tag b/src/riotTags/editors/room-editor/room-editor.tag index 8f3ef8ef9..5a044633d 100644 --- a/src/riotTags/editors/room-editor/room-editor.tag +++ b/src/riotTags/editors/room-editor/room-editor.tag @@ -3,7 +3,7 @@ The room to edit @attribute ondone (riot function) -room-editor.aPanel.aView +room-editor.aPanel.aView(data-hotkey-scope="{asset.uid}") canvas(ref="canvas" oncontextmenu="{openMenus}") // Toolbar .room-editor-aToolsetHolder @@ -13,7 +13,7 @@ room-editor.aPanel.aView class="{active: currentTool === 'select'}" title="{voc.tools.select} (Q)" data-hotkey="q" - data-hotkey-require-scope="rooms" + data-hotkey-require-scope="{asset.uid}" ) svg.feather use(xlink:href="#cursor") @@ -22,7 +22,7 @@ room-editor.aPanel.aView class="{active: currentTool === 'addCopies'}" title="{voc.tools.addCopies} (W)" data-hotkey="w" - data-hotkey-require-scope="rooms" + data-hotkey-require-scope="{asset.uid}" ) svg.feather use(xlink:href="#template") @@ -31,7 +31,7 @@ room-editor.aPanel.aView class="{active: currentTool === 'addTiles'}" title="{voc.tools.addTiles} (E)" data-hotkey="e" - data-hotkey-require-scope="rooms" + data-hotkey-require-scope="{asset.uid}" ) svg.feather use(xlink:href="#grid") @@ -40,7 +40,7 @@ room-editor.aPanel.aView class="{active: currentTool === 'manageBackgrounds'}" title="{voc.tools.manageBackgrounds} (R)" data-hotkey="r" - data-hotkey-require-scope="rooms" + data-hotkey-require-scope="{asset.uid}" ) svg.feather use(xlink:href="#image") @@ -49,7 +49,7 @@ room-editor.aPanel.aView class="{active: currentTool === 'uiTools'}" title="{voc.tools.uiTools} (T)" data-hotkey="t" - data-hotkey-require-scope="rooms" + data-hotkey-require-scope="{asset.uid}" ) svg.feather use(xlink:href="#ui") @@ -58,7 +58,7 @@ room-editor.aPanel.aView class="{active: currentTool === 'roomProperties'}" title="{voc.tools.roomProperties} (Y)" data-hotkey="y" - data-hotkey-require-scope="rooms" + data-hotkey-require-scope="{asset.uid}" ) svg.feather use(xlink:href="#settings") @@ -132,7 +132,7 @@ room-editor.aPanel.aView onchange="{changeSimulated}" checked="{pixiEditor?.simulate}" data-hotkey="S" - data-hotkey-require-scope="rooms" + data-hotkey-require-scope="{asset.uid}" ) span {voc.simulate} button(onclick="{openZoomMenu}") @@ -228,7 +228,6 @@ room-editor.aPanel.aView } }; this.on('mount', () => { - window.hotkeys.push('roomEditor'); window.hotkeys.on('Control+g', gridToggleListener); document.addEventListener('keydown', modifiersDownListener); document.addEventListener('keyup', modifiersUpListener); From f60b0acecf51bbd0e3d4a85b9c9dccf05b80eb22 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 03:25:50 +1200 Subject: [PATCH 17/31] :bug: Fix hotkeys being ignored if non-english keyboard layout was in use --- src/node_requires/hotkeys.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/node_requires/hotkeys.js b/src/node_requires/hotkeys.js index 52c21495d..c6a06446e 100644 --- a/src/node_requires/hotkeys.js +++ b/src/node_requires/hotkeys.js @@ -62,11 +62,20 @@ const getPriority = function getPriority(elt) { return 0; }; -const getCode = e => '' +const getCode = e => { + let letter; + if (e.code.startsWith('Key')) { + letter = e.shiftKey ? e.code.slice(3) : e.code.slice(3).toLowerCase(); + } else { + letter = e.key; + } + const code = '' .concat(e.ctrlKey ? 'Control+' : '') .concat(e.altKey ? 'Alt+' : '') .concat(e.metaKey ? 'Meta+' : '') - .concat(e.key); + .concat(letter); + return code; +}; const listenerRef = Symbol('keydownListener'); const offDomEventsRef = Symbol('offDomEventsRef'); From 316cfa1fb7f1a3b60b21417ce7ccf95b17118e1e Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 03:33:46 +1200 Subject: [PATCH 18/31] :bug: Fix double caching of tile layers that breaks rooms.merge call Closes #501 --- src/ct.release/rooms.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ct.release/rooms.ts b/src/ct.release/rooms.ts index 7850e7406..87ad94c78 100644 --- a/src/ct.release/rooms.ts +++ b/src/ct.release/rooms.ts @@ -442,7 +442,6 @@ const roomsLib = { target.tileLayers.push(tl); target.addChild(tl); generated.tileLayers.push(tl); - tl.cache(); } for (const t of template.objects) { const c = templatesLib.copyIntoRoom(t.template, t.x, t.y, target, { From 37bb3b15b679fd1f777fbafbd236319aa039411c Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 04:37:30 +1200 Subject: [PATCH 19/31] :bug: Fix discardio not removing old keys from asset object before assigning new ones, which led, for example, to not being able to disable stroke/fill/shadow settings of style assets --- src/node_requires/riotMixins/discardio.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/node_requires/riotMixins/discardio.ts b/src/node_requires/riotMixins/discardio.ts index 9f1d8dbcc..e5495677b 100644 --- a/src/node_requires/riotMixins/discardio.ts +++ b/src/node_requires/riotMixins/discardio.ts @@ -28,7 +28,10 @@ const discardio = (riotTag: IRiotTag) => { riotTag.asset.lastmod = Number(new Date()); const sourceObject = discardioSources.get(riotTag); const changedObject = riotTag.asset; - // update the innards of the object without replacing it completely + // update the innards of the object without creating a new one + for (const key of Object.keys(sourceObject)) { + delete sourceObject[key as keyof typeof sourceObject]; + } Object.assign(sourceObject, changedObject); window.signals.trigger('assetChanged', riotTag.asset.uid); window.signals.trigger(`${riotTag.asset.type}Changed`, riotTag.asset.uid); From c2c670d9ff9fc8a4f718c9d5064a6162368ab78a Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 04:57:09 +1200 Subject: [PATCH 20/31] :zap: Transparent background in style editor. Make the preview occupy the available space in the editor. --- src/riotTags/editors/style-editor.tag | 18 +++++++++++++++--- src/styl/tags/editors/style-editor.styl | 2 ++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/riotTags/editors/style-editor.tag b/src/riotTags/editors/style-editor.tag index 47276e210..a1f60ff64 100644 --- a/src/riotTags/editors/style-editor.tag +++ b/src/riotTags/editors/style-editor.tag @@ -157,12 +157,13 @@ style-editor.aPanel.aView(class="{opts.class}") this.tab = tab; }; this.on('mount', () => { - const width = 800; - const height = 500; + const bounds = this.refs.canvasSlot.getBoundingClientRect(); + const width = Math.floor(bounds.width); + const height = Math.floor(bounds.height); this.pixiApp = new PIXI.Application({ width, height, - transparent: true + backgroundAlpha: 0 }); this.refs.canvasSlot.appendChild(this.pixiApp.view); @@ -195,6 +196,17 @@ style-editor.aPanel.aView(class="{opts.class}") this.on('updated', () => { this.refreshStyleTexture(); }); + const resizeCanvas = () => { + const bounds = this.refs.canvasSlot.getBoundingClientRect(); + this.pixiApp.renderer.resize(Math.floor(bounds.width), Math.floor(bounds.height)); + for (const label of this.labels) { + label.x = bounds.width / 2; + } + }; + window.addEventListener('resize', resizeCanvas); + this.on('unmount', () => { + window.removeEventListener('resize', resizeCanvas); + }); this.selectingTexture = false; diff --git a/src/styl/tags/editors/style-editor.styl b/src/styl/tags/editors/style-editor.styl index 68f423936..bf51582c0 100644 --- a/src/styl/tags/editors/style-editor.styl +++ b/src/styl/tags/editors/style-editor.styl @@ -17,3 +17,5 @@ style-editor, [data-is="style-editor"] margin-bottom 0.5rem .flexfix-footer margin-top 0.5rem +.style-editor-aPreview + overflow hidden From cceb28450342be45d62d084df7e4485cf9fe8a3d Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 16:37:09 +1200 Subject: [PATCH 21/31] :sparkles: Export and import emitter tandems from files --- app/data/i18n/Brazilian Portuguese.json | 2 +- app/data/i18n/Chinese Simplified.json | 4 +- app/data/i18n/Comments.json | 4 +- app/data/i18n/Debug.json | 4 +- app/data/i18n/Dutch.json | 2 +- app/data/i18n/English.json | 7 +- app/data/i18n/Russian.json | 7 +- app/data/i18n/Turkish.json | 2 +- .../resources/emitterTandems/index.ts | 80 ++++++++++++--- src/riotTags/shared/create-asset-menu.tag | 97 +++++++++++-------- 10 files changed, 141 insertions(+), 68 deletions(-) diff --git a/app/data/i18n/Brazilian Portuguese.json b/app/data/i18n/Brazilian Portuguese.json index 568342506..88736d008 100644 --- a/app/data/i18n/Brazilian Portuguese.json +++ b/app/data/i18n/Brazilian Portuguese.json @@ -205,7 +205,7 @@ "assetGallery": "Galeria de ativos integrada", "behaviorTemplate": "Compartamento para templates", "behaviorRoom": "Comportamento para salas", - "behaviorImport": "Importar do arquivo", + "importFromFile": "Importar do arquivo", "behaviorMissingCatmods": "Seu projeto está faltando estes catmods: $1. Habilite-os primeiro.", "formatError": "O arquivo aberto está com formato malformado ou, talvez, tenha sido feito para uma versão incompatível do ct.js." }, diff --git a/app/data/i18n/Chinese Simplified.json b/app/data/i18n/Chinese Simplified.json index 646a95609..db7d7c54a 100644 --- a/app/data/i18n/Chinese Simplified.json +++ b/app/data/i18n/Chinese Simplified.json @@ -1239,7 +1239,7 @@ "assetGallery": "内置资产库", "behaviorTemplate": "模板可用行为", "behaviorRoom": "房间可用行为", - "behaviorImport": "从文件导入", + "importFromFile": "从文件导入", "behaviorMissingCatmods": "您的项目缺少这些catmods: $1. 首先启用它们.", "formatError": "打开的文件格式不正确, 或者可能是为不兼容的ct.js版本制作的." }, @@ -1301,4 +1301,4 @@ "refDistance": "衰减开始:", "positionalAudioHint": "这只影响 sounds.playAt 方法. 衰减设置了声音消失的速度, 0表示没有衰减, 大值表示几乎立即下降到沉默. 衰减开始指的是声音开始消失的距离, 1为屏幕的一半." } -} \ No newline at end of file +} diff --git a/app/data/i18n/Comments.json b/app/data/i18n/Comments.json index e392f2f4a..aeb2a44a2 100644 --- a/app/data/i18n/Comments.json +++ b/app/data/i18n/Comments.json @@ -1239,7 +1239,7 @@ "assetGallery": "", "behaviorTemplate": "", "behaviorRoom": "", - "behaviorImport": "", + "importFromFile": "", "behaviorMissingCatmods": "", "formatError": "" }, @@ -1301,4 +1301,4 @@ "refDistance": "", "positionalAudioHint": "" } -} \ No newline at end of file +} diff --git a/app/data/i18n/Debug.json b/app/data/i18n/Debug.json index 703cf96ae..c29c4ede8 100644 --- a/app/data/i18n/Debug.json +++ b/app/data/i18n/Debug.json @@ -1239,7 +1239,7 @@ "assetGallery": "createAsset.assetGallery", "behaviorTemplate": "createAsset.behaviorTemplate", "behaviorRoom": "createAsset.behaviorRoom", - "behaviorImport": "createAsset.behaviorImport", + "importFromFile": "createAsset.importFromFile", "behaviorMissingCatmods": "createAsset.behaviorMissingCatmods", "formatError": "createAsset.formatError" }, @@ -1301,4 +1301,4 @@ "refDistance": "soundView.refDistance", "positionalAudioHint": "soundView.positionalAudioHint" } -} \ No newline at end of file +} diff --git a/app/data/i18n/Dutch.json b/app/data/i18n/Dutch.json index ef5187f3f..37f52d696 100644 --- a/app/data/i18n/Dutch.json +++ b/app/data/i18n/Dutch.json @@ -1240,7 +1240,7 @@ "assetGallery": "Ingebouwde asset gallerij", "behaviorTemplate": "Gedrag voor templates", "behaviorRoom": "Gedrag voor kamers", - "behaviorImport": "Uit een bestand importeren", + "importFromFile": "Uit een bestand importeren", "behaviorMissingCatmods": "Jouw project mist deze catmods: $1. Activeer ze eerst.", "formatError": "Het geopende bestand heeft is slechtgevormd of, misschien was het bedoeld voor een incompatibele versie van ct.js." }, diff --git a/app/data/i18n/English.json b/app/data/i18n/English.json index 53498014c..9d0b7b4ef 100644 --- a/app/data/i18n/English.json +++ b/app/data/i18n/English.json @@ -180,7 +180,8 @@ "unwrapFolder": "Unwrap the folder", "confirmDeleteFolder": "Are you sure you want to delete this folder? All its contents will be deleted as well.", "confirmUnwrapFolder": "Are you sure you want to unwrap this folder? All its contents will be placed in the current folder.", - "exportBehavior": "Export this behavior" + "exportBehavior": "Export this behavior", + "exportTandem": "Export this emitter tandem" }, "behaviorEditor": { "customFields": "Custom Fields", @@ -213,7 +214,7 @@ "assetGallery": "Built-in asset gallery", "behaviorTemplate": "Behavior for templates", "behaviorRoom": "Behavior for rooms", - "behaviorImport": "Import from file", + "importFromFile": "Import from file", "behaviorMissingCatmods": "Your project is missing these catmods: $1. Enable them first.", "formatError": "The opened file has a malformed format or, perhaps, was made for an incompatible version of ct.js." }, @@ -1301,4 +1302,4 @@ "isUi": "Use UI time", "defaultCount": "Default sprite count:" } -} \ No newline at end of file +} diff --git a/app/data/i18n/Russian.json b/app/data/i18n/Russian.json index fdc4dfcdd..0e2f60cbb 100644 --- a/app/data/i18n/Russian.json +++ b/app/data/i18n/Russian.json @@ -802,7 +802,8 @@ "unwrapFolder": "Вывернуть папку", "confirmDeleteFolder": "Ты точно хочешь удалить эту папку? Её содержимое будет тоже удалено.", "confirmUnwrapFolder": "Ты точно хочешь вывернуть эту папку? Всё её содержимое будет расположено в текущей папке.", - "exportBehavior": "Экспортировать поведение" + "exportBehavior": "Экспортировать поведение", + "exportTandem": "Экспортировать этот тандем" }, "soundRecorder": { "recorderHeading": "Диктофон", @@ -1263,7 +1264,7 @@ "placeholderTexture": "Текстура-заглушка", "behaviorTemplate": "Поведение для шаблонов", "behaviorRoom": "Поведение для комнат", - "behaviorImport": "Импортировать из файла", + "importFromFile": "Импортировать из файла", "behaviorMissingCatmods": "В твоём проекте нет следующих котомодов: $1. Сначала включи их.", "formatError": "Файл неправильного формата или, быть может, был сделан для несовместимой версии ct.js.", "assetGallery": "Встроенная галерея ассетов" @@ -1301,4 +1302,4 @@ "language": "Язык:", "convertToJavaScript": "Конвертировать в JavaScript" } -} \ No newline at end of file +} diff --git a/app/data/i18n/Turkish.json b/app/data/i18n/Turkish.json index d6eb76894..5f0922467 100644 --- a/app/data/i18n/Turkish.json +++ b/app/data/i18n/Turkish.json @@ -1212,7 +1212,7 @@ "assetGallery": "Dahili varlık galerisi", "behaviorTemplate": "Şablonlar için davranış", "behaviorRoom": "Odalar için davranış", - "behaviorImport": "Dosyadan içeri aktar", + "importFromFile": "Dosyadan içeri aktar", "behaviorMissingCatmods": "Projende $1 catmodları bulunmuyor. Önce onları etkinleştirmelisin.", "formatError": "Açılmış dosyada ya kusurlu bir format var ya da, belki, ct.js'in uyumsuz bir sürümü için tasarlandı." }, diff --git a/src/node_requires/resources/emitterTandems/index.ts b/src/node_requires/resources/emitterTandems/index.ts index 0fec888d3..5b7d69dda 100644 --- a/src/node_requires/resources/emitterTandems/index.ts +++ b/src/node_requires/resources/emitterTandems/index.ts @@ -1,6 +1,7 @@ import generateGUID from '../../generateGUID'; import {IAssetContextItem, addAsset} from '..'; import {promptName} from '../promptName'; +import {getByPath} from '../../i18n'; const getThumbnail = function getThumbnail(): string { return 'sparkles'; @@ -8,27 +9,59 @@ const getThumbnail = function getThumbnail(): string { import * as defaultEmitter from './defaultEmitter'; -const createNewTandem = async (): Promise => { - const name = await promptName('tandem', 'New Emitter Tandem'); - if (!name) { - // eslint-disable-next-line no-throw-literal - throw 'cancelled'; - } +const YAML = require('js-yaml'); +import {writeFile, readFile} from 'fs-extra'; + +const createNewTandem = async (opts: {src?: string}): Promise => { + if (!opts || !('src' in opts)) { + const name = await promptName('tandem', 'New Emitter Tandem'); + if (!name) { + // eslint-disable-next-line no-throw-literal + throw 'cancelled'; + } + + const emitter = defaultEmitter.get(); + const id = generateGUID(); - const emitter = defaultEmitter.get(); - const generateGUID = require('./../../generateGUID'); + const tandem: ITandem = { + name, + uid: id, + emitters: [emitter], + lastmod: Number(new Date()), + type: 'tandem' + }; + + return tandem; + } + // Importing from file + const source = YAML.load(await readFile(opts.src)) as Partial; + const keys: (keyof ITandem)[] = [ + 'name', + 'type', + 'emitters' + ]; + // Check for missing fields — a user might select a wrong file + for (const key of keys) { + if (!(key in source)) { + const message = getByPath('createAsset.formatError') as string; + alertify.error(message); + throw new Error(message); + } + } const id = generateGUID(), slice = id.slice(-6); - const tandem = { - name, + name: 'Unnamed Tandem', uid: id, origname: 'pt' + slice, - emitters: [emitter], + emitters: [], lastmod: Number(new Date()), type: 'tandem' } as ITandem; - + Object.assign(tandem, source); + for (const emitter of tandem.emitters) { + emitter.uid = generateGUID(); + } return tandem; }; @@ -43,6 +76,29 @@ export const assetContextMenuItems: IAssetContextItem[] = [{ newTandem.name += `_${newTandem.uid.slice(0, 4)}`; addAsset(newTandem, folder); } +}, { + icon: 'upload', + action: async (asset: ITandem): Promise => { + const savePath = await window.showSaveDialog({ + defaultName: `${asset.name}.ctTandem`, + filter: '.ctTandem' + }); + if (!savePath) { + return; + } + const copy = { + ...asset + }; + delete copy.uid; + delete copy.lastmod; + for (const emitter of copy.emitters) { + delete emitter.uid; + emitter.texture = -1; + } + await writeFile(savePath, YAML.dump(copy)); + alertify.success(getByPath('common.done')); + }, + vocPath: 'assetViewer.exportTandem' }]; export { diff --git a/src/riotTags/shared/create-asset-menu.tag b/src/riotTags/shared/create-asset-menu.tag index f0c324bdc..ae84dd763 100644 --- a/src/riotTags/shared/create-asset-menu.tag +++ b/src/riotTags/shared/create-asset-menu.tag @@ -34,7 +34,7 @@ create-asset-menu.relative.inlineblock(class="{opts.class}") this.mixin(require('./data/node_requires/riotMixins/voc').default); const priorityTypes = ['texture', 'template', 'room']; - const customizedTypes = ['behavior']; + const customizedTypes = ['tandem', 'behavior']; const {assetTypes, resourceToIconMap, createAsset} = require('./data/node_requires/resources'); @@ -47,29 +47,28 @@ create-asset-menu.relative.inlineblock(class="{opts.class}") this.update(); }; + const genericCreate = (assetType, payload) => async () => { + try { + const asset = await createAsset(assetType, this.opts.folder || null, payload); + if (asset === null) { + return; // Cancelled by a user + } + if (this.opts.onimported) { + this.opts.onimported(asset); + } + } catch (e) { + alertify.error(e); + throw e; + } + }; + const menuItems = []; const assetTypeIterator = assetType => { const [i18nName] = this.vocGlob.assetTypes[assetType]; menuItems.push({ label: i18nName[0].toUpperCase() + i18nName.slice(1), icon: resourceToIconMap[assetType], - click: async () => { - try { - const asset = await createAsset( - assetType, - this.opts.folder || null - ); - if (asset === null) { - return; // Cancelled by a user - } - if (this.opts.onimported) { - this.opts.onimported(asset); - } - } catch (e) { - alertify.error(e); - throw e; - } - } + click: genericCreate(assetType) }); }; priorityTypes.forEach(assetTypeIterator); @@ -81,42 +80,58 @@ create-asset-menu.relative.inlineblock(class="{opts.class}") !customizedTypes.includes(assetType)) .forEach(assetTypeIterator); - // Behaviors need a subtype preset - const bhVoc = this.vocGlob.assetTypes.behavior; + // Tandems can be imported + const tandemVoc = this.vocGlob.assetTypes.tandem; menuItems.push({ - label: bhVoc[1].slice(0, 1).toUpperCase() + bhVoc[1].slice(1), - icon: 'behavior', + label: tandemVoc[0].slice(0, 1).toUpperCase() + tandemVoc[0].slice(1), + icon: 'sparkles', + click: genericCreate('tandem'), submenu: { items: [{ - label: this.voc.behaviorTemplate, - icon: 'behavior', + label: this.vocGlob.create, + icon: 'plus', + click: genericCreate('tandem') + }, { + label: this.voc.importFromFile, + icon: 'download', click: async () => { - const asset = await createAsset('behavior', this.opts.folder || null, { - behaviorType: 'template' + const src = await window.showOpenDialog({ + filter: '.ctTandem' }); - if (asset === null) { - return; // Cancelled by a user + if (!src) { + return; } + const asset = await createAsset('tandem', this.opts.folder || null, { + src + }); if (this.opts.onimported) { this.opts.onimported(asset); } } + }] + } + }); + + // Behaviors need a subtype preset and can be imported + const bhVoc = this.vocGlob.assetTypes.behavior; + menuItems.push({ + label: bhVoc[0].slice(0, 1).toUpperCase() + bhVoc[0].slice(1), + icon: 'behavior', + submenu: { + items: [{ + label: this.voc.behaviorTemplate, + icon: 'template', + click: genericCreate('behavior', { + behaviorType: 'template' + }) }, { label: this.voc.behaviorRoom, - icon: 'behavior', - click: async () => { - const asset = await createAsset('behavior', this.opts.folder || null, { - behaviorType: 'room' - }); - if (asset === null) { - return; // Cancelled by a user - } - if (this.opts.onimported) { - this.opts.onimported(asset); - } - } + icon: 'room', + click: genericCreate('behavior', { + behaviorType: 'room' + }) }, { - label: this.voc.behaviorImport, + label: this.voc.importFromFile, icon: 'download', click: async () => { const src = await window.showOpenDialog({ From d18eb6545d06c6940eecd560b8dd84ffb9f5b0c5 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 16:40:22 +1200 Subject: [PATCH 22/31] :zap: Emitter editors will now show a (?) image instead of a blank broken image if no texture was set --- .../editors/emitter-tandem-editor/emitter-editor.tag | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/riotTags/editors/emitter-tandem-editor/emitter-editor.tag b/src/riotTags/editors/emitter-tandem-editor/emitter-editor.tag index 0927eb00e..7bcfd2f2a 100644 --- a/src/riotTags/editors/emitter-tandem-editor/emitter-editor.tag +++ b/src/riotTags/editors/emitter-tandem-editor/emitter-editor.tag @@ -495,7 +495,12 @@ emitter-editor.aPanel.pad.nb this.updateShortcuts(); const {getThumbnail, getById} = require('./data/node_requires/resources'); - this.getPreview = () => getThumbnail(getById('texture', this.opts.emitter.texture)); + this.getPreview = () => { + if (this.opts.emitter.texture === -1) { + return '/data/img/unknown.png'; + } + return getThumbnail(getById('texture', this.opts.emitter.texture)); + }; this.wireAndReset = path => e => { this.wire(path)(e); From 0725637bb06d71faa963175a20227c619e583298 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 16:41:50 +1200 Subject: [PATCH 23/31] :zap: Do update checks at max once in an hour if it was successfully requested before --- src/riotTags/project-selector.tag | 61 +++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/src/riotTags/project-selector.tag b/src/riotTags/project-selector.tag index 9654e15b8..c9e9bf6ad 100644 --- a/src/riotTags/project-selector.tag +++ b/src/riotTags/project-selector.tag @@ -414,28 +414,49 @@ project-selector }; // Checking for updates - setTimeout(() => { - const {isWin, isLinux} = require('./data/node_requires/platformUtils.js'); - let channel = 'osx64'; - if (isWin) { - channel = 'win64'; - } else if (isLinux) { - channel = 'linux64'; + // Cache update status for an hour to not DDoS itch.io while developing. + let needsUpdateCheck = false, + lastUpdateCheck; + if (localStorage.lastUpdateCheck) { + lastUpdateCheck = new Date(localStorage.lastUpdateCheck); + // Check once an hour + if ((new Date()) - lastUpdateCheck > 1000 * 60 * 60) { + needsUpdateCheck = true; } - fetch(`https://itch.io/api/1/x/wharf/latest?target=comigo/ct&channel_name=${channel}`) - .then(response => response.json()) - .then(json => { - if (!json.errors) { - if (this.ctjsVersion !== json.latest) { - this.newVersion = this.voc.latestVersion.replace('$1', json.latest); - this.update(); - } - } else { - console.error('Update check failed:'); - console.error(json.errors); + } else { + needsUpdateCheck = true; + } + if (needsUpdateCheck) { + setTimeout(() => { + const {isWin, isLinux} = require('./data/node_requires/platformUtils.js'); + let channel = 'osx64'; + if (isWin) { + channel = 'win64'; + } else if (isLinux) { + channel = 'linux64'; } - }); - }, 0); + fetch(`https://itch.io/api/1/x/wharf/latest?target=comigo/ct&channel_name=${channel}`) + .then(response => response.json()) + .then(json => { + if (!json.errors) { + if (this.ctjsVersion !== json.latest) { + this.newVersion = this.voc.latestVersion.replace('$1', json.latest); + this.update(); + } + localStorage.lastUpdateCheck = new Date(); + localStorage.lastUpdateCheckVersion = json.latest; + } else { + console.error('Update check failed:'); + console.error(json.errors); + } + }); + }, 0); + } else { + const newVersion = localStorage.lastUpdateCheckVersion; + if (this.ctjsVersion !== newVersion) { + this.newVersion = this.voc.latestVersion.replace('$1', newVersion); + } + } this.openExternal = link => e => { nw.Shell.openExternal(link); From bb080d102de669dceba0d34d138c9be479d0507f Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 16:50:54 +1200 Subject: [PATCH 24/31] :bug: Sound assets should prompt for a name when created --- src/node_requires/resources/sounds/index.ts | 22 ++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/node_requires/resources/sounds/index.ts b/src/node_requires/resources/sounds/index.ts index ada6f1c1a..2e839eb36 100644 --- a/src/node_requires/resources/sounds/index.ts +++ b/src/node_requires/resources/sounds/index.ts @@ -1,19 +1,27 @@ -import {SoundPreviewer} from '../preview/sound'; +import path from 'path'; +import fs from 'fs-extra'; -const path = require('path'), - fs = require('fs-extra'); +import {SoundPreviewer} from '../preview/sound'; +import {promptName} from '../promptName'; import {sound} from 'node_modules/@pixi/sound'; export const getThumbnail = SoundPreviewer.getClassic; export const areThumbnailsIcons = false; -export const createAsset = function (): ISound { +export const createAsset = async (name?: string): Promise => { + if (!name) { + const newName = await promptName('sound', 'New Sound'); + if (!newName) { + // eslint-disable-next-line no-throw-literal + throw 'cancelled'; + } + name = newName; + } const generateGUID = require('./../../generateGUID'); - var id = generateGUID(), - slice = id.slice(-6); + var id = generateGUID(); const newSound: ISound = { - name: ('Sound_' + slice), + name, uid: id, type: 'sound' as const, lastmod: Number(new Date()), From a377dd403b37a444b4c64e55730d418582f3723f Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 17:09:15 +1200 Subject: [PATCH 25/31] :bug: Fix fs catmod failing when run in node.js context and trying to work with relative paths --- app/data/ct.libs/fs/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/data/ct.libs/fs/index.js b/app/data/ct.libs/fs/index.js index 9a375fa94..9a694a08b 100644 --- a/app/data/ct.libs/fs/index.js +++ b/app/data/ct.libs/fs/index.js @@ -247,18 +247,19 @@ try { const getPath = dest => { dest = normalize(dest); - const absoluteDest = path.isAbsolute(dest) ? dest : path.join(fs.gameFolder, dest); + const absoluteDest = normalize(path.isAbsolute(dest) ? + dest : + path.join(fs.gameFolder, dest)); if (fs.forceLocal) { if (absoluteDest.indexOf(fs.gameFolder) !== 0) { throw new Error('[fs] Operations outside the save directory are not permitted by default due to safety concerns. If you do need to work outside the save directory, change `fs.forceLocal` to `false`. ' + - `The save directory: "${fs.gameFolder}", the target directory: "${dest}", which resolves into "${absoluteDest}".`); + `The save directory: "${fs.gameFolder}", the target item: "${dest}", which resolves into "${absoluteDest}".`); } } return absoluteDest; }; const ensureParents = async dest => { const parents = path.dirname(getPath(dest)); - console.log(parents); await fsNative.mkdir(getPath(parents), { recursive: true }); From d76e6dbb1083d1f6802d803a497925c070e185f2 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 17:27:22 +1200 Subject: [PATCH 26/31] :sparkles: Now you can change the background color in the style editor --- src/node_requires/themes/index.ts | 4 +++ src/riotTags/editors/style-editor.tag | 35 ++++++++++++++++++++++++- src/styl/tags/editors/style-editor.styl | 7 +++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/node_requires/themes/index.ts b/src/node_requires/themes/index.ts index e19dee5ac..e12a9b065 100644 --- a/src/node_requires/themes/index.ts +++ b/src/node_requires/themes/index.ts @@ -5,6 +5,10 @@ import {getLanguageJSON} from '../i18n'; const defaultTheme = 'Day'; const defaultMonacoTheme = defaultTheme; +/** + * The list of the built-in themes coupled with the list of accent colors + * shown in the theme list. + */ const builtInThemes: [string, string[]][] = [ ['Day', ['#ffffff', '#5144db', '#446adb']], ['SpringStream', ['#ffffff', '#00c09e']], diff --git a/src/riotTags/editors/style-editor.tag b/src/riotTags/editors/style-editor.tag index a1f60ff64..5b715b207 100644 --- a/src/riotTags/editors/style-editor.tag +++ b/src/riotTags/editors/style-editor.tag @@ -136,13 +136,22 @@ style-editor.aPanel.aView(class="{opts.class}") svg.feather use(xlink:href="#check") span {vocGlob.apply} - .style-editor-aPreview.tall(ref="canvasSlot") + .style-editor-aPreview.tall(ref="canvasSlot" style="background-color: {previewColor};") + button.inline.forcebackground.style-editor-aChangeBgButton(onclick="{changePreviewBg}") + svg.feather + use(xlink:href="#droplet") + span {vocFull.textureView.bgColor} asset-selector( if="{selectingFont}" assettypes="font" onselected="{applyFont}" oncancelled="{cancelCustomFontSelector}" ) + color-picker( + ref="previewBackgroundColor" if="{changingPreviewBg}" + hidealpha="true" + color="{previewColor}" onapply="{updatePreviewColor}" onchanged="{updatePreviewColor}" oncancel="{cancelPreviewColor}" + ) script. this.namespace = 'styleView'; this.mixin(require('./data/node_requires/riotMixins/voc').default); @@ -287,3 +296,27 @@ style-editor.aPanel.aView(class="{opts.class}") await this.saveAsset(); this.opts.ondone(this.asset); }; + + // Color of the preview window and changing it + const themesAPI = require('./data/node_requires/themes'); + console.log(themesAPI); + const {getSwatch} = require('./data/node_requires/themes'); + this.previewColor = getSwatch('backgroundDeeper'); + this.changePreviewBg = () => { + this.changingPreviewBg = !this.changingPreviewBg; + if (this.changingPreviewBg) { + this.oldPreviewColor = this.previewColor; + } + }; + this.updatePreviewColor = (color, evtype) => { + this.previewColor = color; + if (evtype === 'onapply') { + this.changingPreviewBg = false; + } + this.update(); + }; + this.cancelPreviewColor = () => { + this.changingPreviewBg = false; + this.previewColor = this.oldPreviewColor; + this.update(); + }; diff --git a/src/styl/tags/editors/style-editor.styl b/src/styl/tags/editors/style-editor.styl index bf51582c0..17cef8f9d 100644 --- a/src/styl/tags/editors/style-editor.styl +++ b/src/styl/tags/editors/style-editor.styl @@ -17,5 +17,12 @@ style-editor, [data-is="style-editor"] margin-bottom 0.5rem .flexfix-footer margin-top 0.5rem + & > button + margin-top 0.5rem .style-editor-aPreview overflow hidden + position relative +.style-editor-aChangeBgButton + position absolute + left 0.5rem + bottom 0.5rem From 6a2fd200bbee93d5b38a5ed501c948b816f9880a Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 17:33:08 +1200 Subject: [PATCH 27/31] :zap: Now line height in style editors will scale with the font size when you change the latter --- src/riotTags/editors/style-editor.tag | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/riotTags/editors/style-editor.tag b/src/riotTags/editors/style-editor.tag index 5b715b207..a698ee1e3 100644 --- a/src/riotTags/editors/style-editor.tag +++ b/src/riotTags/editors/style-editor.tag @@ -20,7 +20,7 @@ style-editor.aPanel.aView(class="{opts.class}") label.fifty.npl.nmt b {voc.fontSize} br - input#fontsize.wide(type="number" value="{asset.font.size || '12'}" onchange="{wire('asset.font.size')}" oninput="{wire('asset.font.size')}" step="1") + input#fontsize.wide(type="number" value="{asset.font.size || '12'}" onchange="{wireFontSize}" oninput="{wireFontSize}" step="1") label.fifty.npr.nmt b {voc.fontWeight} br @@ -241,6 +241,14 @@ style-editor.aPanel.aView(class="{opts.class}") alertify.success(this.vocGlob.done); }; + this.wireFontSize = e => { + const oldSize = this.asset.font.size, + oldLineHeight = this.asset.font.lineHeight; + this.wire('asset.font.size')(e); + const k = this.asset.font.size / oldSize; + this.asset.font.lineHeight = Math.round(oldLineHeight * k * 100) / 100; + }; + this.styleSetAlign = align => () => { this.asset.font.halign = align; }; From bf747c995742efe441c280e4a642e7e855a6f663 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 17:41:16 +1200 Subject: [PATCH 28/31] :zap: Behaviors will now have an additional icon in the asset viewer showing the asset type it was created for --- src/riotTags/shared/asset-browser.tag | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/riotTags/shared/asset-browser.tag b/src/riotTags/shared/asset-browser.tag index 109295961..e9ff1bb9c 100644 --- a/src/riotTags/shared/asset-browser.tag +++ b/src/riotTags/shared/asset-browser.tag @@ -170,6 +170,8 @@ asset-browser.flexfix(class="{opts.namespace} {opts.class} {compact: opts.compac span.secondary(if="{asset.type !== 'folder' && (parent.assetTypes.length > 1 || parent.assetTypes[0] === 'all')}") svg.feather use(xlink:href="#{iconMap[asset.type]}") + svg.feather(if="{asset.type === 'behavior'}") + use(xlink:href="#{iconMap[asset.behaviorType]}") span(if="{!parent.opts.compact}") {vocGlob.assetTypes[asset.type][0].slice(0, 1).toUpperCase()}{vocGlob.assetTypes[asset.type][0].slice(1)} .asset-browser-Icons(if="{asset.type !== 'folder'}") svg.feather(each="{icon in parent.getIcons(asset)}" class="feather-{icon}") From f503355bdaf53713534b7e117c5016c93ec47c17 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 19:19:03 +1200 Subject: [PATCH 29/31] :bento: Pull the latest docs --- docs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs b/docs index 0db0da893..977188ba1 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 0db0da89356937f0546277d09f4557b6cca2c39c +Subproject commit 977188ba163fc2793b06243d76620178b292eae3 From 06d0f609f10a0dd19b8972535480e4a5559952dc Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 19:20:09 +1200 Subject: [PATCH 30/31] :bookmark: Bump the version number --- app/package-lock.json | 4 ++-- app/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index f8ec9c72a..b1fb24965 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,12 +1,12 @@ { "name": "ctjs", - "version": "4.0.1", + "version": "4.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ctjs", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "dependencies": { "@capacitor/cli": "^5.5.0", diff --git a/app/package.json b/app/package.json index a84bfb089..719d1d932 100644 --- a/app/package.json +++ b/app/package.json @@ -2,7 +2,7 @@ "main": "index.html", "name": "ctjs", "description": "ct.js — a free 2D game engine", - "version": "4.0.1", + "version": "4.0.2", "homepage": "https://ctjs.rocks/", "author": { "name": "Cosmo Myzrail Gorynych", diff --git a/package-lock.json b/package-lock.json index 5792a6eb3..f499ee8d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ctjsbuildenvironment", - "version": "4.0.1", + "version": "4.0.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ctjsbuildenvironment", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "dependencies": { "@ct.js/gulp-typescript": "^6.0.0", diff --git a/package.json b/package.json index f3a8b6a3f..d66bf5c8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ctjsbuildenvironment", - "version": "4.0.1", + "version": "4.0.2", "description": "", "directories": { "doc": "docs" From fbf5b2c45803f27e70666d131be4d46a01404d20 Mon Sep 17 00:00:00 2001 From: Cosmo Myzrail Gorynych Date: Sun, 25 Feb 2024 19:24:06 +1200 Subject: [PATCH 31/31] :pencil: Update the changelog --- app/Changelog.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/app/Changelog.md b/app/Changelog.md index b8d9023df..35afde62c 100644 --- a/app/Changelog.md +++ b/app/Changelog.md @@ -1,3 +1,47 @@ +## v4.0.2 +*Sun Feb 25 2024* + +### ✨ New Features + +* Add a menu item to duplicate emitter tandems. Closes #498 +* Export and import emitter tandems from files +* Now you can change the background color in the style editor + +### ⚡️ General Improvements + +* :globe_with_meridians: Update Chinese Simplified translations (#500 by @emaoshushu) +* Behaviors will now have an additional icon in the asset viewer showing the asset type it was created for +* Do update checks at max once in an hour if it was successfully requested before +* Emitter editors will now show a (?) image instead of a blank broken image if no texture was set +* (Internal) Move properties assignment for AnimatedSprite from the Copy mixin into the pixi.js-based class. +* Now line height in style editors will scale with the font size when you change the latter +* (Internal) Refactor copy creation off base classes, move CopyAnimatedSprite, CopyText, CopyContainer prototypes into separate pixi.js-based classes. +* Remember whether the grid was disabled in a specific room +* Transparent background in style editor. Make the preview occupy the available space in the editor. + +### 🐛 Bug Fixes + +* Fix broken QWERTY and Shift+S hotkeys in room editors +* Fix buttons skipping their pointer events after being disabled and enabled back +* Fix discardio not removing old keys from asset object before assigning new ones, which led, for example, to not being able to disable stroke/fill/shadow settings of style assets +* Fix double caching of tile layers that breaks `rooms.merge` call. Closes #501 +* Fix `fs` catmod failing when run in node.js context and trying to work with relative paths +* Fix hotkeys being ignored if non-english keyboard layout was in use +* Fix nine slice panes' tint being reset on click and not saved in the room-editor +* Fix not being able to deselect items after sorting *and* moving them +* Fix not being able to set tint with `this.tint` or the room editor to buttons. +* Fix scripts sometimes having blank screen when switching back and forth tabs +* Remove deleted behaviors from opened template editors, and deleted behaviors and templates links from rooms' properties panel +* Sound assets should prompt for a name when created + +### 📝 Docs + +* :bug: Fix movement by a grid (#132 by @0xFFAAF) + +### 🌐 Website + +* :zap: Update wording on essentials on the homepage + ## v4.0.1 *Sun Feb 18 2024*