Compare commits

..

6 Commits

Author SHA1 Message Date
Florian Rival
64cad25b81 Bump newIDE version (#4752) 2022-12-26 16:24:35 +01:00
Florian Rival
8472e30342 Show a warning about Piskel/Jfxr/Yarn not being available yet for cloud projects on desktop (#4751) 2022-12-26 16:23:29 +01:00
Clément Pasteau
1b7d258727 Remove mentions of "experimental" for features and tools widely used and supported (#4749) 2022-12-26 15:02:56 +01:00
github-actions[bot]
946b77093d Update translations [skip ci] (#4742)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2022-12-26 14:10:34 +01:00
Florian Rival
74c882f219 Fix wrong scrollbars in the Sprite editor 2022-12-26 12:50:40 +01:00
Florian Rival
abd417c494 Fix games crash because of some extensions not included in the generated games
Revert "Fix missing export files by including the free functions of every extension that is actually used (#4672)" (#4746) (commit 93121d1a1c)
2022-12-26 12:36:39 +01:00
27 changed files with 218 additions and 102 deletions

View File

@@ -151,7 +151,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsTimeExtension(
extension
.AddAction("Wait",
_("Wait X seconds (experimental)"),
_("Wait X seconds"),
_("Waits a number of seconds before running "
"the next actions (and sub-events)."),
_("Wait _PARAM0_ seconds"),

View File

@@ -28,7 +28,7 @@ module.exports = {
extension
.setExtensionInformation(
'DialogueTree',
_('Dialogue Tree (experimental)'),
_('Dialogue Tree'),
'Handle dialogue trees, made using Yarn Spinner. Useful to make complex dialogues with multiple choices. The Yarn Spinner editor is embedded in GDevelop so you can edit your dialogues without leaving GDevelop.',
'Todor Imreorov',
'Open source (MIT License)'
@@ -36,7 +36,7 @@ module.exports = {
.setExtensionHelpPath('/all-features/dialogue-tree')
.setCategory('Game mechanic');
extension
.addInstructionOrExpressionGroupMetadata(_('Dialogue Tree (experimental)'))
.addInstructionOrExpressionGroupMetadata(_('Dialogue Tree'))
.setIcon('JsPlatform/Extensions/yarn32.png');
extension

View File

@@ -28,14 +28,14 @@ module.exports = {
extension
.setExtensionInformation(
'Leaderboards',
_('Leaderboards (experimental)'),
_('Leaderboards'),
_('Allow your game to send scores to your leaderboards.'),
'Florian Rival',
'Open source (MIT License)'
)
.setExtensionHelpPath('/all-features/leaderboards')
.setCategory('Players')
.addInstructionOrExpressionGroupMetadata(_('Leaderboards (experimental)'))
.addInstructionOrExpressionGroupMetadata(_('Leaderboards'))
.setIcon('JsPlatform/Extensions/leaderboard.svg');
extension

View File

@@ -28,7 +28,7 @@ module.exports = {
extension
.setExtensionInformation(
'P2P',
_('P2P (experimental)'),
_('P2P'),
'Allow game instances to communicate remotely using messages sent via WebRTC (P2P).',
'Arthur Pacaud (arthuro555)',
'MIT'
@@ -36,7 +36,7 @@ module.exports = {
.setExtensionHelpPath('/all-features/p2p')
.setCategory('Network');
extension
.addInstructionOrExpressionGroupMetadata(_('P2P (experimental)'))
.addInstructionOrExpressionGroupMetadata(_('P2P'))
.setIcon('JsPlatform/Extensions/p2picon.svg');
extension

View File

@@ -28,7 +28,7 @@ module.exports = {
extension
.setExtensionInformation(
'PlayerAuthentication',
_('Player Authentication (experimental)'),
_('Player Authentication'),
_('Allow your game to authenticate players.'),
'Florian Rival',
'Open source (MIT License)'
@@ -36,9 +36,7 @@ module.exports = {
.setExtensionHelpPath('/all-features/player-authentication')
.setCategory('Players');
extension
.addInstructionOrExpressionGroupMetadata(
_('Player Authentication (experimental)')
)
.addInstructionOrExpressionGroupMetadata(_('Player Authentication'))
.setIcon('JsPlatform/Extensions/authentication.svg');
extension

View File

@@ -25,16 +25,18 @@ module.exports = {
gd /*: libGDevelop */
) {
const extension = new gd.PlatformExtension();
extension.setExtensionInformation(
'TextInput',
_('Text Input'),
_('A text field the player can type text into.'),
'Florian Rival',
'MIT'
)
.setCategory('User interface');
extension.addInstructionOrExpressionGroupMetadata(_("Text Input"))
.setIcon("JsPlatform/Extensions/text_input.svg");
extension
.setExtensionInformation(
'TextInput',
_('Text Input'),
_('A text field the player can type text into.'),
'Florian Rival',
'MIT'
)
.setCategory('User interface');
extension
.addInstructionOrExpressionGroupMetadata(_('Text Input'))
.setIcon('JsPlatform/Extensions/text_input.svg');
const textInputObject = new gd.ObjectJsImplementation();
// $FlowExpectedError - ignore Flow warning as we're creating an object
@@ -275,7 +277,7 @@ module.exports = {
const object = extension
.addObject(
'TextInputObject',
_('Text input (experimental)'),
_('Text input'),
_('A text field the player can type text into.'),
'JsPlatform/Extensions/text_input.svg',
textInputObject
@@ -645,8 +647,7 @@ module.exports = {
update() {
const instance = this._instance;
const properties = this._associatedObjectConfiguration
.getProperties();
const properties = this._associatedObjectConfiguration.getProperties();
const placeholder =
instance.getRawStringProperty('placeholder') ||

View File

@@ -107,7 +107,7 @@ const defineTileMap = function (
.setLabel(_('Atlas image'))
.setDescription(
_(
"Tiled only - not useful for LDtk files. The Atlas image containing the tileset."
'Tiled only - not useful for LDtk files. The Atlas image containing the tileset.'
)
)
.setGroup(_('Tiled only: Tileset and Atlas image'))
@@ -223,7 +223,12 @@ const defineTileMap = function (
'JsPlatform/Extensions/tile_map.svg'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.addParameter('tilemapResource', _('Tilemap file (Tiled or LDtk)'), '', false)
.addParameter(
'tilemapResource',
_('Tilemap file (Tiled or LDtk)'),
'',
false
)
.getCodeExtraInformation()
.setFunctionName('isTilemapJsonFile');
@@ -240,7 +245,12 @@ const defineTileMap = function (
'JsPlatform/Extensions/tile_map.svg'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.addParameter('tilemapResource', _('Tilemap file (Tiled or LDtk)'), '', false)
.addParameter(
'tilemapResource',
_('Tilemap file (Tiled or LDtk)'),
'',
false
)
.getCodeExtraInformation()
.setFunctionName('setTilemapJsonFile');
@@ -376,10 +386,7 @@ const defineTileMap = function (
'JsPlatform/Extensions/tile_map.svg'
)
.addParameter('object', _('Tile map'), 'TileMap', false)
.useStandardParameters(
'number',
gd.ParameterOptions.makeNewOptions()
)
.useStandardParameters('number', gd.ParameterOptions.makeNewOptions())
.setFunctionName('getLevelndex');
object
@@ -652,7 +659,9 @@ const defineCollisionMask = function (
.addExtraInfo('json')
.setLabel(_('Tilemap JSON file'))
.setDescription(
_('This is the JSON file that was saved or exported from Tiled. LDtk is not supported yet for collisions.')
_(
'This is the JSON file that was saved or exported from Tiled. LDtk is not supported yet for collisions.'
)
)
);
objectProperties.set(
@@ -771,7 +780,7 @@ const defineCollisionMask = function (
const object = extension
.addObject(
'CollisionMask',
_('Tilemap collision mask (experimental)'),
_('Tilemap collision mask'),
_('Invisible object handling collisions with parts of a tilemap.'),
'JsPlatform/Extensions/tile_map_collision_mask32.svg',
collisionMaskObject

View File

@@ -54,8 +54,8 @@ bool Exporter::ExportWholePixiProject(
auto usedExtensions = gd::UsedExtensionsFinder::ScanProject(project);
auto exportProject = [this, &exportedProject, &exportOptions, &helper,
&usedExtensions](gd::String exportDir) {
auto exportProject = [this, &exportedProject, &exportOptions, &helper](
gd::String exportDir) {
bool exportForCordova = exportOptions["exportForCordova"];
bool exportForFacebookInstantGames =
exportOptions["exportForFacebookInstantGames"];
@@ -88,8 +88,7 @@ bool Exporter::ExportWholePixiProject(
exportedProject.GetLoadingScreen().GetGDevelopLogoStyle(),
includesFiles);
// Export files for free function, object and behaviors
helper.ExportFreeFunctionIncludes(exportedProject, includesFiles, usedExtensions);
// Export files for object and behaviors
helper.ExportObjectAndBehaviorsIncludes(exportedProject, includesFiles);
helper.ExportObjectAndBehaviorsRequiredFiles(exportedProject, resourcesFiles);

View File

@@ -41,7 +41,6 @@
#include "GDCore/Tools/Log.h"
#include "GDJS/Events/CodeGeneration/LayoutCodeGenerator.h"
#include "GDJS/Extensions/JsPlatform.h"
#include "GDCore/IDE/Events/UsedExtensionsFinder.h"
#undef CopyFile // Disable an annoying macro
namespace {
@@ -116,9 +115,7 @@ bool ExporterHelper::ExportProjectForPixiPreview(
immutableProject.GetLoadingScreen().GetGDevelopLogoStyle(),
includesFiles);
// Export files for free function, object and behaviors
auto usedExtensions = gd::UsedExtensionsFinder::ScanProject(exportedProject);
ExportFreeFunctionIncludes(exportedProject, includesFiles, usedExtensions);
// Export files for object and behaviors
ExportObjectAndBehaviorsIncludes(immutableProject, includesFiles);
ExportObjectAndBehaviorsRequiredFiles(immutableProject, resourcesFiles);
@@ -800,37 +797,6 @@ bool ExporterHelper::ExportIncludesAndLibs(
return true;
}
void ExporterHelper::ExportFreeFunctionIncludes(
gd::Project &project, std::vector<gd::String> &includesFiles,
std::set<gd::String> &usedExtensions) {
auto addIncludeFiles = [&](const std::vector<gd::String> &newIncludeFiles) {
for (const auto &includeFile : newIncludeFiles) {
InsertUnique(includesFiles, includeFile);
}
};
for (auto &&usedExtension : usedExtensions) {
if (project.HasEventsFunctionsExtensionNamed(usedExtension)) {
auto &extension = project.GetEventsFunctionsExtension(usedExtension);
for (size_t functionIndex = 0;
functionIndex < extension.GetEventsFunctionsCount(); functionIndex++) {
auto &function = extension.GetEventsFunction(functionIndex);
gd::String fullType = gd::PlatformExtension::GetEventsFunctionFullType(
usedExtension, function.GetName());
auto metadata = function.IsCondition()
? gd::MetadataProvider::GetConditionMetadata(
project.GetCurrentPlatform(), fullType)
: gd::MetadataProvider::GetActionMetadata(
project.GetCurrentPlatform(), fullType);
addIncludeFiles(metadata.GetIncludeFiles());
}
}
}
}
void ExporterHelper::ExportObjectAndBehaviorsIncludes(
const gd::Project &project, std::vector<gd::String> &includesFiles) {
auto addIncludeFiles = [&](const std::vector<gd::String> &newIncludeFiles) {

View File

@@ -247,12 +247,6 @@ class ExporterHelper {
bool ExportEffectIncludes(const gd::Project &project,
std::vector<gd::String> &includesFiles);
/**
* \brief Add the include files for all free functions from used extensions.
*/
void ExportFreeFunctionIncludes(gd::Project &project,
std::vector<gd::String> &includesFiles,
std::set<gd::String> &usedExtensions);
/**
* \brief Add the include files for all the objects of the project
* and their behaviors.

View File

@@ -43,6 +43,7 @@ type Options = {|
type CodeGenerationContext = {|
codeNamespacePrefix: string,
extensionIncludeFiles: Array<string>,
|};
const mangleName = (name: string) => {
@@ -144,6 +145,29 @@ const loadProjectEventsFunctionsExtension = (
});
};
/**
* Get the list of mandatory include files when using the
* extension.
*/
const getExtensionIncludeFiles = (
project: gdProject,
eventsFunctionsExtension: gdEventsFunctionsExtension,
options: Options,
codeNamespacePrefix: string
): Array<string> => {
return mapFor(0, eventsFunctionsExtension.getEventsFunctionsCount(), i => {
const eventsFunction = eventsFunctionsExtension.getEventsFunctionAt(i);
const codeNamespace = getFreeFunctionCodeNamespace(
eventsFunction,
codeNamespacePrefix
);
const functionName = codeNamespace + '.func'; // TODO
return options.eventsFunctionCodeWriter.getIncludeFileFor(functionName);
}).filter(Boolean);
};
/**
* Generate the code for the events based extension
*/
@@ -158,8 +182,15 @@ const generateEventsFunctionExtension = (
const codeNamespacePrefix =
'gdjs.evtsExt__' + mangleName(eventsFunctionsExtension.getName());
const extensionIncludeFiles = getExtensionIncludeFiles(
project,
eventsFunctionsExtension,
options,
codeNamespacePrefix
);
const codeGenerationContext = {
codeNamespacePrefix,
extensionIncludeFiles,
};
return Promise.all(
@@ -215,6 +246,9 @@ const generateEventsFunctionExtension = (
)
)
.then(functionInfos => {
if (!options.skipCodeGeneration) {
applyFunctionIncludeFilesDependencyTransitivity(functionInfos);
}
return extension;
});
};
@@ -267,6 +301,11 @@ const generateFreeFunction = (
.setIncludeFile(functionFile)
.setFunctionName(functionName);
// Always include the extension include files when using a free function.
codeGenerationContext.extensionIncludeFiles.forEach(includeFile => {
instructionOrExpression.addIncludeFile(includeFile);
});
if (!options.skipCodeGeneration) {
const includeFiles = new gd.SetString();
const eventsFunctionsExtensionCodeGenerator = new gd.EventsFunctionsExtensionCodeGenerator(
@@ -314,6 +353,65 @@ const generateFreeFunction = (
}
};
/**
* Add dependencies between functions according to transitivity.
* @param functionInfos free function metadatas
*/
const applyFunctionIncludeFilesDependencyTransitivity = (
functionInfos: Array<{
functionFile: string,
functionMetadata:
| gdInstructionMetadata
| gdExpressionMetadata
| gdMultipleInstructionMetadata,
}>
): void => {
// Note that the iteration order doesn't matter, for instance for:
// a -> b
// b -> c
// c -> d
//
// going from a to c:
// a -> (b -> c)
// b -> c
// c -> d
//
// or from c to a:
// a -> b
// b -> (c -> d)
// c -> d
//
// give the same result:
// a -> (b -> (c -> d))
// b -> (c -> d)
// c -> d
const includeFileSets = functionInfos.map(
functionInfo =>
new Set(functionInfo.functionMetadata.getIncludeFiles().toJSArray())
);
// For any function A of the extension...
for (let index = 0; index < functionInfos.length; index++) {
const includeFiles = includeFileSets[index];
const functionIncludeFile = functionInfos[index].functionFile;
// ...and any function B of the extension...
for (let otherIndex = 0; otherIndex < functionInfos.length; otherIndex++) {
const otherFunctionMetadata = functionInfos[otherIndex].functionMetadata;
const otherIncludeFileSet = includeFileSets[otherIndex];
// ...where function B depends on function A...
if (otherIncludeFileSet.has(functionIncludeFile)) {
// ...add function A dependencies to the function B ones.
includeFiles.forEach(includeFile => {
if (!otherIncludeFileSet.has(includeFile)) {
otherIncludeFileSet.add(includeFile);
otherFunctionMetadata.addIncludeFile(includeFile);
}
});
}
}
}
};
function generateBehavior(
project: gdProject,
extension: gdPlatformExtension,
@@ -338,6 +436,11 @@ function generateBehavior(
behaviorMetadata.setIncludeFile(includeFile);
// Always include the extension include files when using a behavior.
codeGenerationContext.extensionIncludeFiles.forEach(includeFile => {
behaviorMetadata.addIncludeFile(includeFile);
});
return Promise.resolve().then(() => {
const behaviorMethodMangledNames = new gd.MapStringString();
@@ -454,6 +557,11 @@ function generateObject(
objectMetadata.setIncludeFile(includeFile);
// Always include the extension include files when using an object.
codeGenerationContext.extensionIncludeFiles.forEach(includeFile => {
objectMetadata.addIncludeFile(includeFile);
});
return Promise.resolve().then(() => {
const objectMethodMangledNames = new gd.MapStringString();

View File

@@ -31,7 +31,6 @@ export type Exporter = {|
tabName: React.Node,
helpPage: string,
disabled?: boolean,
experimental?: boolean,
key: ExporterKey,
exportPipeline: ExportPipeline<any, any, any, any, any>,
|};

View File

@@ -35,10 +35,6 @@ export const getExtraObjectsInformation = (): {
},
],
'BitmapText::BitmapTextObject': [
{
kind: 'warning',
message: t`This object is experimental and not yet complete. It might have bugs or incomplete support in GDevelop, be sure to read the wiki by clicking on help button below.`,
},
{
kind: 'info',
message: t`For a pixel type font, you must disable the Smooth checkbox related to your texture in the game resources to disable anti-aliasing.`,

View File

@@ -243,6 +243,7 @@ export default class SpritesList extends Component<Props, void> {
editWith = (externalEditor: ResourceExternalEditor) => {
const {
project,
resourceManagementProps,
direction,
resourcesLoader,
onReplaceByDirection,
@@ -271,6 +272,7 @@ export default class SpritesList extends Component<Props, void> {
externalEditor.edit({
project,
getStorageProvider: resourceManagementProps.getStorageProvider,
resourcesLoader,
singleFrame: false,
resourceNames,

View File

@@ -31,7 +31,7 @@ import {
} from './Utils/SpriteObjectHelper';
import { type EditorProps } from '../EditorProps.flow';
import { type ResourceManagementProps } from '../../../ResourcesList/ResourceSource';
import { Column, Line } from '../../../UI/Grid';
import { Column } from '../../../UI/Grid';
import { ResponsiveLineStackLayout } from '../../../UI/Layout';
import ScrollView from '../../../UI/ScrollView';
import Checkbox from '../../../UI/Checkbox';
@@ -41,6 +41,15 @@ import SpacedDismissableTutorialMessage from './SpacedDismissableTutorialMessage
const gd: libGDevelop = global.gd;
const styles = {
animationLine: {
// Use a non standard spacing because:
// - The SortableAnimationsList won't work with <Spacer /> or <LargeSpacer /> between elements.
// - We need to visually show a difference between animations.
marginBottom: 16,
},
};
type AnimationProps = {|
animation: gdAnimation,
id: number,
@@ -82,7 +91,7 @@ class Animation extends React.Component<AnimationProps, void> {
const animationName = animation.getName();
return (
<Line expand>
<div style={styles.animationLine}>
<Column expand noMargin>
{isAnimationListLocked && (
<Column expand noMargin>
@@ -132,7 +141,7 @@ class Animation extends React.Component<AnimationProps, void> {
);
})}
</Column>
</Line>
</div>
);
}
}

View File

@@ -5,6 +5,7 @@ import { openYarn } from './LocalYarnBridge';
import { type ResourceExternalEditor } from './ResourceExternalEditor.flow';
import { sendExternalEditorOpened } from '../Utils/Analytics/EventSender';
import { t } from '@lingui/macro';
import Window from '../Utils/Window';
/**
* This is the list of editors that can be used to edit resources
@@ -17,6 +18,14 @@ const editors: Array<ResourceExternalEditor> = [
editDisplayName: t`Edit with Piskel`,
kind: 'image',
edit: options => {
const storageProvider = options.getStorageProvider();
if (storageProvider.internalName !== 'LocalFile') {
Window.showMessageBox(
'Piskel is only supported when your project is saved locally. It will be available for Cloud projects in a future version. To use Piskel, save your project on your computer first.'
);
return;
}
sendExternalEditorOpened('piskel');
return openPiskel(options);
},
@@ -27,6 +36,14 @@ const editors: Array<ResourceExternalEditor> = [
editDisplayName: t`Edit with Jfxr`,
kind: 'audio',
edit: options => {
const storageProvider = options.getStorageProvider();
if (storageProvider.internalName !== 'LocalFile') {
Window.showMessageBox(
'Jfxr is only supported when your project is saved locally. It will be available for Cloud projects in a future version. To use Jfxr, save your project on your computer first.'
);
return;
}
sendExternalEditorOpened('jfxr');
return openJfxr(options);
},
@@ -37,6 +54,14 @@ const editors: Array<ResourceExternalEditor> = [
editDisplayName: t`Edit with Yarn`,
kind: 'json',
edit: options => {
const storageProvider = options.getStorageProvider();
if (storageProvider.internalName !== 'LocalFile') {
Window.showMessageBox(
'Yarn is only supported when your project is saved locally. It will be available for Cloud projects in a future version. To use Yarn, save your project on your computer first.'
);
return;
}
sendExternalEditorOpened('yarn');
return openYarn(options);
},

View File

@@ -2,12 +2,14 @@
import { type ResourceKind } from './ResourceSource';
import ResourcesLoader from '../ResourcesLoader';
import { type MessageDescriptor } from '../Utils/i18n/MessageDescriptor.flow';
import { type StorageProvider } from '../ProjectsStorage';
/**
* These are the options passed to an external editor to edit one or more resources.
*/
export type ExternalEditorOpenOptions = {|
project: gdProject,
getStorageProvider: () => StorageProvider,
resourcesLoader: typeof ResourcesLoader,
singleFrame?: boolean, // If set to true, edition should be limited to a single frame
resourceNames: Array<string>,

View File

@@ -215,7 +215,12 @@ export default class ResourceSelector extends React.Component<Props, State> {
};
_editWith = (resourceExternalEditor: ResourceExternalEditor) => {
const { project, resourcesLoader, resourceKind } = this.props;
const {
project,
resourcesLoader,
resourceKind,
resourceManagementProps,
} = this.props;
const { resourceName } = this.state;
const resourcesManager = project.getResourcesManager();
const initialResource = resourcesManager.getResource(resourceName);
@@ -236,6 +241,7 @@ export default class ResourceSelector extends React.Component<Props, State> {
}
const externalEditorOptions = {
project,
getStorageProvider: resourceManagementProps.getStorageProvider,
resourcesLoader,
singleFrame: true,
resourceNames,
@@ -259,6 +265,7 @@ export default class ResourceSelector extends React.Component<Props, State> {
} else if (resourceKind === 'audio') {
const externalEditorOptions = {
project,
getStorageProvider: resourceManagementProps.getStorageProvider,
resourcesLoader,
resourceNames: [resourceName],
extraOptions: {
@@ -280,6 +287,7 @@ export default class ResourceSelector extends React.Component<Props, State> {
) {
const externalEditorOptions = {
project,
getStorageProvider: resourceManagementProps.getStorageProvider,
resourcesLoader,
resourceNames: [resourceName],
extraOptions: {

View File

@@ -35,7 +35,7 @@ module.exports = [
"languageCode": "ca_ES",
"languageName": "Catalan",
"languageNativeName": "Català",
"translationRatio": 0.27719821162444114
"translationRatio": 0.2774962742175857
},
{
"languageCode": "cs_CZ",
@@ -77,7 +77,7 @@ module.exports = [
"languageCode": "es_ES",
"languageName": "Spanish",
"languageNativeName": "Español",
"translationRatio": 0.98301043219076
"translationRatio": 0.9833084947839046
},
{
"languageCode": "fa_IR",
@@ -101,7 +101,7 @@ module.exports = [
"languageCode": "fr_FR",
"languageName": "French",
"languageNativeName": "Français",
"translationRatio": 0.9812220566318927
"translationRatio": 0.9815201192250372
},
{
"languageCode": "ha_HG",
@@ -149,7 +149,7 @@ module.exports = [
"languageCode": "ja_JP",
"languageName": "Japanese",
"languageNativeName": "日本語",
"translationRatio": 0.980327868852459
"translationRatio": 0.9821162444113264
},
{
"languageCode": "ka_GE",
@@ -227,7 +227,7 @@ module.exports = [
"languageCode": "pt_BR",
"languageName": "Brazilian Portuguese",
"languageNativeName": "Português brasileiro",
"translationRatio": 0.9169895678092399
"translationRatio": 0.919672131147541
},
{
"languageCode": "pt_PT",
@@ -329,7 +329,7 @@ module.exports = [
"languageCode": "vi_VN",
"languageName": "Vietnamese",
"languageNativeName": "Tiếng Việt",
"translationRatio": 0.06140089418777939
"translationRatio": 0.1250372578241431
},
{
"languageCode": "yo_NG",
@@ -341,7 +341,7 @@ module.exports = [
"languageCode": "zh_CN",
"languageName": "Chinese Simplified",
"languageNativeName": "简化字",
"translationRatio": 0.9907600596125187
"translationRatio": 0.9921013412816692
},
{
"languageCode": "zh_TW",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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.1.154",
"version": "5.1.155",
"author": "GDevelop Team <hello@gdevelop.io>",
"license": "MIT",
"homepage": "https://gdevelop.io",