mirror of
https://github.com/Heretek-AI/GDevelop.git
synced 2026-07-22 01:55:25 -04:00
582e71e9d3
- Change help link for light object - Set new object default name depending on its type - Make object input sticky at the bottom in ObjectGroupEditor - Force loss of focus on sliders when mouse is up - Add possibility to close editor tabs with mouse middle click - Use user preferred theme to set IDE loading screen background color
429 lines
13 KiB
JavaScript
429 lines
13 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(
|
||
'Lighting',
|
||
_('Lights'),
|
||
|
||
'This provides a light object, and a behavior to mark other objects as being obstacles for the lights. This is a great way to create a special atmosphere to your game, along with effects, make it more realistic or to create gameplays based on lights.',
|
||
'Harsimran Virk',
|
||
'MIT'
|
||
);
|
||
|
||
const lightObstacleBehavior = new gd.BehaviorJsImplementation();
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
lightObstacleBehavior.updateProperty = function (
|
||
behaviorContent,
|
||
propertyName,
|
||
newValue
|
||
) {
|
||
return false;
|
||
};
|
||
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
lightObstacleBehavior.getProperties = function (behaviorContent) {
|
||
const behaviorProperties = new gd.MapStringPropertyDescriptor();
|
||
|
||
return behaviorProperties;
|
||
};
|
||
|
||
// $FlowExpectedError - ignore Flow warning as we're creating a behavior
|
||
lightObstacleBehavior.initializeContent = function (behaviorContent) {};
|
||
extension
|
||
.addBehavior(
|
||
'LightObstacleBehavior',
|
||
_('Light Obstacle Behavior'),
|
||
'LightObstacleBehavior',
|
||
_(
|
||
'Flag objects as being obstacles to light. The light emitted by light objects will be stopped by the object.'
|
||
),
|
||
'',
|
||
'CppPlatform/Extensions/lightObstacleIcon32.png',
|
||
'LightObstacleBehavior',
|
||
lightObstacleBehavior,
|
||
new gd.BehaviorsSharedData()
|
||
)
|
||
.setIncludeFile('Extensions/Lighting/lightobstacleruntimebehavior.js')
|
||
.addIncludeFile('Extensions/Lighting/lightruntimeobject.js')
|
||
.addIncludeFile(
|
||
'Extensions/Lighting/lightruntimeobject-pixi-renderer.js'
|
||
);
|
||
|
||
const lightObject = new gd.ObjectJsImplementation();
|
||
|
||
// $FlowExpectedError - ignore Flow warning as we're creating an object.
|
||
lightObject.updateProperty = function (
|
||
objectContent,
|
||
propertyName,
|
||
newValue
|
||
) {
|
||
if (propertyName === 'radius') {
|
||
objectContent.radius = parseFloat(newValue);
|
||
return true;
|
||
}
|
||
|
||
if (propertyName === 'color') {
|
||
objectContent.color = newValue;
|
||
return true;
|
||
}
|
||
|
||
if (propertyName === 'debugMode') {
|
||
objectContent.debugMode = newValue === '1';
|
||
return true;
|
||
}
|
||
|
||
if (propertyName === 'texture') {
|
||
objectContent.texture = newValue;
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
};
|
||
|
||
// $FlowExpectedError - ignore Flow warning as we're creating an object.
|
||
lightObject.getProperties = function (objectContent) {
|
||
const objectProperties = new gd.MapStringPropertyDescriptor();
|
||
|
||
objectProperties.set(
|
||
'radius',
|
||
new gd.PropertyDescriptor(objectContent.radius.toString())
|
||
.setType('number')
|
||
.setLabel(_('Radius'))
|
||
);
|
||
|
||
objectProperties.set(
|
||
'color',
|
||
new gd.PropertyDescriptor(objectContent.color)
|
||
.setType('color')
|
||
.setLabel(_('Color'))
|
||
);
|
||
|
||
objectProperties.set(
|
||
'debugMode',
|
||
new gd.PropertyDescriptor(objectContent.debugMode ? 'true' : 'false')
|
||
.setType('boolean')
|
||
.setLabel(_('Debug mode'))
|
||
.setDescription(
|
||
_(
|
||
'When activated, display the lines used to render the light - useful to understand how the light is rendered on screen.'
|
||
)
|
||
)
|
||
.setGroup(_('Advanced'))
|
||
);
|
||
|
||
objectProperties
|
||
.getOrCreate('texture')
|
||
.setValue(objectContent.texture)
|
||
.setType('resource')
|
||
.addExtraInfo('image')
|
||
.setLabel(_('Light texture (optional)'))
|
||
.setDescription(
|
||
_(
|
||
"A texture to be used to display the light. If you don't specify a texture, the light is rendered as fading from bright, in its center, to dark."
|
||
)
|
||
);
|
||
|
||
return objectProperties;
|
||
};
|
||
lightObject.setRawJSONContent(
|
||
JSON.stringify({
|
||
radius: 50,
|
||
color: '255;255;255',
|
||
debugMode: false,
|
||
texture: '',
|
||
})
|
||
);
|
||
|
||
// $FlowExpectedError - ignore Flow warning as we're creating an object.
|
||
lightObject.updateInitialInstanceProperty = function (
|
||
objectContent,
|
||
instance,
|
||
propertyName,
|
||
newValue,
|
||
project,
|
||
layout
|
||
) {
|
||
return false;
|
||
};
|
||
|
||
// $FlowExpectedError - ignore Flow warning as we're creating an object.
|
||
lightObject.getInitialInstanceProperties = function (
|
||
content,
|
||
instance,
|
||
project,
|
||
layout
|
||
) {
|
||
const instanceProperties = new gd.MapStringPropertyDescriptor();
|
||
|
||
return instanceProperties;
|
||
};
|
||
|
||
const object = extension
|
||
.addObject(
|
||
'LightObject',
|
||
_('Light'),
|
||
_(
|
||
'Displays a light on the scene, with a customizable radius and color. Add then the Light Obstacle behavior to the objects that must act as obstacle to the lights.'
|
||
),
|
||
'CppPlatform/Extensions/lightIcon32.png',
|
||
lightObject
|
||
)
|
||
.setIncludeFile('Extensions/Lighting/lightruntimeobject.js')
|
||
.addIncludeFile('Extensions/Lighting/lightruntimeobject-pixi-renderer.js')
|
||
.addIncludeFile('Extensions/Lighting/lightobstacleruntimebehavior.js')
|
||
.setCategoryFullName(_('Lights'));
|
||
|
||
object
|
||
.addAction(
|
||
'SetRadius',
|
||
_('Set the radius of light object'),
|
||
_('Set the radius of light object'),
|
||
_('Set the radius of _PARAM0_ to: _PARAM1_'),
|
||
'',
|
||
'CppPlatform/Extensions/lightIcon24.png',
|
||
'CppPlatform/Extensions/lightIcon16.png'
|
||
)
|
||
.addParameter('object', _('Object'), 'LightObject', false)
|
||
.addParameter('expression', _('Radius'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setRadius');
|
||
|
||
object
|
||
.addAction(
|
||
'SetColor',
|
||
_('Set the color of light object'),
|
||
_('Set the color of light object in format "R;G;B" string.'),
|
||
_('Set the color of _PARAM0_ to: _PARAM1_'),
|
||
'',
|
||
'res/actions/color24.png',
|
||
'res/actions/color.png'
|
||
)
|
||
.addParameter('object', _('Object'), 'LightObject', false)
|
||
.addParameter('color', _('Color'), '', false)
|
||
.getCodeExtraInformation()
|
||
.setFunctionName('setColor');
|
||
|
||
return extension;
|
||
},
|
||
|
||
runExtensionSanityTests: function (
|
||
gd /*: libGDevelop */,
|
||
extension /*: gdPlatformExtension*/
|
||
) {
|
||
return [];
|
||
},
|
||
|
||
registerEditorConfigurations: function (
|
||
objectsEditorService /*: ObjectsEditorService */
|
||
) {
|
||
objectsEditorService.registerEditorConfiguration(
|
||
'Lighting::LightObject',
|
||
objectsEditorService.getDefaultObjectJsImplementationPropertiesEditor({
|
||
helpPagePath: '/all-features/lighting/reference',
|
||
})
|
||
);
|
||
},
|
||
/**
|
||
* 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 LightObject inside the IDE.
|
||
*
|
||
* @extends RenderedInstance
|
||
* @class RenderedLightObjectInstance
|
||
* @constructor
|
||
*/
|
||
function RenderedLightObjectInstance(
|
||
project,
|
||
layout,
|
||
instance,
|
||
associatedObject,
|
||
pixiContainer,
|
||
pixiResourcesLoader
|
||
) {
|
||
RenderedInstance.call(
|
||
this,
|
||
project,
|
||
layout,
|
||
instance,
|
||
associatedObject,
|
||
pixiContainer,
|
||
pixiResourcesLoader
|
||
);
|
||
this._radius = parseFloat(
|
||
this._associatedObject
|
||
.getProperties(this.project)
|
||
.get('radius')
|
||
.getValue()
|
||
);
|
||
if (this._radius <= 0) this._radius = 1;
|
||
const colorHex = objectsRenderingService.rgbOrHexToHexNumber(
|
||
this._associatedObject
|
||
.getProperties(this.project)
|
||
.get('color')
|
||
.getValue()
|
||
);
|
||
this._color = [
|
||
((colorHex >> 16) & 0xff) / 255,
|
||
((colorHex >> 8) & 0xff) / 255,
|
||
(colorHex & 0xff) / 255,
|
||
];
|
||
|
||
const geometry = new PIXI.Geometry();
|
||
const shader = PIXI.Shader.from(
|
||
`
|
||
precision mediump float;
|
||
attribute vec2 aVertexPosition;
|
||
|
||
uniform mat3 translationMatrix;
|
||
uniform mat3 projectionMatrix;
|
||
varying vec2 vPos;
|
||
|
||
void main() {
|
||
vPos = aVertexPosition;
|
||
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
|
||
}`,
|
||
`
|
||
precision mediump float;
|
||
uniform vec2 center;
|
||
uniform float radius;
|
||
uniform vec3 color;
|
||
uniform mat3 translationMatrix;
|
||
uniform mat3 projectionMatrix;
|
||
|
||
varying vec2 vPos;
|
||
|
||
void main() {
|
||
float l = length(vPos - center);
|
||
float intensity = 0.0;
|
||
if(l < radius)
|
||
intensity = clamp((radius - l)*(radius - l)/(radius*radius), 0.0, 1.0);
|
||
gl_FragColor = vec4(color*intensity, 1.0);
|
||
}
|
||
`,
|
||
{
|
||
center: [this._instance.getX(), this._instance.getY()],
|
||
radius: this._radius,
|
||
color: this._color,
|
||
}
|
||
);
|
||
|
||
this._vertexBuffer = new Float32Array([
|
||
this._instance.getX() - this._radius,
|
||
this._instance.getY() + this._radius,
|
||
this._instance.getX() + this._radius,
|
||
this._instance.getY() + this._radius,
|
||
this._instance.getX() + this._radius,
|
||
this._instance.getY() - this._radius,
|
||
this._instance.getX() - this._radius,
|
||
this._instance.getY() - this._radius,
|
||
]);
|
||
|
||
geometry
|
||
.addAttribute('aVertexPosition', this._vertexBuffer, 2)
|
||
.addIndex([0, 1, 2, 2, 3, 0]);
|
||
|
||
this._pixiObject = new PIXI.Mesh(geometry, shader);
|
||
this._pixiObject.blendMode = PIXI.BLEND_MODES.ADD;
|
||
this._pixiContainer.addChild(this._pixiObject);
|
||
this.update();
|
||
}
|
||
RenderedLightObjectInstance.prototype = Object.create(
|
||
RenderedInstance.prototype
|
||
);
|
||
|
||
/**
|
||
* Return the path to the thumbnail of the specified object.
|
||
*/
|
||
RenderedLightObjectInstance.getThumbnail = function (
|
||
project,
|
||
resourcesLoader,
|
||
object
|
||
) {
|
||
return 'CppPlatform/Extensions/lightIcon32.png';
|
||
};
|
||
|
||
/**
|
||
* This is called to update the PIXI object on the scene editor
|
||
*/
|
||
RenderedLightObjectInstance.prototype.update = function () {
|
||
this._pixiObject.shader.uniforms.center = new Float32Array([
|
||
this._instance.getX(),
|
||
this._instance.getY(),
|
||
]);
|
||
|
||
this._vertexBuffer[0] = this._instance.getX() - this._radius;
|
||
this._vertexBuffer[1] = this._instance.getY() + this._radius;
|
||
this._vertexBuffer[2] = this._instance.getX() + this._radius;
|
||
this._vertexBuffer[3] = this._instance.getY() + this._radius;
|
||
this._vertexBuffer[4] = this._instance.getX() + this._radius;
|
||
this._vertexBuffer[5] = this._instance.getY() - this._radius;
|
||
this._vertexBuffer[6] = this._instance.getX() - this._radius;
|
||
this._vertexBuffer[7] = this._instance.getY() - this._radius;
|
||
|
||
this._pixiObject.geometry
|
||
.getBuffer('aVertexPosition')
|
||
.update(this._vertexBuffer);
|
||
};
|
||
|
||
/**
|
||
* Return the width of the instance, when it's not resized.
|
||
*/
|
||
RenderedLightObjectInstance.prototype.getDefaultWidth = function () {
|
||
return this._pixiObject.width;
|
||
};
|
||
|
||
/**
|
||
* Return the height of the instance, when it's not resized.
|
||
*/
|
||
RenderedLightObjectInstance.prototype.getDefaultHeight = function () {
|
||
return this._pixiObject.height;
|
||
};
|
||
|
||
RenderedLightObjectInstance.prototype.getOriginX = function () {
|
||
return this._radius;
|
||
};
|
||
|
||
RenderedLightObjectInstance.prototype.getOriginY = function () {
|
||
return this._radius;
|
||
};
|
||
|
||
objectsRenderingService.registerInstanceRenderer(
|
||
'Lighting::LightObject',
|
||
RenderedLightObjectInstance
|
||
);
|
||
},
|
||
};
|