Compare commits

..

11 Commits

Author SHA1 Message Date
Arthur Pacaud
1640ca49d7 Fix games hanging when using an empty While event (#2480) 2021-03-28 16:55:22 +01:00
D8H
654b8d0bc1 Change zoom in the scene editor so that it's centered on the cursor (#2478)
* This makes it easier to navigate in the scene by zooming in and out
2021-03-28 15:46:37 +01:00
Florian Rival
2a47d2f630 Auto close issues related to b105 web-app update
Don't show in changelog
2021-03-28 15:40:02 +01:00
Nilay Majorwar
e1423e0b90 Add One Dark theme (for the editor and for JavaScript code blocks) (#2476) 2021-03-28 15:28:56 +01:00
D8H
458430bbfb Fix platformer objects wrongly able to jump after falling (#2477) 2021-03-28 14:37:22 +01:00
Florian Rival
2a5c18474e Fix warning 2021-03-27 17:51:02 +00:00
D8H
827c53e9d5 Add an option to display an isometric grid in the scene editor (#2445) 2021-03-27 17:13:39 +00:00
Florian Rival
019920009a Fix ToJSON wrongly transforming an empty string to a variable containing the number 0 (#2475) 2021-03-27 15:44:45 +00:00
Nilay Majorwar
aaed10a598 Fix bug in theme creation script (#2474)
Only show in developer changelog
2021-03-27 12:20:22 +00:00
Arthur Pacaud
193ce8bcf8 Add VS Code debug configuration (#2466)
This allows to use breakpoints and debug the react app from inside the IDE.

Only show in developer changelog
2021-03-25 21:49:12 +00:00
Florian Rival
fd020fd2a3 Bump newIDE version 2021-03-25 20:16:20 +00:00
31 changed files with 1291 additions and 197 deletions

View File

@@ -18,3 +18,10 @@ jobs:
type: "body"
regex: ".*Scroll down to '\\.\\.\\.\\.'.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed because it seems that you have not included any steps to reproduce the bug.\n\nGitHub is a place for the technical development of GDevelop itself - you may want to go on the [forum](https://forum.gdevelop-app.com/), the Discord chat or [read the documentation](http://wiki.compilgames.net/doku.php/gdevelop5/start) to learn more about GDevelop. Thanks!"
- name: Autoclose known beta 105 web-app update bug
uses: arkon/issue-closer-action@v1.1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
type: "body"
regex: ".*_instance.getRawFloatProperty is not a function.*"
message: "Hi @${issue.user.login}! 👋 This issue was automatically closed as this seems to be a known bug. It can be solved by **closing entirely the web-app and opening it again**. This will allow the web-app to auto-update and the problem should be gone."

15
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
}

View File

@@ -17,6 +17,7 @@ LayoutEditorCanvasOptions::LayoutEditorCanvasOptions()
gridHeight(32),
gridOffsetX(0),
gridOffsetY(0),
gridType("rectangular"),
gridR(158),
gridG(180),
gridB(255),
@@ -30,6 +31,7 @@ void LayoutEditorCanvasOptions::SerializeTo(SerializerElement& element) const {
element.SetAttribute("gridHeight", gridHeight);
element.SetAttribute("gridOffsetX", gridOffsetX);
element.SetAttribute("gridOffsetY", gridOffsetY);
element.SetAttribute("gridType", gridType);
element.SetAttribute("gridR", gridR);
element.SetAttribute("gridG", gridG);
element.SetAttribute("gridB", gridB);
@@ -42,10 +44,11 @@ void LayoutEditorCanvasOptions::UnserializeFrom(
grid = element.GetBoolAttribute("grid");
snap = element.GetBoolAttribute("snap");
windowMask = element.GetBoolAttribute("windowMask");
gridWidth = element.GetIntAttribute("gridWidth", 32);
gridHeight = element.GetIntAttribute("gridHeight", 32);
gridOffsetX = element.GetIntAttribute("gridOffsetX", 0);
gridOffsetY = element.GetIntAttribute("gridOffsetY", 0);
gridWidth = element.GetDoubleAttribute("gridWidth", 32);
gridHeight = element.GetDoubleAttribute("gridHeight", 32);
gridOffsetX = element.GetDoubleAttribute("gridOffsetX", 0);
gridOffsetY = element.GetDoubleAttribute("gridOffsetY", 0);
gridType = element.GetStringAttribute("gridType", "rectangular");
gridR = element.GetIntAttribute("gridR", 158);
gridG = element.GetIntAttribute("gridG", 180);
gridB = element.GetIntAttribute("gridB", 255);

View File

@@ -40,10 +40,11 @@ class GD_CORE_API LayoutEditorCanvasOptions {
bool grid; ///< True if grid activated in editor
bool snap; ///< True if snap to grid activated in editor
int gridWidth; ///< Grid width in editor
int gridHeight; ///< Grid height in editor
int gridOffsetX; ///< Grid X offset
int gridOffsetY; ///< Grid Y offset
double gridWidth; ///< Grid width in editor
double gridHeight; ///< Grid height in editor
double gridOffsetX; ///< Grid X offset
double gridOffsetY; ///< Grid Y offset
gd::String gridType; ///< Grid type: rectangular or isometric
int gridR; ///< Grid red color in editor
int gridG; ///< Grid green color in editor
int gridB; ///< Grid blue color in editor

View File

@@ -0,0 +1,78 @@
describe('gdjs.PlatformerObjectRuntimeBehavior Benchmark', function () {
let runtimeScene;
let objects;
const duplicateCount = 60;
const stepCount = 6000;
beforeEach(function () {
runtimeScene = makeTestRuntimeScene();
objects = new Array(duplicateCount);
for (let i = 0; i < duplicateCount; ++i) {
// Put a platformer object on a platform
object = new gdjs.RuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
{
type: 'PlatformBehavior::PlatformerObjectBehavior',
name: 'auto1',
gravity: 1500,
maxFallingSpeed: 1500,
acceleration: 500,
deceleration: 1500,
maxSpeed: 500,
jumpSpeed: 900,
canGrabPlatforms: true,
ignoreDefaultControls: true,
slopeMaxAngle: 60,
jumpSustainTime: 0.2,
roundCoordinates: true,
},
],
});
object.getWidth = function () {
return 10;
};
object.getHeight = function () {
return 20;
};
runtimeScene.addObject(object);
object.setPosition(100 * i + 60 * 5, 400 * i - 32);
objects[i] = object;
// Put a platform.
for (let p = 0; p < 10; ++p) {
const platform = addPlatformObject(runtimeScene);
platform.setPosition(100 * i + p * platform.getWidth(), 400 * i - 10);
}
}
});
it('benchmark', function () {
this.timeout(30000);
for (let b = 0; b < 10; ++b) {
const benchmarkSuite = makeBenchmarkSuite({
benchmarksCount: 1,
iterationsCount: stepCount,
});
benchmarkSuite.add('jump in loop', (t) => {
for (let i = 0; i < duplicateCount; ++i) {
const object = objects[i];
if (t % 60 == i % 60) {
object.getBehavior('auto1').simulateJumpKey();
}
if (t + (i % 61) < 31) {
object.getBehavior('auto1').simulateRightKey();
}
if (t + (i % 61) >= 31) {
object.getBehavior('auto1').simulateLeftKey();
}
}
runtimeScene.renderAndStep(1000 / 60);
});
console.log(benchmarkSuite.run());
}
});
});

View File

@@ -1357,7 +1357,9 @@ namespace gdjs {
this._behavior = behavior;
}
enter() {}
enter() {
this._behavior._canJump = false;
}
leave() {}

View File

@@ -471,6 +471,35 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getY()).to.be(-30);
});
it('should not jump after falling from a platform', function () {
// Ensure the object falls on the platform
for (let i = 0; i < 10; ++i) {
runtimeScene.renderAndStep(1000 / 60);
}
// Check the object is on the platform
// So at this point, the object could jump
expect(object.getY()).to.be(-30); // -30 = -10 (platform y) + -20 (object height)
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isFallingWithoutJumping()).to.be(
false
);
expect(object.getBehavior('auto1').isMoving()).to.be(false);
// Fell from the platform
for (let i = 0; i < 35; ++i) {
object.getBehavior('auto1').simulateLeftKey();
runtimeScene.renderAndStep(1000 / 60);
}
// Try to jump
object.getBehavior('auto1').simulateJumpKey();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isJumping()).to.be(false);
expect(object.getBehavior('auto1').isFalling()).to.be(true);
expect(object.getBehavior('auto1').isFallingWithoutJumping()).to.be(true);
});
it('should not grab a platform while in the ascending phase of a jump', function () {
const topPlatform = addPlatformObject(runtimeScene);
topPlatform.setPosition(12, -80);
@@ -1576,83 +1605,4 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getY()).to.be.below(platform.getY());
});
});
describe.skip('(benchmark)', function () {
let runtimeScene;
let objects;
const duplicateCount = 60;
const stepCount = 6000;
beforeEach(function () {
runtimeScene = makeTestRuntimeScene();
objects = new Array(duplicateCount);
for (let i = 0; i < duplicateCount; ++i) {
// Put a platformer object on a platform
object = new gdjs.RuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
{
type: 'PlatformBehavior::PlatformerObjectBehavior',
name: 'auto1',
gravity: 1500,
maxFallingSpeed: 1500,
acceleration: 500,
deceleration: 1500,
maxSpeed: 500,
jumpSpeed: 900,
canGrabPlatforms: true,
ignoreDefaultControls: true,
slopeMaxAngle: 60,
jumpSustainTime: 0.2,
roundCoordinates: true,
},
],
});
object.getWidth = function () {
return 10;
};
object.getHeight = function () {
return 20;
};
runtimeScene.addObject(object);
object.setPosition(100 * i + 60 * 5, 400 * i - 32);
objects[i] = object;
// Put a platform.
for (let p = 0; p < 10; ++p) {
const platform = addPlatformObject(runtimeScene);
platform.setPosition(100 * i + p * platform.getWidth(), 400 * i - 10);
}
}
});
it('benchmark', function () {
this.timeout(30000);
for (let b = 0; b < 10; ++b) {
const benchmarkSuite = makeBenchmarkSuite({
benchmarksCount: 1,
iterationsCount: stepCount,
});
benchmarkSuite.add('jump in loop', (t) => {
for (let i = 0; i < duplicateCount; ++i) {
const object = objects[i];
if (t % 60 == i % 60) {
object.getBehavior('auto1').simulateJumpKey();
}
if (t + (i % 61) < 31) {
object.getBehavior('auto1').simulateRightKey();
}
if (t + (i % 61) >= 31) {
object.getBehavior('auto1').simulateLeftKey();
}
}
runtimeScene.renderAndStep(1000 / 60);
});
console.log(benchmarkSuite.run());
}
});
});
});

