mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00

* This should make it easier to identify and find actions/conditions. Categories give more clarity and icons help to identify what's available without reading everything.
590 lines
19 KiB
JavaScript
590 lines
19 KiB
JavaScript
// @flow
|
||
/**
|
||
* This is a declaration of an extension for GDevelop 5.
|
||
*
|
||
* ℹ️ Changes in this file are watched and automatically imported if the editor
|
||
* is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
|
||
*
|
||
* The file must be named "JsExtension.js", otherwise GDevelop won't load it.
|
||
* ⚠️ If you make a change and the extension is not loaded, open the developer console
|
||
* and search for any errors.
|
||
*
|
||
* More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
|
||
*/
|
||
|
||
/*::
|
||
// Import types to allow Flow to do static type checking on this file.
|
||
// Extensions declaration are typed using Flow (like the editor), but the files
|
||
// for the game engine are checked with TypeScript annotations.
|
||
import { type ObjectsRenderingService, type ObjectsEditorService } from '../JsExtensionTypes.flow.js'
|
||
*/
|
||
|
||
module.exports = {
|
||
createExtension: function (
|
||
_ /*: (string) => string */,
|
||
gd /*: libGDevelop */
|
||
) {
|
||
const extension = new gd.PlatformExtension();
|
||
extension.setExtensionInformation(
|
||
'MyDummyExtension',
|
||
_('My Dummy Extension'),
|
||
_('An example of a declaration of an extension'),
|
||
'Florian Rival',
|
||
'MIT'
|
||
);
|
||
extension
|
||
.addInstructionOrExpressionGroupMetadata(_('My Dummy Extension'))
|
||
.setIcon('CppPlatform/Extensions/topdownmovementicon.png');
|
||
|
||
// Register Properties
|
||
extension
|
||
.registerProperty('DummyPropertyString')
|
||
.setLabel(_('Dummy Property Name'))
|
||
.setDescription('Type in anything :)')
|
||
.setType('string');
|
||
|
||
extension
|
||
.registerProperty('DummyPropertyNumber')
|
||
.setLabel(_('Dummy Numeric Property Name'))
|
||
.setDescription('Only numbers here ;)')
|
||
.setType('number');
|
||
|
||
extension
|
||
.registerProperty('DummyPropertyBoolean')
|
||
.setDescription(_('A boolean property'))
|
||
.setType('boolean');
|
||
|
||
// Register Cordova/NPM dependencies
|
||
extension
|
||
.addDependency()
|
||
.setName('Thirteen Checker')
|
||
.setDependencyType('npm')
|
||
.setExportName('is-thirteen')
|
||
.setVersion('2.0.0');
|
||
|
||
// Declare effects:
|
||
const dummyEffect = extension
|
||
.addEffect('DummyEffect')
|
||
.setFullName(_('Dummy effect example'))
|
||
.setDescription(
|
||
_(
|
||
'This is an example of an effect ("shader") with an [external link to the wiki](http://wiki.compilgames.net/doku.php/gdevelop5/) and **bold letters**.'
|
||
)
|
||
)
|
||
.addIncludeFile('Extensions/ExampleJsExtension/dummyeffect.js');
|
||
const dummyEffectProperties = dummyEffect.getProperties();
|
||
dummyEffectProperties
|
||
.getOrCreate('opacity')
|
||
.setValue('1')
|
||
.setLabel(_('Opacity of the effect (between 0 and 1)'))
|
||
.setType('number')
|
||
.setDescription(_('This is an optional description.'));
|
||
dummyEffectProperties
|
||
.getOrCreate('someImage')
|
||
.setValue('')
|
||
.setLabel(
|
||
_("Image resource (won't be used, just for demonstration purpose)")
|
||
)
|
||
.setType('resource')
|
||
.addExtraInfo('image');
|
||
dummyEffectProperties
|
||
.getOrCreate('someColor')
|
||
.setValue('#0022FF')
|
||
.setLabel(_("Color (won't be used, just for demonstration purpose)"))
|
||
.setType('color')
|
||
.setDescription(_('Another optional description.'));
|
||
dummyEffectProperties
|
||
.getOrCreate('someBoolean')
|
||
.setValue('true')
|
||
.setLabel(_('Some setting to enable or not for the effect'))
|
||
.setType('boolean')
|
||
.setDescription(_('And some *optional* description.'));
|
||
|
||
// Declare conditions, actions or expressions:
|
||
extension
|
||
.addCondition(
|
||
'MyNewCondition',
|
||
_('Dummy condition example'),
|
||
_(
|
||
'This is an example of a condition displayed in the events sheet. Will return true if the number is less than 10 and the length of the text is less than 5.'
|
||
),
|
||
_('Call the example condition with _PARAM0_ and _PARAM1_'),
|
||
'',
|
||
'res/conditions/camera24.png',
|
||
'res/conditions/camera.png'
|
||
)
|
||
.addParameter('expression', _('Number 1'), '', false)
|
||
.addParameter('string', _('Text 1'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setIncludeFile(
|
||
'Extensions/ExampleJsExtension/examplejsextensiontools.js'
|
||
)
|
||
.setFunctionName('gdjs.evtTools.exampleJsExtension.myConditionFunction');
|
||
|
||
extension
|
||
.addExpression(
|
||
'DummyExpression',
|
||
_('Dummy expression example'),
|
||
_('This is an example of an expression'),
|
||
'',
|
||
'res/actions/camera.png'
|
||
)
|
||
.addParameter('expression', _('Maximum'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('gdjs.random');
|
||
|
||
extension
|
||
.addStrExpression(
|
||
'DummyStrExpression',
|
||
_('Dummy string expression example'),
|
||
_('This is an example of an expression returning a string'),
|
||
'',
|
||
'res/actions/camera.png'
|
||
)
|
||
.getCodeExtraInformation()
|
||
.setIncludeFile(
|
||
'Extensions/ExampleJsExtension/examplejsextensiontools.js'
|
||
)
|
||
.setFunctionName('gdjs.evtTools.exampleJsExtension.getString');
|
||
|
||
// Declare a behavior.
|
||
// Create a new gd.BehaviorJsImplementation object and implement the methods
|
||
// that are called to get and set the properties of the behavior.
|
||
// Everything that is stored inside the behavior is in "behaviorContent" and is automatically
|
||
// saved/loaded to JSON.
|
||
var dummyBehavior = new gd.BehaviorJsImplementation();
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
dummyBehavior.updateProperty = function (
|
||
behaviorContent,
|
||
propertyName,
|
||
newValue
|
||
) {
|
||
if (propertyName === 'My first property') {
|
||
behaviorContent.setStringAttribute('property1', newValue);
|
||
return true;
|
||
}
|
||
if (propertyName === 'My other property') {
|
||
behaviorContent.setBoolAttribute('property2', newValue === '1');
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
dummyBehavior.getProperties = function (behaviorContent) {
|
||
var behaviorProperties = new gd.MapStringPropertyDescriptor();
|
||
|
||
behaviorProperties
|
||
.getOrCreate('My first property')
|
||
.setValue(behaviorContent.getStringAttribute('property1'));
|
||
behaviorProperties
|
||
.getOrCreate('My other property')
|
||
.setValue(
|
||
behaviorContent.getBoolAttribute('property2') ? 'true' : 'false'
|
||
)
|
||
.setType('Boolean')
|
||
.setGroup(_('Look and Feel'));
|
||
|
||
return behaviorProperties;
|
||
};
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
dummyBehavior.initializeContent = function (behaviorContent) {
|
||
behaviorContent.setStringAttribute('property1', 'Initial value 1');
|
||
behaviorContent.setBoolAttribute('property2', true);
|
||
};
|
||
extension
|
||
.addBehavior(
|
||
'DummyBehavior',
|
||
_('Dummy behavior for testing'),
|
||
'DummyBehavior',
|
||
_('Do nothing.'),
|
||
'',
|
||
'CppPlatform/Extensions/topdownmovementicon.png',
|
||
'DummyBehavior',
|
||
dummyBehavior,
|
||
new gd.BehaviorsSharedData()
|
||
)
|
||
.setIncludeFile('Extensions/ExampleJsExtension/dummyruntimebehavior.js')
|
||
// You can optionally include more than one file when the behavior is used:
|
||
.addIncludeFile(
|
||
'Extensions/ExampleJsExtension/examplejsextensiontools.js'
|
||
);
|
||
|
||
// Declare another behavior, with shared data between the behaviors
|
||
// In addition to the usual behavior:
|
||
// Create a new gd.BehaviorSharedDataJsImplementation object and implement the methods
|
||
// that are called to get and set the properties of the shared data.
|
||
var dummyBehaviorWithSharedData = new gd.BehaviorJsImplementation();
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
dummyBehaviorWithSharedData.updateProperty = function (
|
||
behaviorContent,
|
||
propertyName,
|
||
newValue
|
||
) {
|
||
if (propertyName === 'My behavior property') {
|
||
behaviorContent.setStringAttribute('property1', newValue);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
dummyBehaviorWithSharedData.getProperties = function (behaviorContent) {
|
||
var behaviorProperties = new gd.MapStringPropertyDescriptor();
|
||
|
||
behaviorProperties
|
||
.getOrCreate('My behavior property')
|
||
.setValue(behaviorContent.getStringAttribute('property1'));
|
||
|
||
return behaviorProperties;
|
||
};
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
dummyBehaviorWithSharedData.initializeContent = function (behaviorContent) {
|
||
behaviorContent.setStringAttribute('property1', 'Initial value 1');
|
||
};
|
||
|
||
var sharedData = new gd.BehaviorSharedDataJsImplementation();
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
sharedData.updateProperty = function (
|
||
sharedContent,
|
||
propertyName,
|
||
newValue
|
||
) {
|
||
if (propertyName === 'My shared property') {
|
||
sharedContent.setStringAttribute('sharedProperty1', newValue);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
sharedData.getProperties = function (sharedContent) {
|
||
var sharedProperties = new gd.MapStringPropertyDescriptor();
|
||
|
||
sharedProperties
|
||
.getOrCreate('My shared property')
|
||
.setValue(sharedContent.getStringAttribute('sharedProperty1'));
|
||
|
||
return sharedProperties;
|
||
};
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
sharedData.initializeContent = function (behaviorContent) {
|
||
behaviorContent.setStringAttribute(
|
||
'sharedProperty1',
|
||
'Initial shared value 1'
|
||
);
|
||
};
|
||
|
||
extension
|
||
.addBehavior(
|
||
'DummyBehaviorWithSharedData',
|
||
_('Dummy behavior with shared data for testing'),
|
||
'DummyBehaviorWithSharedData',
|
||
_('Do nothing but use shared data.'),
|
||
'',
|
||
'CppPlatform/Extensions/topdownmovementicon.png',
|
||
'DummyBehaviorWithSharedData',
|
||
dummyBehaviorWithSharedData,
|
||
sharedData
|
||
)
|
||
.setIncludeFile(
|
||
'Extensions/ExampleJsExtension/dummywithshareddataruntimebehavior.js'
|
||
)
|
||
// You can optionally include more than one file when the behavior is used:
|
||
.addIncludeFile(
|
||
'Extensions/ExampleJsExtension/examplejsextensiontools.js'
|
||
);
|
||
|
||
// Declare an object.
|
||
// Create a new gd.ObjectJsImplementation object and implement the methods
|
||
// that are called to get and set the properties of the object, as well
|
||
// as the properties of the initial instances of this object
|
||
// Everything that is stored inside the object is in "content" and is automatically
|
||
// saved/loaded to JSON.
|
||
var dummyObject = new gd.ObjectJsImplementation();
|
||
// $FlowExpectedError - ignore Flow warning as we're creating an object
|
||
dummyObject.updateProperty = function (
|
||
objectContent,
|
||
propertyName,
|
||
newValue
|
||
) {
|
||
if (propertyName === 'My first property') {
|
||
objectContent.property1 = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'My other property') {
|
||
objectContent.property2 = newValue === '1';
|
||
return true;
|
||
}
|
||
if (propertyName === 'My third property') {
|
||
objectContent.property3 = newValue;
|
||
return true;
|
||
}
|
||
if (propertyName === 'myImage') {
|
||
objectContent.myImage = newValue;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
// $FlowExpectedError - ignore Flow warning as we're creating an object
|
||
dummyObject.getProperties = function (objectContent) {
|
||
var objectProperties = new gd.MapStringPropertyDescriptor();
|
||
|
||
objectProperties
|
||
.getOrCreate('My first property')
|
||
.setValue(objectContent.property1);
|
||
objectProperties
|
||
.getOrCreate('My other property')
|
||
.setValue(objectContent.property2 ? 'true' : 'false')
|
||
.setType('boolean');
|
||
objectProperties
|
||
.getOrCreate('My third property')
|
||
.setValue(objectContent.property3.toString())
|
||
.setType('number');
|
||
objectProperties
|
||
.getOrCreate('myImage')
|
||
.setValue(objectContent.myImage)
|
||
.setType('resource')
|
||
.addExtraInfo('image')
|
||
.setLabel(
|
||
_("Image resource (won't be shown, just for demonstration purpose)")
|
||
);
|
||
|
||
return objectProperties;
|
||
};
|
||
dummyObject.setRawJSONContent(
|
||
JSON.stringify({
|
||
property1: 'Hello world',
|
||
property2: true,
|
||
property3: 123,
|
||
myImage: '',
|
||
})
|
||
);
|
||
|
||
// $FlowExpectedError - ignore Flow warning as we're creating an object
|
||
dummyObject.updateInitialInstanceProperty = function (
|
||
objectContent,
|
||
instance,
|
||
propertyName,
|
||
newValue,
|
||
project,
|
||
layout
|
||
) {
|
||
if (propertyName === 'My instance property') {
|
||
instance.setRawStringProperty('instanceprop1', newValue);
|
||
return true;
|
||
}
|
||
if (propertyName === 'My other instance property') {
|
||
instance.setRawDoubleProperty('instanceprop2', parseFloat(newValue));
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
// $FlowExpectedError - ignore Flow warning as we're creating an object
|
||
dummyObject.getInitialInstanceProperties = function (
|
||
content,
|
||
instance,
|
||
project,
|
||
layout
|
||
) {
|
||
var instanceProperties = new gd.MapStringPropertyDescriptor();
|
||
|
||
instanceProperties
|
||
.getOrCreate('My instance property')
|
||
.setValue(instance.getRawStringProperty('instanceprop1'));
|
||
instanceProperties
|
||
.getOrCreate('My other instance property')
|
||
.setValue(instance.getRawDoubleProperty('instanceprop2').toString())
|
||
.setType('number');
|
||
|
||
return instanceProperties;
|
||
};
|
||
|
||
const object = extension
|
||
.addObject(
|
||
'DummyObject',
|
||
_('Dummy object for testing'),
|
||
_('This dummy object does nothing'),
|
||
'CppPlatform/Extensions/topdownmovementicon.png',
|
||
dummyObject
|
||
)
|
||
.setIncludeFile('Extensions/ExampleJsExtension/dummyruntimeobject.js')
|
||
.addIncludeFile(
|
||
'Extensions/ExampleJsExtension/dummyruntimeobject-pixi-renderer.js'
|
||
)
|
||
.setCategoryFullName(_('Testing'));
|
||
|
||
object
|
||
.addAction(
|
||
'MyMethod',
|
||
_('Display a dummy text in Developer console'),
|
||
_(
|
||
'Display a dummy text in Developer console. Open it with CTRL-SHIFT-J (Cmd-Alt-J on macOS).'
|
||
),
|
||
_('Display a dummy text for _PARAM0_, with params: _PARAM1_, _PARAM2_'),
|
||
'',
|
||
'res/conditions/camera24.png',
|
||
'res/conditions/camera.png'
|
||
)
|
||
.addParameter('object', _('Object'), 'DummyObject', false) // This parameter is mandatory for any object action/condition
|
||
.addParameter('expression', _('Number 1'), '', false)
|
||
.addParameter('string', _('Text 1'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('myMethod');
|
||
|
||
return extension;
|
||
},
|
||
/**
|
||
* You can optionally add sanity tests that will check the basic working
|
||
* of your extension behaviors/objects by instanciating behaviors/objects
|
||
* and setting the property to a given value.
|
||
*
|
||
* If you don't have any tests, you can simply return an empty array.
|
||
*
|
||
* But it is recommended to create tests for the behaviors/objects properties you created
|
||
* to avoid mistakes.
|
||
*/
|
||
runExtensionSanityTests: function (
|
||
gd /*: libGDevelop */,
|
||
extension /*: gdPlatformExtension*/
|
||
) {
|
||
const dummyBehavior = extension
|
||
.getBehaviorMetadata('MyDummyExtension::DummyBehavior')
|
||
.get();
|
||
const sharedData = extension
|
||
.getBehaviorMetadata('MyDummyExtension::DummyBehaviorWithSharedData')
|
||
.getSharedDataInstance();
|
||
return [
|
||
gd.ProjectHelper.sanityCheckBehaviorProperty(
|
||
dummyBehavior,
|
||
'My first property',
|
||
'Testing value'
|
||
),
|
||
gd.ProjectHelper.sanityCheckBehaviorsSharedDataProperty(
|
||
sharedData,
|
||
'My shared property',
|
||
'Testing value'
|
||
),
|
||
];
|
||
},
|
||
/**
|
||
* Register editors for objects.
|
||
*
|
||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
|
||
*/
|
||
registerEditorConfigurations: function (
|
||
objectsEditorService /*: ObjectsEditorService */
|
||
) {
|
||
objectsEditorService.registerEditorConfiguration(
|
||
'MyDummyExtension::DummyObject',
|
||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
|
||
helpPagePath: '/extensions/extend-gdevelop',
|
||
})
|
||
);
|
||
},
|
||
/**
|
||
* Register renderers for instance of objects on the scene editor.
|
||
*
|
||
* ℹ️ Run `node import-GDJS-Runtime.js` (in newIDE/app/scripts) if you make any change.
|
||
*/
|
||
registerInstanceRenderers: function (
|
||
objectsRenderingService /*: ObjectsRenderingService */
|
||
) {
|
||
const RenderedInstance = objectsRenderingService.RenderedInstance;
|
||
const PIXI = objectsRenderingService.PIXI;
|
||
|
||
/**
|
||
* Renderer for instances of DummyObject inside the IDE.
|
||
*
|
||
* @extends RenderedInstance
|
||
* @class RenderedDummyObjectInstance
|
||
* @constructor
|
||
*/
|
||
function RenderedDummyObjectInstance(
|
||
project,
|
||
layout,
|
||
instance,
|
||
associatedObject,
|
||
pixiContainer,
|
||
pixiResourcesLoader
|
||
) {
|
||
RenderedInstance.call(
|
||
this,
|
||
project,
|
||
layout,
|
||
instance,
|
||
associatedObject,
|
||
pixiContainer,
|
||
pixiResourcesLoader
|
||
);
|
||
|
||
//Setup the PIXI object:
|
||
this._pixiObject = new PIXI.Text('This is a dummy object', {
|
||
align: 'left',
|
||
});
|
||
this._pixiObject.anchor.x = 0.5;
|
||
this._pixiObject.anchor.y = 0.5;
|
||
this._pixiContainer.addChild(this._pixiObject);
|
||
this.update();
|
||
}
|
||
RenderedDummyObjectInstance.prototype = Object.create(
|
||
RenderedInstance.prototype
|
||
);
|
||
|
||
/**
|
||
* Return the path to the thumbnail of the specified object.
|
||
*/
|
||
RenderedDummyObjectInstance.getThumbnail = function (
|
||
project,
|
||
resourcesLoader,
|
||
object
|
||
) {
|
||
return 'CppPlatform/Extensions/texticon24.png';
|
||
};
|
||
|
||
/**
|
||
* This is called to update the PIXI object on the scene editor
|
||
*/
|
||
RenderedDummyObjectInstance.prototype.update = function () {
|
||
// Read a property from the object
|
||
const property1Value = this._associatedObject
|
||
.getProperties()
|
||
.get('My first property')
|
||
.getValue();
|
||
this._pixiObject.text = property1Value;
|
||
|
||
// Read position and angle from the instance
|
||
this._pixiObject.position.x =
|
||
this._instance.getX() + this._pixiObject.width / 2;
|
||
this._pixiObject.position.y =
|
||
this._instance.getY() + this._pixiObject.height / 2;
|
||
this._pixiObject.rotation = RenderedInstance.toRad(
|
||
this._instance.getAngle()
|
||
);
|
||
// Custom size can be read in instance.getCustomWidth() and
|
||
// instance.getCustomHeight()
|
||
};
|
||
|
||
/**
|
||
* Return the width of the instance, when it's not resized.
|
||
*/
|
||
RenderedDummyObjectInstance.prototype.getDefaultWidth = function () {
|
||
return this._pixiObject.width;
|
||
};
|
||
|
||
/**
|
||
* Return the height of the instance, when it's not resized.
|
||
*/
|
||
RenderedDummyObjectInstance.prototype.getDefaultHeight = function () {
|
||
return this._pixiObject.height;
|
||
};
|
||
|
||
objectsRenderingService.registerInstanceRenderer(
|
||
'MyDummyExtension::DummyObject',
|
||
RenderedDummyObjectInstance
|
||
);
|
||
},
|
||
};
|