Skip to content

Commit

Permalink
✨ Behaviors: add Behavior added and Behavior removed events
Browse files Browse the repository at this point in the history
  • Loading branch information
CosmoMyzrailGorynych committed May 19, 2024
1 parent 5423e3e commit 7c970d6
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 10 deletions.
4 changes: 4 additions & 0 deletions app/data/i18n/English.json
Original file line number Diff line number Diff line change
Expand Up @@ -1646,6 +1646,8 @@
"OnDraw": "Frame end",
"OnDestroy": "Destruction",
"OnRoomEnd": "Room end",
"OnBehaviorAdded": "Behavior added",
"OnBehaviorRemoved": "Behavior removed",
"OnPointerClick": "Click",
"OnPointerSecondaryClick": "Secondary click",
"OnPointerEnter": "Pointer enter",
Expand Down Expand Up @@ -1694,6 +1696,8 @@
"OnStep": "Happens at the beginning of every frame.",
"OnDraw": "Happens at the end of every frame. Good for animation updates.",
"OnDestroy": "Happens between \"frame start\" and \"frame end\" when this copy gets destroyed.",
"OnBehaviorAdded": "This gets called when a behavior is dynamically added to this copy. This event won't work with static behaviors; for those, use the Creation event instead.",
"OnBehaviorRemoved": "This gets called when you remove this behavior from a copy dynamically. This event won't work with static behaviors; for those, use the Descruction event instead.",
"OnRoomEnd": "Triggers when you switch to another room, or when this room is removed after it was added as a layer.",
"OnActionPress": "Happens when an action's input becomes active — by pressing, clicking, flicking a joystick, etc.",
"OnActionRelease": "Happens when an action's input becomes inactive — by releasing buttons, resting joysticks, etc.",
Expand Down
16 changes: 12 additions & 4 deletions src/ct.release/behaviors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ const behaviorsLib = {
throw new Error(`[behaviors.add] Behavior ${behavior} already exists on ${target instanceof Room ? target.name : target.template}`);
}
const domain = target instanceof Room ? 'rooms' : 'templates';
if (behaviorsLib[domain][behavior] === 'static') {
const bh = behaviorsLib[domain][behavior];
if (bh === 'static') {
throw new Error(`[behaviors.add] Behavior ${behavior} cannot be added to ${target instanceof Room ? target.name : target.template} because this behavior cannot be added dynamically.`);
}
if (!behaviorsLib[domain][behavior]) {
if (!bh) {
throw new Error(`[behaviors.add] Behavior ${behavior} does not exist or cannot be applied to ${domain}.`);
}
target.behaviors.push(behavior);
if (bh.thisOnAdded) {
bh.thisOnAdded.apply(target);
}
},
/**
* Removes a behavior from the given room or template.
Expand All @@ -46,12 +50,16 @@ const behaviorsLib = {
throw new Error(`[behaviors.remove] Behavior ${behavior} already exists on ${target instanceof Room ? target.name : target.template}`);
}
const domain = target instanceof Room ? 'rooms' : 'templates';
if (behaviorsLib[domain][behavior] === 'static') {
const bh = behaviorsLib[domain][behavior];
if (bh === 'static') {
throw new Error(`[behaviors.remove] Behavior ${behavior} cannot be removed from ${target instanceof Room ? target.name : target.template} because this behavior cannot be removed dynamically.`);
}
if (!behaviorsLib[domain][behavior]) {
if (!bh) {
throw new Error(`[behaviors.remove] Behavior ${behavior} does not exist or cannot be applied to ${domain}.`);
}
if (bh.thisOnRemoved) {
bh.thisOnRemoved.apply(target);
}
target.behaviors.splice(target.behaviors.indexOf(behavior), 1);
},
/**
Expand Down
Binary file modified src/examples/MoveBlocks/img/splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions src/node_requires/events/IEvent.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ declare interface IEventCategory {
[key: string]: string;
}

type EventApplicableEntities = 'template' | 'room';
type EventApplicableEntities = 'template' | 'room' | 'behavior';
type EventArgumentTypes =
'integer' | 'float' | 'string' | 'boolean' |
'template' | 'room' | 'sound' | 'tandem' | 'font' | 'style' | 'texture' | 'action';
type EventCodeTargets =
'thisOnStep' | 'thisOnCreate' | 'thisOnDraw' | 'thisOnDestroy' |
'rootRoomOnCreate' | 'rootRoomOnStep' | 'rootRoomOnDraw' | 'rootRoomOnLeave';
'rootRoomOnCreate' | 'rootRoomOnStep' | 'rootRoomOnDraw' | 'rootRoomOnLeave' |
'thisOnAdded' | 'thisOnRemoved';

declare interface IEventArgumentDeclaration {
name: string;
Expand Down
20 changes: 20 additions & 0 deletions src/node_requires/events/coreEventsLifecycle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,26 @@ const coreEvents = {
inlineCodeTemplates: {
thisOnDestroy: '{\n/*%%USER_CODE%%*/\n}'
}
},
core_OnBehaviorAdded: {
name: 'On behavior added',
applicable: ['behavior'],
icon: 'behavior',
category: 'lifecycle',
codeTargets: ['thisOnAdded'],
inlineCodeTemplates: {
thisOnAdded: '{\n/*%%USER_CODE%%*/\n}'
}
},
core_OnBehaviorRemoved: {
name: 'On behavior removed',
applicable: ['behavior'],
icon: 'behavior',
category: 'lifecycle',
codeTargets: ['thisOnRemoved'],
inlineCodeTemplates: {
thisOnRemoved: '{\n/*%%USER_CODE%%*/\n}'
}
}
} as Record<string, IEventDeclaration>;