View File

@@ -347,11 +347,22 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
[](gd::BaseEvent& event_,
gd::EventsCodeGenerator& codeGenerator,
gd::EventsCodeGenerationContext& parentContext) {
gd::String outputCode;
gd::WhileEvent& event = dynamic_cast<gd::WhileEvent&>(event_);
// Context is "reset" each time the event is repeated (i.e. objects are
// picked again)
// Prevent code generation if the event is empty, as this would
// get the game stuck in a never ending loop.
if (
event.GetWhileConditions().empty() &&
event.GetConditions().empty() &&
event.GetActions().empty()
)
return gd::String(
"\n// While event not generated to prevent an infinite loop.\n");
gd::String outputCode;
// Context is "reset" each time the event is repeated (i.e. objects
// are picked again)
gd::EventsCodeGenerationContext context;
context.InheritsFrom(parentContext);
context.ForbidReuse();
@@ -459,10 +470,12 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
// Define references to variables (if they exist)
if (keyIteratorExists)
outputCode +=
"const $KEY_ITERATOR_REFERENCE = $KEY_ITERATOR_VARIABLE_ACCESSOR;\n";
"const $KEY_ITERATOR_REFERENCE = "
"$KEY_ITERATOR_VARIABLE_ACCESSOR;\n";
if (valueIteratorExists)
outputCode +=
"const $VALUE_ITERATOR_REFERENCE = $VALUE_ITERATOR_VARIABLE_ACCESSOR;\n";
"const $VALUE_ITERATOR_REFERENCE = "
"$VALUE_ITERATOR_VARIABLE_ACCESSOR;\n";
outputCode +=
"const $ITERABLE_REFERENCE = $ITERABLE_VARIABLE_ACCESSOR;\n";

View File

@@ -168,11 +168,13 @@ namespace gdjs {
) {
if (obj === null) {
variable.setString('null');
} else if (
(typeof obj === 'number' || typeof obj === 'string') &&
!isNaN(obj as number)
) {
variable.setNumber(obj as number);
} else if (typeof obj === 'number') {
if (Number.isNaN(obj)) {
console.warn('Variables cannot be set to NaN, setting it to 0.');
variable.setNumber(0);
} else {
variable.setNumber(obj);
}
} else if (typeof obj === 'string') {
variable.setString(obj);
} else if (typeof obj === 'undefined') {
@@ -195,9 +197,6 @@ namespace gdjs {
}
} else if (typeof obj === 'symbol') {
variable.setString(obj.toString());
} else if (typeof obj === 'number' && isNaN(obj)) {
console.warn('Variables cannot be set to NaN, setting it to 0.');
variable.setNumber(0);
} else if (typeof obj === 'bigint') {
if (obj > Number.MAX_SAFE_INTEGER)
console.warn(

View File

@@ -239,7 +239,7 @@ namespace gdjs {
},
setValue: () => {},
getValue: () => 0,
getChild: () => gdjs.VariablesContainer.badVariable,
getChild: () => gdjs.VariablesContainer.badVariable,
getChildAt: () => gdjs.VariablesContainer.badVariable,
hasChild: function () {
return false;

View File

@@ -28,7 +28,7 @@
"scripts": {
"check-types": "tsc",
"build": "node scripts/build.js",
"test": "cd tests && npm test",
"test": "cd tests && npm run test-benchmark",
"format": "prettier --write \"Runtime/**/*.ts\" \"../Extensions/**/*.ts\" \"../Extensions/**/*.spec.js\"",
"check-format": "prettier --list-different \"Runtime/**/*.ts\" \"../Extensions/**/*.ts\" \"../Extensions/**/*.spec.js\"",
"generate-doc": "jsdoc -c docs/jsdoc.conf.json docs/DocMainPage.md"

View File

@@ -12,10 +12,13 @@ npm install
Then launch tests:
```bash
npm test #This will use Chrome Headless
npm test:firefox #To run tests using Firefox
npm run test:watch # This will use Chrome Headless
npm run test-benchmark:watch # This will also run benchmarks
npm run test:firefox:watch # To run tests using Firefox
```
> ⚠️ If you're working on GDJS or extensions, make sure to have the development version of GDevelop running so that changes in GDJS or extension files are rebuilt (or run `npm run build` in `GDJS/`, but better run GDevelop so that any changes are watched).
## About the tests
### Unit tests

View File

@@ -1,4 +1,15 @@
module.exports = function(config) {
const testFiles = [
'../../Extensions/**/tests/**.spec.js',
'./tests/**/*.js',
];
const benchmarkFiles = [
'./benchmarks/init.js',
'../../Extensions/**/benchmark/**.benchmark.js',
'./benchmarks/**/*.js',
];
config.set({
frameworks: ['mocha'],
browserNoActivityTimeout: 400000,
@@ -63,15 +74,12 @@ module.exports = function(config) {
//All tests files:
'./tests-utils/init.pixiruntimegamewithassets.js',
'../../Extensions/**/tests/**.spec.js',
'./tests/**/*.js',
//All benchmark files:
'./benchmarks/init.js',
'./benchmarks/**/*.js',
// Assets
{pattern: './tests-utils/assets/*.jpg', watched: false, included: false, served: true, nocache: false}
{pattern: './tests-utils/assets/*.jpg', watched: false, included: false, served: true, nocache: false},
...testFiles,
...(config.enableBenchmarks ? benchmarkFiles : [])
]
});
};

View File

@@ -7,6 +7,8 @@
"scripts": {
"test": "karma start --browsers ChromeHeadless --single-run",
"test:watch": "karma start --browsers ChromeHeadless",
"test-benchmark": "karma start --browsers ChromeHeadless --single-run --enableBenchmarks",
"test-benchmark:watch": "karma start --browsers ChromeHeadless --enableBenchmarks",
"test:firefox": "karma start --browsers Firefox --single-run",
"test:firefox:watch": "karma start --browsers Firefox"
},

View File

@@ -126,7 +126,11 @@ describe('gdjs.Variable', function () {
it('can be serialized to JSON', function () {
var structure = new gdjs.Variable({ value: '0' });
// Verify numbers serialization
structure.getChild('a').setNumber(5);
// Verify structures serialization
structure.getChild('b').getChild('alpha').setString('Apples');
structure.getChild('b').getChild('beta').setBoolean(true);
structure
@@ -135,15 +139,24 @@ describe('gdjs.Variable', function () {
.setString(
'String with quotes "", and a backslash \\ and new line \\n \\n\\r and brackets {[{}]}!'
);
// Verify array serialization
const array = structure.getChild('c');
array.castTo('array');
array.pushValue(49);
array.pushVariableCopy(structure.getChild('b'));
// Verify empty string serialization
structure.getChild('d').setString("");
// Verify boolean serialization
structure.getChild('e').setBoolean(false);
structure.getChild('f').setBoolean(true);
const b =
'{"alpha": "Apples","beta": true,"Child with quotes \\"\\" and a backlash \\\\": "String with quotes \\"\\", and a backslash \\\\ and new line \\\\n \\\\n\\\\r and brackets {[{}]}!"}';
expect(gdjs.evtTools.network.variableStructureToJSON(structure)).to.be(
`{"a": 5,"b": ${b},"c": [49,${b}]}`
`{"a": 5,"b": ${b},"c": [49,${b}],"d": "","e": false,"f": true}`
);
});
@@ -153,7 +166,7 @@ describe('gdjs.Variable', function () {
'{"alpha": "Apples","beta": true,"Child with quotes \\"\\" and a backlash \\\\": "String with quotes \\"\\", and a backslash \\\\ and new line \\\\n \\\\n\\\\r and brackets {[{}]}!"}';
gdjs.evtTools.network.jsonToVariableStructure(
`{"a": 5,"b": ${b},"c": [49,${b}]}`,
`{"a": 5,"b": ${b},"c": [49,${b}],"d": "","e": false, "f": true}`,
structure
);
@@ -187,6 +200,15 @@ describe('gdjs.Variable', function () {
).to.be(
'String with quotes "", and a backslash \\ and new line \\n \\n\\r and brackets {[{}]}!'
);
expect(structure.getChild('d').getType()).to.be('string');
expect(structure.getChild('d').getAsString()).to.be('');
expect(structure.getChild('e').getType()).to.be('boolean');
expect(structure.getChild('e').getAsBoolean()).to.be(false);
expect(structure.getChild('f').getType()).to.be('boolean');
expect(structure.getChild('f').getAsBoolean()).to.be(true);
});
it('exposes a badVariable that is neutral for all operations', function () {
expect(gdjs.VariablesContainer.badVariable.getValue()).to.be(0);

View File

@@ -5,21 +5,26 @@ const fs = require('fs');
const shell = require('shelljs');
const readThemeRegistry = require('./lib/ReadThemeRegistry');
let theme = args[2];
if (!theme) {
let themeName = args[2];
if (!themeName) {
shell.echo('❌ Please enter a theme name');
}
if (!theme.toLowerCase().endsWith('theme')) {
theme = theme + 'Theme';
if (themeName.toLowerCase().endsWith('theme')) {
// Remove unnecessary trailing "Theme" from input
// eg: "Foo Bar" => "Foo Bar", "Foo Theme" => "Foo", "FooTheme" => "Foo"
const lastIndex = themeName.toLowerCase().lastIndexOf('theme');
themeName = themeName.slice(0, lastIndex).trim();
}
const themeId = theme.replace(/\s+/, '');
// Remove spaces and append 'Theme' to get full theme identifier
// eg: "Foo Bar" => "FooBarTheme"
const themeId = themeName.replace(/\s+/, '') + 'Theme';
const dir = path.resolve(__dirname, '../src/UI/Theme/', themeId);
if (fs.existsSync(dir)) {
shell.echo('❌ Theme `' + theme + '` already exists');
shell.echo('❌ Theme `' + themeName + '` already exists');
process.exit(0);
}
@@ -45,7 +50,7 @@ shell.echo('✅ Created index.js');
const registry = readThemeRegistry()
.concat({
id: themeId,
name: theme,
name: themeName,
});
const imports =

View File

@@ -1,4 +1,4 @@
import { createGdevelopTheme } from '../theme';
import { createGdevelopTheme } from '../CreateTheme';
import styles from './$THEME_IDVariables.json';
import './$THEME_IDVariables.css';

View File

@@ -0,0 +1,399 @@
const themeData = {
base: 'vs-dark',
inherit: true,
rules: [
{
foreground: 'abb2bf',
token: 'text',
},
{
foreground: 'abb2bf',
token: 'source',
},
{
foreground: 'adb7c9',
token: 'variable.parameter.function',
},
{
foreground: '5f697a',
fontStyle: ' italic',
token: 'comment',
},
{
foreground: '5f697a',
fontStyle: ' italic',
token: 'punctuation.definition.comment',
},
{
foreground: 'adb7c9',
token: 'none',
},
{
foreground: 'adb7c9',
token: 'keyword.operator',
},
{
foreground: 'cd74e8',
token: 'keyword',
},
{
foreground: 'eb6772',
token: 'variable',
},
{
foreground: '5cb3fa',
token: 'entity.name.function',
},
{
foreground: '5cb3fa',
token: 'meta.require',
},
{
foreground: '5cb3fa',
token: 'support.function.any-method',
},
{
foreground: 'f0c678',
token: 'support.class',
},
{
foreground: 'f0c678',
token: 'entity.name.class',
},
{
foreground: 'f0c678',
token: 'entity.name.type.class',
},
{
foreground: 'adb7c9',
token: 'meta.class',
},
{
foreground: '5cb3fa',
token: 'keyword.other.special-method',
},
{
foreground: 'cd74e8',
token: 'storage',
},
{
foreground: '5ebfcc',
token: 'support.function',
},
{
foreground: '9acc76',
token: 'string',
},
{
foreground: '9acc76',
token: 'constant.other.symbol',
},
{
foreground: '9acc76',
token: 'entity.other.inherited-class',
},
{
foreground: 'db9d63',
token: 'constant.numeric',
},
{
foreground: 'db9d63',
token: 'none',
},
{
foreground: 'db9d63',
token: 'none',
},
{
foreground: 'db9d63',
token: 'constant',
},
{
foreground: 'eb6772',
token: 'entity.name.tag',
},
{
foreground: 'db9d63',
token: 'entity.other.attribute-name',
},
{
foreground: 'db9d63',
token: 'entity.other.attribute-name.id',
},
{
foreground: 'db9d63',
token: 'punctuation.definition.entity',
},
{
foreground: 'cd74e8',
token: 'meta.selector',
},
{
foreground: 'db9d63',
token: 'none',
},
{
foreground: '5cb3fa',
token: 'markup.heading punctuation.definition.heading',
},
{
foreground: '5cb3fa',
token: 'entity.name.section',
},
{
foreground: 'db9d63',
token: 'keyword.other.unit',
},
{
foreground: 'f0c678',
token: 'markup.bold',
},
{
foreground: 'f0c678',
token: 'punctuation.definition.bold',
},
{
foreground: 'cd74e8',
token: 'markup.italic',
},
{
foreground: 'cd74e8',
token: 'punctuation.definition.italic',
},
{
foreground: '9acc76',
token: 'markup.raw.inline',
},
{
foreground: 'eb6772',
token: 'string.other.link',
},
{
foreground: 'eb6772',
token: 'punctuation.definition.string.end.markdown',
},
{
foreground: 'db9d63',
token: 'meta.link',
},
{
foreground: 'eb6772',
token: 'markup.list',
},
{
foreground: 'db9d63',
token: 'markup.quote',
},
{
foreground: 'adb7c9',
background: '515151',
token: 'meta.separator',
},
{
foreground: '9acc76',
token: 'markup.inserted',
},
{
foreground: 'eb6772',
token: 'markup.deleted',
},
{
foreground: 'cd74e8',
token: 'markup.changed',
},
{
foreground: '5ebfcc',
token: 'constant.other.color',
},
{
foreground: '5ebfcc',
token: 'string.regexp',
},
{
foreground: '5ebfcc',
token: 'constant.character.escape',
},
{
foreground: 'c94e42',
token: 'punctuation.section.embedded',
},
{
foreground: 'c94e42',
token: 'variable.interpolation',
},
{
foreground: 'ffffff',
background: 'e05252',
token: 'invalid.illegal',
},
{
foreground: '2d2d2d',
background: 'f99157',
token: 'invalid.broken',
},
{
foreground: '2c323d',
background: 'd27b53',
token: 'invalid.deprecated',
},
{
foreground: '2c323d',
background: '747369',
token: 'invalid.unimplemented',
},
{
foreground: 'eb6772',
token:
'source.json meta.structure.dictionary.json string.quoted.double.json',
},
{
foreground: '9acc76',
token:
'source.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json',
},
{
foreground: 'eb6772',
token:
'source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json string.quoted.double.json',
},
{
foreground: '9acc76',
token:
'source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json string.quoted.double.json',
},
{
foreground: 'cd74e8',
token:
'text.html.laravel-blade source.php.embedded.line.html entity.name.tag.laravel-blade',
},
{
foreground: 'cd74e8',
token:
'text.html.laravel-blade source.php.embedded.line.html support.constant.laravel-blade',
},
{
foreground: 'db9d63',
token:
'source.python meta.function.python meta.function.parameters.python variable.parameter.function.python',
},
{
foreground: '5ebfcc',
token: 'source.python meta.function-call.python support.type.python',
},
{
foreground: 'cd74e8',
token: 'source.python keyword.operator.logical.python',
},
{
foreground: 'f0c678',
token:
'source.python meta.class.python punctuation.definition.inheritance.begin.python',
},
{
foreground: 'f0c678',
token:
'source.python meta.class.python punctuation.definition.inheritance.end.python',
},
{
foreground: 'db9d63',
token:
'source.python meta.function-call.python meta.function-call.arguments.python variable.parameter.function.python',
},
{
foreground: 'db9d63',
token:
'text.html.basic source.php.embedded.block.html support.constant.std.php',
},
{
foreground: 'f0c678',
token:
'text.html.basic source.php.embedded.block.html meta.namespace.php entity.name.type.namespace.php',
},
{
foreground: 'db9d63',
token: 'source.js meta.function.js support.constant.js',
},
{
foreground: 'cd74e8',
token:
'text.html.basic` source.php.embedded.block.html constant.other.php',
},
{
foreground: 'db9d63',
token:
'text.html.basic source.php.embedded.block.html support.other.namespace.php',
},
{
foreground: 'adb7c9',
token:
'text.tex.latex meta.function.environment.math.latex string.other.math.block.environment.latex meta.definition.label.latex variable.parameter.definition.label.latex',
},
{
foreground: 'cd74e8',
fontStyle: ' italic',
token: 'text.tex.latex meta.function.emph.latex markup.italic.emph.latex',
},
{
foreground: 'adb7c9',
token: 'source.js variable.other.readwrite.js',
},
{
foreground: 'adb7c9',
token:
'source.js meta.function-call.with-arguments.js variable.function.js',
},
{
foreground: 'adb7c9',
token:
'source.js meta.group.braces.round meta.group.braces.curly meta.function-call.method.without-arguments.js variable.function.js',
},
{
foreground: 'adb7c9',
token:
'source.js meta.group.braces.round meta.group.braces.curly variable.other.object.js',
},
{
foreground: 'adb7c9',
token:
'source.js meta.group.braces.round meta.group.braces.curly constant.other.object.key.js string.unquoted.label.js',
},
{
foreground: 'adb7c9',
token:
'source.js meta.group.braces.round meta.group.braces.curly constant.other.object.key.js punctuation.separator.key-value.js',
},
{
foreground: 'adb7c9',
token:
'source.js meta.group.braces.round meta.group.braces.curly meta.function-call.method.with-arguments.js variable.function.js',
},
{
foreground: 'adb7c9',
token:
'source.js meta.function-call.method.with-arguments.js variable.function.js',
},
{
foreground: 'adb7c9',
token:
'source.js meta.function-call.method.without-arguments.js variable.function.js',
},
],
colors: {
'editor.foreground': '#6c7079',
'editor.background': '#2B303B',
'editor.selectionBackground': '#bbccf51b',
'editor.inactiveSelectionBackground': '#bbccf51b',
'editor.lineHighlightBackground': '#8cc2fc0b',
'editorCursor.foreground': '#528bff',
'editorWhitespace.foreground': '#747369',
'editorIndentGuide.background': '#464c55',
'editorIndentGuide.activeBackground': '#464c55',
'editor.selectionHighlightBorder': '#bbccf51b',
},
};
export default {
name: 'One-Dark',
themeName: 'one-dark',
themeData,
};

View File

@@ -7,6 +7,7 @@ import SolarizedLight from './SolarizedLight';
import VibrantInk from './VibrantInk';
import GitHub from './GitHub';
import NordDark from './NordDark';
import OneDark from './OneDark';
type CodeEditorTheme = {|
name: string,
@@ -37,6 +38,7 @@ const themes: Array<CodeEditorTheme> = [
TomorrowNight,
VibrantInk,
NordDark,
OneDark,
];
export const getAllThemes = () => themes;

View File

@@ -50,38 +50,122 @@ export default class SelectionRectangle {
this.viewPosition.getHeight()
);
const startXPos =
Math.floor(sceneStartPoint[0] / options.gridWidth) * options.gridWidth;
const startYPos =
Math.floor(sceneStartPoint[1] / options.gridHeight) * options.gridHeight;
const offsetX =
((options.gridOffsetX % options.gridWidth) + options.gridWidth) %
options.gridWidth;
const offsetY =
((options.gridOffsetY % options.gridHeight) + options.gridHeight) %
options.gridHeight;
const endXPos =
Math.ceil(sceneEndPoint[0] / options.gridWidth) * options.gridWidth;
const endYPos =
Math.ceil(sceneEndPoint[1] / options.gridHeight) * options.gridHeight;
const startX =
Math.floor((sceneStartPoint[0] - offsetX) / options.gridWidth) *
options.gridWidth +
offsetX;
const startY =
Math.floor((sceneStartPoint[1] - offsetY) / options.gridHeight) *
options.gridHeight +
offsetY;
for (
let Xpos = startXPos + options.gridOffsetX;
Xpos < endXPos;
Xpos += options.gridWidth
) {
const start = this.viewPosition.toCanvasCoordinates(Xpos, startYPos);
const end = this.viewPosition.toCanvasCoordinates(Xpos, endYPos);
const endX =
Math.ceil((sceneEndPoint[0] - offsetX) / options.gridWidth) *
options.gridWidth +
offsetX;
const endY =
Math.ceil((sceneEndPoint[1] - offsetY) / options.gridHeight) *
options.gridHeight +
offsetY;
this.pixiGrid.moveTo(start[0], start[1]);
this.pixiGrid.lineTo(end[0], end[1]);
}
if (options.gridType === 'isometric') {
const countX = Math.round((endX - startX) / options.gridWidth);
const countY = Math.round((endY - startY) / options.gridHeight);
const lineCount = countX + countY;
for (let i = 0; i < lineCount; ++i) {
let lineStartX;
let lineStartY;
if (i < countX) {
// top
lineStartX = startX + options.gridWidth / 2 + i * options.gridWidth;
lineStartY = startY;
} else {
// right
lineStartX = endX;
lineStartY =
startY + options.gridHeight / 2 + (i - countX) * options.gridHeight;
}
let lineEndX;
let lineEndY;
if (i < countY) {
// left
lineEndX = startX;
lineEndY = startY + options.gridHeight / 2 + i * options.gridHeight;
} else {
// bottom
lineEndX =
startX + options.gridWidth / 2 + (i - countY) * options.gridWidth;
lineEndY = endY;
}
const start = this.viewPosition.toCanvasCoordinates(
lineStartX,
lineStartY
);
const end = this.viewPosition.toCanvasCoordinates(lineEndX, lineEndY);
this.pixiGrid.moveTo(start[0], start[1]);
this.pixiGrid.lineTo(end[0], end[1]);
}
for (let i = 0; i < lineCount; ++i) {
let lineStartX;
let lineStartY;
if (i < countY) {
// reverse left
lineStartX = startX;
lineStartY =
startY +
options.gridHeight / 2 +
(countY - 1 - i) * options.gridHeight;
} else {
// top
lineStartX =
startX + options.gridWidth / 2 + (i - countY) * options.gridWidth;
lineStartY = startY;
}
let lineEndX;
let lineEndY;
if (i < countX) {
// bottom
lineEndX = startX + options.gridWidth / 2 + i * options.gridWidth;
lineEndY = endY;
} else {
// reverse right
lineEndX = endX;
lineEndY =
startY +
options.gridHeight / 2 +
(lineCount - 1 - i) * options.gridHeight;
}
const start = this.viewPosition.toCanvasCoordinates(
lineStartX,
lineStartY
);
const end = this.viewPosition.toCanvasCoordinates(lineEndX, lineEndY);
this.pixiGrid.moveTo(start[0], start[1]);
this.pixiGrid.lineTo(end[0], end[1]);
}
} else {
for (let x = startX; x < endX; x += options.gridWidth) {
const start = this.viewPosition.toCanvasCoordinates(x, startY);
const end = this.viewPosition.toCanvasCoordinates(x, endY);
for (
let Ypos = startYPos + options.gridOffsetY;
Ypos < endYPos;
Ypos += options.gridHeight
) {
const start = this.viewPosition.toCanvasCoordinates(startXPos, Ypos);
const end = this.viewPosition.toCanvasCoordinates(endXPos, Ypos);
this.pixiGrid.moveTo(start[0], start[1]);
this.pixiGrid.lineTo(end[0], end[1]);
}
this.pixiGrid.moveTo(start[0], start[1]);
this.pixiGrid.lineTo(end[0], end[1]);
for (let y = startY; y < endY; y += options.gridHeight) {
const start = this.viewPosition.toCanvasCoordinates(startX, y);
const end = this.viewPosition.toCanvasCoordinates(endX, y);
this.pixiGrid.moveTo(start[0], start[1]);
this.pixiGrid.lineTo(end[0], end[1]);
}
}
this.pixiGrid.endFill();

View File

@@ -14,15 +14,13 @@ const roundPositionsToGrid = (
const newPos = pos;
if (options.grid && options.snap) {
newPos[0] = roundPosition(
newPos[0],
roundPosition(
newPos,
options.gridWidth,
options.gridOffsetX
);
newPos[1] = roundPosition(
newPos[1],
options.gridHeight,
options.gridOffsetY
options.gridOffsetX,
options.gridOffsetY,
options.gridType
);
} else {
newPos[0] = Math.round(newPos[0]);

View File

@@ -7,24 +7,27 @@ export default class InstancesMover {
this.instancePositions = {};
this.totalDeltaX = 0;
this.totalDeltaY = 0;
this.temporaryPoint = [0, 0];
}
setOptions(options) {
this.options = options;
}
_roundXPosition(x, noGridSnap) {
if (!this.options.snap || !this.options.grid || noGridSnap)
return Math.round(x);
return roundPosition(x, this.options.gridWidth, this.options.gridOffsetX);
}
_roundYPosition(y, noGridSnap) {
if (!this.options.snap || !this.options.grid || noGridSnap)
return Math.round(y);
return roundPosition(y, this.options.gridHeight, this.options.gridOffsetY);
_roundPosition(pos, noGridSnap) {
if (!this.options.snap || !this.options.grid || noGridSnap) {
pos[0] = Math.round(pos[0]);
pos[1] = Math.round(pos[1]);
return;
}
roundPosition(
pos,
this.options.gridWidth,
this.options.gridHeight,
this.options.gridOffsetX,
this.options.gridOffsetY,
this.options.gridType
);
}
_getMoveDeltaX(followAxis) {
@@ -55,19 +58,13 @@ export default class InstancesMover {
y: selectedInstance.getY(),
};
}
selectedInstance.setX(
this._roundXPosition(
initialPosition.x + this._getMoveDeltaX(followAxis),
noGridSnap
)
);
selectedInstance.setY(
this._roundYPosition(
initialPosition.y + this._getMoveDeltaY(followAxis),
noGridSnap
)
);
this.temporaryPoint[0] =
initialPosition.x + this._getMoveDeltaX(followAxis);
this.temporaryPoint[1] =
initialPosition.y + this._getMoveDeltaY(followAxis);
this._roundPosition(this.temporaryPoint, noGridSnap);
selectedInstance.setX(this.temporaryPoint[0]);
selectedInstance.setY(this.temporaryPoint[1]);
}
}

View File

@@ -48,8 +48,8 @@ export default class ViewPosition {
toSceneCoordinates = (x: number, y: number): [number, number] => {
x -= this._width / 2;
y -= this._height / 2;
x /= Math.abs(this._pixiContainer.scale.x);
y /= Math.abs(this._pixiContainer.scale.y);
x /= Math.abs(this.options.zoomFactor);
y /= Math.abs(this.options.zoomFactor);
var viewRotation = 0;
var tmp = x;
@@ -80,8 +80,8 @@ export default class ViewPosition {
Math.sin((viewRotation / 180) * Math.PI) * tmp +
Math.cos((viewRotation / 180) * Math.PI) * y;
x *= Math.abs(this._pixiContainer.scale.x);
y *= Math.abs(this._pixiContainer.scale.y);
x *= Math.abs(this.options.zoomFactor);
y *= Math.abs(this.options.zoomFactor);
return [x + this._width / 2, y + this._height / 2];
};

View File

@@ -158,7 +158,7 @@ export default class InstancesEditor extends Component<Props> {
this.pixiRenderer.view.onwheel = event => {
if (this.keyboardShortcuts.shouldZoom()) {
this.zoomBy(-event.deltaY / 5000);
this.zoomOnCursorBy(-event.deltaY / 5000);
} else if (this.keyboardShortcuts.shouldScrollHorizontally()) {
this.viewPosition.scrollBy(-event.deltaY / 10, 0);
} else {
@@ -457,6 +457,23 @@ export default class InstancesEditor extends Component<Props> {
this.setZoomFactor(this.getZoomFactor() + value);
}
/**
* Zoom and scroll so that the cursor stays on the same position scene-wise.
*/
zoomOnCursorBy(value: number) {
const beforeZoomCursorPosition = this.getLastCursorSceneCoordinates();
this.setZoomFactor(this.getZoomFactor() + value);
const afterZoomCursorPosition = this.getLastCursorSceneCoordinates();
// Compensate for the cursor change in position
this.viewPosition.scrollBy(
beforeZoomCursorPosition[0] - afterZoomCursorPosition[0],
beforeZoomCursorPosition[1] - afterZoomCursorPosition[1]
);
if (this.props.onViewPositionChanged) {
this.props.onViewPositionChanged(this.viewPosition);
}
}
getZoomFactor = () => {
return this.props.options.zoomFactor;
};

View File

@@ -2,6 +2,7 @@ import { Trans } from '@lingui/macro';
import React, { Component } from 'react';
import FlatButton from '../UI/FlatButton';
import TextField from '../UI/TextField';
import Checkbox from '../UI/Checkbox';
import { ColumnStackLayout, ResponsiveLineStackLayout } from '../UI/Layout';
import Dialog from '../UI/Dialog';
import ColorField from '../UI/ColorField';
@@ -21,6 +22,7 @@ export default class SetupGridDialog extends Component {
gridOffsetX: this.state.gridOffsetX,
gridOffsetY: this.state.gridOffsetY,
gridColor: this.state.gridColor,
gridType: this.state.gridType,
});
};
@@ -69,7 +71,7 @@ export default class SetupGridDialog extends Component {
type="number"
value={this.state.gridWidth}
onChange={(e, value) =>
this.setState({ gridWidth: parseInt(value, 10) })
this.setState({ gridWidth: parseFloat(value) })
}
/>
<TextField
@@ -78,7 +80,7 @@ export default class SetupGridDialog extends Component {
type="number"
value={this.state.gridHeight}
onChange={(e, value) =>
this.setState({ gridHeight: parseInt(value, 10) })
this.setState({ gridHeight: parseFloat(value) })
}
/>
</ResponsiveLineStackLayout>
@@ -89,7 +91,7 @@ export default class SetupGridDialog extends Component {
type="number"
value={this.state.gridOffsetX}
onChange={(e, value) =>
this.setState({ gridOffsetX: parseInt(value, 10) })
this.setState({ gridOffsetX: parseFloat(value) })
}
/>
<TextField
@@ -98,7 +100,18 @@ export default class SetupGridDialog extends Component {
type="number"
value={this.state.gridOffsetY}
onChange={(e, value) =>
this.setState({ gridOffsetY: parseInt(value, 10) })
this.setState({ gridOffsetY: parseFloat(value) })
}
/>
</ResponsiveLineStackLayout>
<ResponsiveLineStackLayout noMargin expand>
<Checkbox
checked={this.state.gridType === 'isometric'}
label={<Trans>Isometric</Trans>}
onCheck={(e, check) =>
this.setState({
gridType: check ? 'isometric' : 'rectangular',
})
}
/>
</ResponsiveLineStackLayout>

View File

@@ -0,0 +1,11 @@
import { createGdevelopTheme } from '../CreateTheme';
import styles from './OneDarkThemeVariables.json';
import './OneDarkThemeVariables.css';
export default createGdevelopTheme(
styles,
'OneDarkTheme',
'dark',
'hue-rotate(-10deg) saturate(50%)'
);

View File

@@ -0,0 +1,406 @@
{
"theme": {
"primary": {
"color": {
"value": "#61AFEF"
},
"text-contrast-color": {
"value": "#FFFFFF"
}
},
"secondary": {
"color": {
"value": "#D6DEEC"
},
"text-contrast-color": {
"value": "#282C34"
}
},
"surface": {
"window": {
"background-color": {
"value": "#21252B"
}
},
"canvas": {
"background-color": {
"value": "#282C34"
}
},
"alternate-canvas": {
"background-color": {
"value": "#3E4452"
}
}
},
"selection": {
"background-color": {
"value": "#3E4452"
},
"color": {
"value": "#D6DEEC"
}
},
"text": {
"default": {
"color": {
"value": "#D6DEEC"
}
},
"secondary": {
"color": {
"value": "#ABB2BF"
}
},
"disabled": {
"color": {
"value": "#9AA1AD"
}
},
"contrast": {
"color": {
"value": "#D6DEEC"
}
}
},
"message": {
"warning": {
"color": {
"value": "#E5C07B"
}
},
"error": {
"color": {
"value": "#E06C75"
}
},
"valid": {
"color": {
"value": "#98C379"
}
},
"empty-shadow": {
"color": {
"value": "transparent"
}
}
},
"toolbar-separator": {
"color": {
"value": "#3E4452"
}
},
"closable-tabs": {
"default": {
"background-color": {
"value": "#21252B"
},
"color": {
"value": "#D6DEEC"
}
},
"selected": {
"background-color": {
"value": "#4C5361"
},
"color": {
"value": "#D6DEEC"
}
}
},
"list-item": {
"group": {
"text": {
"color": {
"value": "#ABB2BF"
}
},
"text-deprecated": {
"color": {
"value": "#9AA1AD"
}
}
},
"separator": {
"color": {
"value": "#3E4452"
}
},
"warning": {
"color": {
"value": "#E5C07B"
}
},
"error": {
"color": {
"value": "#E06C75"
}
}
},
"right-icon": {
"color": {
"value": "#FFFFFF"
}
},
"palette": {
"black": {
"value": "#6E2A2A"
},
"white": {
"value": "#D6DEEC"
}
}
},
"input": {
"border-bottom": {
"color": {
"value": "#D6DEEC"
}
}
},
"tabs": {
"background-color": {
"value": "#4C5361"
}
},
"event-sheet": {
"event-tree": {
"background-color": {
"value": "#282C34"
}
},
"rst": {
"move-handle": {
"background-color": {
"value": "#3E4452"
}
},
"move-handle-hover": {
"background-color": {
"value": "#6C7D96"
}
},
"line": {
"background-color": {
"value": "#ABB2BF"
}
},
"row-contents": {
"background-color": {
"value": "#282C34"
},
"color": {
"value": "#D6DEEC"
}
},
"row-search": {
"border-color": {
"value": "#18DCF2"
},
"border-left-width-match": {
"value": "3px"
},
"border-left-width-focus": {
"value": "5px"
}
}
},
"rst-with-results": {
"background-color": {
"value": "#D3D3D3"
},
"border": {
"value": "1px #D3D3D3 solid"
}
},
"selectable": {
"background-color": {
"value": "rgba(0, 0, 0, 0.1)"
},
"border": {
"value": "1px #282C34 solid"
},
"selected-border": {
"value": "1px #4AB0E4 dashed"
}
},
"conditions": {
"background-color": {
"value": "#3E4452"
},
"border-color": {
"value": "#3E4452"
},
"border": {
"value": "1px #3E4452 solid"
},
"color": {
"value": "rgb(209, 209, 209)"
},
"border-right-color": {
"value": "#494949"
},
"border-bottom-color": {
"value": "black"
}
},
"actions": {
"background-color": {
"value": "#282C34"
},
"color": {
"value": "#D6DEEC"
}
},
"sub-instructions": {
"border-color": {
"value": "#282C34"
},
"border": {
"value": "1px #282C34 solid"
}
},
"instruction-parameter": {
"base": {
"color": {
"value": "#98C379"
}
},
"expression": {
"color": {
"value": "#E5C07B"
}
},
"object": {
"color": {
"value": "#C678DD"
}
},
"behavior": {
"color": {
"value": "#E09563"
}
},
"operator": {
"color": {
"value": "#B77CFF"
}
},
"var": {
"color": {
"value": "#56B6C2"
}
},
"error": {
"color": {
"value": "#E06C75"
},
"background-color": {
"value": "#E06C7544"
}
}
},
"drop-indicator": {
"can-drop": {
"border-top-color": {
"value": "#61AFEF"
}
},
"cannot-drop": {
"border-top-color": {
"value": "#E06C75"
}
}
},
"link": {
"container": {
"background-color": {
"value": "#3E4452"
},
"color": {
"value": "#D6DEEC"
}
},
"selectable-link": {
"color": {
"value": "#C678DD"
}
}
}
},
"markdown": {
"blockquote": {
"border-left": {
"color": {
"value": "rgba(221, 221, 221, 0.6)"
}
}
},
"link": {
"color": {
"value": "rgb(221, 221, 221)"
}
}
},
"mosaic": {
"title": {
"color": {
"value": "#FFFFFF"
}
},
"layout": {
"border-color": {
"value": "#3E4452"
},
"background-color": {
"value": "#21252B"
}
},
"toolbar": {
"background-color": {
"value": "#4C5361"
},
"color": {
"value": "#D6DEEC"
},
"border-color-hover": {
"value": "#D6DEEC"
}
}
},
"table": {
"border": {
"color": {
"value": "#282C34"
}
},
"text": {
"color": {
"value": "#ABB2BF"
},
"color-header": {
"value": "#D6DEEC"
}
},
"row": {
"odd": {
"background-color": {
"value": "#282C34"
}
},
"even": {
"background-color": {
"value": "#21252B"
}
},
"selected": {
"background-color": {
"value": "#3E4452"
},
"color": {
"value": "#D6DEEC"
}
}
}
}
}

View File

@@ -3,6 +3,7 @@ import DefaultTheme from './DefaultTheme';
import DarkTheme from './DarkTheme';
import NordTheme from './NordTheme';
import SolarizedDarkTheme from './SolarizedDarkTheme';
import OneDarkTheme from './OneDarkTheme';
/*eslint no-useless-computed-key: "off"*/
export const themes = {
@@ -10,4 +11,5 @@ export const themes = {
['Dark']: DarkTheme,
['Nord']: NordTheme,
['Solarized Dark']: SolarizedDarkTheme,
['One Dark']: OneDarkTheme,
};

View File

@@ -1,12 +1,69 @@
// @flow
export const roundPosition = (
pos: number,
gridSize: number,
gridOffset: number
pos: [number, number],
gridWidth: number,
gridHeight: number,
gridOffsetX: number,
gridOffsetY: number,
gridType: string
) => {
if (gridSize <= 0) {
return Math.round(pos);
if (gridType === 'isometric') {
if (gridWidth <= 0 || gridHeight <= 0) {
pos[0] = Math.round(pos[0]);
pos[1] = Math.round(pos[1]);
return;
}
// Why do we need this?
// Take a 2x2 squares, put a diamond inside each square,
// there is a 5th diamond in the center
// So, half cells are needed, but some are not to be used.
// It makes a pattern in diagonal like this:
// o-o-o-
// -o-o-o
// o-o-o-
let cellX = Math.round(((pos[0] - gridOffsetX) * 2) / gridWidth);
let cellY = Math.round(((pos[1] - gridOffsetY) * 2) / gridHeight);
if ((((cellX + cellY) % 2) + 2) % 2 === 1) {
// This cell should not be used, find the nearest one
const deltaX =
(pos[0] - ((cellX / 2) * gridWidth + gridOffsetX)) / gridWidth;
const deltaY =
(pos[1] - ((cellY / 2) * gridHeight + gridOffsetY)) / gridHeight;
if (Math.abs(deltaX) > Math.abs(deltaY)) {
if (deltaX > 0) {
cellX++;
} else {
cellX--;
}
} else {
if (deltaY > 0) {
cellY++;
} else {
cellY--;
}
}
}
// magnet to the half cell
pos[0] = (cellX / 2) * gridWidth + gridOffsetX;
pos[1] = (cellY / 2) * gridHeight + gridOffsetY;
} else {
if (gridWidth <= 0) {
pos[0] = Math.round(pos[0]);
} else {
pos[0] =
Math.floor((pos[0] - gridOffsetX) / gridWidth) * gridWidth +
gridOffsetX;
}
if (gridHeight <= 0) {
pos[1] = Math.round(pos[1]);
} else {
pos[1] =
Math.floor((pos[1] - gridOffsetY) / gridHeight) * gridHeight +
gridOffsetY;
}
}
return Math.floor((pos - gridOffset) / gridSize) * gridSize + gridOffset;
};

View File

@@ -2,7 +2,7 @@
"name": "gdevelop",
"productName": "GDevelop 5",
"description": "GDevelop 5 IDE - the open-source, cross-platform game engine designed for everyone",
"version": "5.0.0-beta106",
"version": "5.0.0-beta107",
"author": "Florian Rival",
"license": "MIT",
"homepage": "http://gdevelop-app.com",