Expand Down
5 changes: 3 additions & 2 deletions src/node_requires/events/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ const canUseBaseClass = (event: IEventDeclaration, baseClass: TemplateBaseClass)
const bakeCategories = function bakeCategories(
entity: EventApplicableEntities,
callback: (affixedData: IEventDeclaration) => void,
baseClass?: TemplateBaseClass
baseClass?: TemplateBaseClass,
isBehavior?: boolean
): EventMenu {
const menu = {
items: [] as IEventMenuSubmenu[]
Expand All @@ -203,7 +204,7 @@ const bakeCategories = function bakeCategories(
for (const eventKey in events) {
const event = events[eventKey];
// Filter out events for other entities
if (!event.applicable.includes(entity)) {
if (!event.applicable.includes(entity) && !(isBehavior && event.applicable.includes('behavior'))) {
continue;
}
// Filter out events that require a specific base class
Expand Down
4 changes: 3 additions & 1 deletion src/node_requires/exporter/_exporterContracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,9 @@ export type ExportedBehaviorDynamic = {
thisOnStep?: () => void,
thisOnCreate?: () => void,
thisOnDraw?: () => void,
thisOnDestroy?: () => void
thisOnDestroy?: () => void,
thisOnAdded?: () => void,
thisOnRemoved?: () => void
};
export type ExportedBehavior = 'static' | ExportedBehaviorDynamic;

Expand Down
2 changes: 2 additions & 0 deletions src/node_requires/exporter/scriptableProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ const getBaseScripts = function (entity: IScriptable, project: IProject): Script
thisOnCreate: '',
thisOnDraw: '',
thisOnDestroy: '',
thisOnAdded: '',
thisOnRemoved: '',
rootRoomOnCreate: '',
rootRoomOnStep: '',
rootRoomOnDraw: '',
Expand Down
1 change: 1 addition & 0 deletions src/riotTags/editors/behavior-editor.tag
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ behavior-editor.aPanel.aView.flexrow
onchanged="{changeCodeTab}"
currentevent="{currentSheet}"
warnbehaviors="yes"
isbehavior="yes"
).tall
.flexfix-footer
.aSpacer
Expand Down
5 changes: 4 additions & 1 deletion src/riotTags/shared/scriptables/event-list-scriptable.tag
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
@attribute [currentevent] (IScriptableEvent)
Currently selected event.
Defaults to the first event in the `events` attributes.
@attribute [isbehavior] (atomic)
Set it to true for behavior editors.

@attribute [warnbehaviors] (atomic)
If set, will show warning icons for events that make behaviors static.
Expand Down Expand Up @@ -211,7 +213,8 @@ event-list-scriptable.flexfix(class="{opts.class}")
this.eventsMenu = eventsAPI.bakeCategories(
this.opts.entitytype,
this.addEvent,
this.opts.baseclass
this.opts.baseclass,
Boolean(this.opts.isbehavior)
);
};
this.refreshEventsMenu();
Expand Down

0 comments on commit 7c970d6

Please sign in to comment.