Compare commits

...

105 Commits

Author SHA1 Message Date
AlexandreSi
329340cb47 Use correct reference to layer 2023-05-19 09:57:11 +02:00
AlexandreSi
1ab96b3d8e Remove forgotten ref 2023-05-18 17:30:10 +02:00
Florian Rival
3272b8eda7 Fix 2D rendering sometimes occluding/cutting semi transparent 3D objects 2023-05-18 16:58:42 +02:00
AlexandreS
cd432a22b6 Fix crash when working with instances in the scene editor (#5303) 2023-05-18 16:28:19 +02:00
AlexandreS
9ac483156e Unsubscribe effects and asynchronous tasks (#5301)
Only show in developer changelog
2023-05-17 17:44:18 +02:00
AlexandreS
d0f39027af Slightly lighten project file when lots of comments are present in the events sheets (#5299) 2023-05-17 16:45:19 +02:00
github-actions[bot]
5d8a59533d Update translations [skip ci] (#5294)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-05-17 14:48:23 +02:00
D8H
243a72008b Fix a key exception in the 3D model object editor (#5298)
* Don't show in changelog
2023-05-17 14:39:10 +02:00
Florian Rival
e56cdac96a Fix 3D rendering after loading a scene (#5296) 2023-05-17 13:12:27 +02:00
AlexandreS
cf374737fc Add support for built-in 3D games (#5285)
* This provides new 3D objects: 3D Box (perfect to create walls, floors, or billboards) and 3D Model (to import objects created in a 3D modeling app).
* 2D and 3D can be mixed in a same game. Each layer of a game can contain 2D objects, 3D objects or a mix of both.
* This allows to build 2D games, 2.5D games and full 3D games: platformers, racing games, FPS, hyper casual games. It's easy to start adding 3D objects to an existing 2D game.
* You can set up a light by adding an ambient light and/or directional light in the effects of a 3D layer. 3D objects can be configured to react to light or ignore it.
* In the future, support for 3D objects will be improved: light objects, animations, etc...
2023-05-16 17:37:49 +02:00
Florian Rival
3d043f5264 Update the GDevelop app icon on web and desktop (#5295) 2023-05-16 16:21:02 +02:00
AlexandreS
032f53e4e3 Bump newIDE version (#5293) 2023-05-16 10:34:20 +02:00
github-actions[bot]
f68ae4b8d8 Update translations [skip ci] (#5282) 2023-05-16 09:37:57 +02:00
Clément Pasteau
fa40381fb4 Fix design of login & register (#5280)
Do not show in changelog
2023-05-15 09:55:44 +02:00
Florian Rival
544b6243ba Add some tooltips in the Events Sheet (#5290) 2023-05-14 21:01:47 +02:00
supertree-wook
0a03cbb89c Add support for mouse "Forward" and "Back" buttons (#5278) 2023-05-13 16:52:30 +02:00
supertree-wook
c00d75f047 Fix typo in comments and message strings (#5287) 2023-05-12 22:20:27 +02:00
Aurélien Vivet
5dce0684f5 Fix typo (#5284)
Do not show in changelog
2023-05-12 17:47:26 +02:00
D8H
cb44999538 Fix extension refresh on a new project. (#5283)
Do not show in changelog
2023-05-12 14:12:36 +02:00
D8H
4a9de2edf3 Fix the tile map scale action (#5281)
* Operators +=, -=, *= and /= were making previews crash.
2023-05-11 18:00:52 +02:00
github-actions[bot]
8c3728f8b7 Update translations [skip ci] (#5274)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-05-10 10:36:09 +02:00
Clément Pasteau
212c58f67c Fix TextInput having extra margins on iOS (#5277) 2023-05-09 10:35:54 +02:00
github-actions[bot]
f5d89c66ea Update translations [skip ci] (#5271)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-05-04 09:22:40 +02:00
supertree-wook
8fb139ff63 Rename the readme and license files to match standard (#5272)
Do not show in changelog
2023-05-04 09:22:12 +02:00
Clément Pasteau
0c0fb5da1c Fix generated .exe not working if the project name contains special characters (#5254) 2023-05-03 11:48:17 +02:00
github-actions[bot]
65a33589c0 Update translations [skip ci] (#5251)
Co-authored-by: 4ian <4ian@users.noreply.github.com>
2023-05-02 09:37:54 +02:00
supertree-wook
7ec5a95de8 Improve bug report template on GitHub (#5261)
Do not show in changelog
2023-05-02 09:37:01 +02:00
Aurélien Vivet
26289eec99 Fix typo (#5263)
Don't show in the changelog
2023-05-02 09:36:21 +02:00
Aurélien Vivet
cc0a01bbdb Add an action to focus a text input object (#5268)
* Useful to let the player type something without having them manually clicking/touching the input on the screen.
2023-05-01 21:21:57 +02:00
supertree-wook
a0fb289aaf Update to the new forum URL in some part of the interface (#5264) 2023-04-28 13:13:55 +02:00
D8H
94045aec91 Fix typo in comments and message strings (thanks @st-Wook!) (#5262)
Co-authored-by: supertree-wook <phk09242@supertree.co>
2023-04-28 11:58:51 +02:00
supertree-wook
b70d4c54c1 Improve bug report template on GitHub (#5259)
Only show in developer changelog
2023-04-26 14:24:45 +02:00
Aurélien Vivet
f269b820fc Add 2 expressions for the Tiled Sprite to get X & Y offset of the displayed image. (#5252) 2023-04-25 10:45:59 +02:00
Clément Pasteau
f91bde1fca Limit bio and username size (#5257)
Do not show in changelog
2023-04-25 10:25:55 +02:00
AlexandreS
73ac71dadf Add possibility to open events context menu on iOS devices (#4793) 2023-04-21 16:54:26 +02:00
AlexandreS
52fcf52ecc Prevent loss of focus when editing the value of an object variable in the instance properties panel (#5247) 2023-04-21 13:39:56 +02:00
supertree-wook
55c7c4e8bf Change Bug report issue template to Github Forms (#5250) 2023-04-21 10:35:14 +02:00
github-actions[bot]
d6a15d4090 Update translations [skip ci] (#5241)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-04-21 09:46:29 +02:00
supertree-wook
21a87daf64 Clean markdown files and fix typos (#5248)
Only show in develop changelog
2023-04-20 16:22:46 +02:00
AlexandreS
c712ab3163 fix: Correctly detect project manager open to fix some stall guided lessons (#5246) 2023-04-20 13:34:54 +02:00
AlexandreS
6c27c3dcf8 Improve command palette on mobile (#5249) 2023-04-20 13:34:09 +02:00
AlexandreS
f8de6c2ea9 Fix resource opening on iOS and opening of urls (#5245) 2023-04-19 14:10:20 +02:00
AlexandreS
b2e8c23944 Add text field to search in wiki on home page learn section (#5243) 2023-04-19 12:29:17 +02:00
AlexandreS
bfc6a69d77 Decrease checkbox padding to have the same appearance as small icon buttons (#5242)
Don't show in changelog
2023-04-18 15:42:52 +02:00
D8H
3abf81fda2 Add a grab tool to the collision mask and points editors (#5235)
* It also fixes the Physics2 polygon editor that wasn't refreshed when dragging a vertex.
2023-04-18 12:19:43 +02:00
D8H
4caef9c7e6 Add an special object parameter to handle object creation in extensions (#5239)
* It allows extension users to apply actions to the created objects.
2023-04-18 09:55:19 +02:00
github-actions[bot]
f9edfdef72 Update translations [skip ci] (#5233)
Co-authored-by: Bouh <Bouh@users.noreply.github.com>
2023-04-17 10:13:26 +02:00
DaddyPluM
4e0e4b9184 Rename license.txt to license.md (#4783)
Don't show in the changelog
2023-04-15 16:00:16 +02:00
Clément Pasteau
908926bf73 Allow deleting your account from the profile (#5232) 2023-04-13 17:18:49 +02:00
github-actions[bot]
2ba81cc7d6 Update translations [skip ci] (#5231)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-04-13 14:06:56 +02:00
D8H
e77e1fb840 Allow event extensions to declare variable parameters (#5220) 2023-04-13 13:48:47 +02:00
D8H
f0f47ca2f0 Set collision mask zoom bounds relatively to the default zoom (#5229) 2023-04-13 13:48:10 +02:00
github-actions[bot]
07920fda9f Update translations [skip ci] (#5230)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-04-13 12:20:43 +02:00
D8H
48e4447212 Add a button to report extension issues (#5224) 2023-04-13 12:19:59 +02:00
Clément Pasteau
b69dcafcc6 Improve errors when registering game with translations (#5216) 2023-04-13 12:03:08 +02:00
D8H
89e06b0801 Fix collision mask editor image border size (#5228) 2023-04-13 11:52:36 +02:00
github-actions[bot]
5e1a6eb084 Update translations [skip ci] (#5225)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-04-13 10:22:32 +02:00
D8H
f89a48fb2c Allow to lock layers in the editor (#5226) 2023-04-13 10:18:54 +02:00
D8H
ddb65012ce Allow to copy and paste effects from one object or layer to another (#5207) 2023-04-12 10:39:11 +02:00
D8H
bb0fe0ea37 Allow to copy and paste behaviors from one object to another (#5205) 2023-04-12 10:38:27 +02:00
D8H
747cdf0243 Allow drag and drop or copy and paste of properties in the extension editor (#5222) 2023-04-11 18:14:27 +02:00
Clément Pasteau
385ec5e9ca Fix long touch not working on iOS Safari (#5223) 2023-04-11 16:45:16 +02:00
github-actions[bot]
bcbdc35d72 Update translations [skip ci] (#5221)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-04-11 12:10:17 +02:00
Clément Pasteau
6053369474 Rework multiple fields to have selects (#5208)
* External Layout, Animation, Effect and Effect Parameter
2023-04-11 09:25:41 +02:00
AlexandreS
ed1e0852b5 Check other scenes for conflicts when creating or manipulating global object and groups (#5215) 2023-04-11 09:09:35 +02:00
D8H
23ce46439a Show invalid resources errors in the event sheet (#5218) 2023-04-09 18:50:43 +02:00
github-actions[bot]
d9b35018e7 Update translations [skip ci] (#5200)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-04-07 17:38:03 +02:00
AlexandreS
571ab1313d Recompute full url to include a refreshed token (#5217)
Don't show in changelog
2023-04-07 16:55:23 +02:00
AlexandreS
aff82c4733 Add possibility to close preview window with Cmd/Ctrl + W (#5214)
- The previous announce about making this feature available was wrong
2023-04-07 12:05:59 +02:00
AlexandreS
2d4fc0af07 Update function configuration after toggling async function (#5212)
Don't show in changelog
2023-04-07 11:18:13 +02:00
AlexandreS
7cadf35642 Allow to open or download resources of a cloud project from the Resources editor 2023-04-06 10:17:21 +02:00
D8H
73e1ea5ff1 Fix missing errors when a variable name is used alone in an expression (#5203) 2023-04-05 12:01:43 +02:00
AlexandreS
bc87caf640 Handle cloud project opening errors better (#5202)
Don't show in changelog
2023-04-05 10:28:25 +02:00
Florian Rival
95dfb391c4 Revert Publish icon (#5201)
Don't show in changelog
2023-04-05 08:58:40 +02:00
AlexandreS
4b973c9655 Do not raise error if community link field is empty (#5199)
Don't show in changelog
2023-04-04 18:22:55 +02:00
Clément Pasteau
23f24a3939 Bump to 5.1.160 2023-04-04 16:47:53 +02:00
github-actions[bot]
b3ed22806b Update translations [skip ci] (#5192) 2023-04-04 16:47:02 +02:00
Clément Pasteau
4afefafc76 Fix Fling game tutorial being properly closed and restarted on the saved step (#5198) 2023-04-04 16:28:43 +02:00
Florian Rival
d2a1d8620f Handle management of subscriptions made on another platform (#5196) 2023-04-04 14:33:19 +02:00
AlexandreS
fdfc028a2c Scroll editor tabs when a new tab is activated or opened (#5194) 2023-04-04 10:22:26 +02:00
Peter Anderson
48cd42fee0 Fix Objects Panel button to show correct tooltip (#5195) 2023-04-04 09:21:09 +02:00
Clément Pasteau
b215eff444 Add more customization to the user profile (#5181)
* Community links can now be entered (Twitter, Reddit, Discord, etc...)
* Those links will be displayed on your GDevelop profile as well as your gd.games profile page!
2023-04-03 16:17:21 +02:00
AlexandreS
1bf66a97ae Improve the UI of the alert messages so that they are more noticeable (#5186) 2023-04-03 11:59:43 +02:00
github-actions[bot]
cc6890a495 Update translations [skip ci] (#5182)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-03-31 14:04:32 +02:00
AlexandreS
418dd834c0 Fix attempt: Check cloud project before sending it to avoid corrupt cloud project (#5183) 2023-03-31 13:45:45 +02:00
D8H
e52faae332 Test the create action picking (#5178)
* Useful for dev only
2023-03-31 13:27:34 +02:00
Clément Pasteau
31809fdf61 Add missing types (#5184)
Do not show in changelog
2023-03-31 12:32:50 +02:00
AlexandreS
17504c1713 Add possibility to revert cloud project to last sane version (#5180) 2023-03-31 11:25:15 +02:00
github-actions[bot]
e29b79fdc9 Update translations [skip ci] (#5172)
Co-authored-by: ClementPasteau <ClementPasteau@users.noreply.github.com>
2023-03-31 11:23:40 +02:00
Clément Pasteau
b0009cc999 Fix too many aliases for analytics (#5177)
Do not show in changelog
2023-03-31 11:12:57 +02:00
Clément Pasteau
384eb2b8ee Add sounds packs category on the asset store (#5179) 2023-03-30 14:39:20 +02:00
AlexandreS
5fa0627218 Randomize private asset packs in asset home (#5176)
Don't show in changelog
2023-03-30 11:11:15 +02:00
D8H
e66660d2df Fix the creation of a new frame with the image editor when the animation doesn't have a name (#5175)
* Don't show in changelog
2023-03-29 12:32:25 +02:00
Clément Pasteau
df6320c39f Display lessons on Learn section on mobile (#5174)
Do not show in changelog
2023-03-29 10:44:25 +02:00
D8H
ca667c02be [Tilemap] Fix collision name between tilemap object and collision mask object (#5173)
* Projects that use actions and conditions of the wrong object should still work. To fix the red underling of object names, actions for the right object can now be selected.
2023-03-29 10:00:18 +02:00
AlexandreS
62d0754b33 Update properties panel after paste or duplication of instances (#5171) 2023-03-28 15:36:17 +02:00
Clément Pasteau
4c4ca0d202 Try to fix providers crashing the app if services return an unexpected response (#5170)
Do not show in changelog
2023-03-28 13:08:37 +02:00
github-actions[bot]
5c68d117c6 Update translations [skip ci] (#5168)
Co-authored-by: AlexandreSi <AlexandreSi@users.noreply.github.com>
2023-03-28 11:51:21 +02:00
AlexandreS
21b9ae47ab Update icons bis (#5169)
Don't show in changelog
2023-03-28 11:33:07 +02:00
D8H
e8687119c9 Fix errors not being displayed in expressions when an object or a behavior doesn't exist (#5146)
* GDevelop will now warn when an object (or group of objects) written in an expression is misspelled or does not exist in the scene.
2023-03-27 13:55:23 +02:00
D8H
9c09e26aaa Fix the Not condition to handle its last sub condition correctly (#5074) 2023-03-27 13:29:27 +02:00
Tristan Rhodes
c223abad60 Add property a property to particle to "Jump the emitter forward in time" when it's created (#5039)
* This works like the action of the same name, and allow newly created particle emitters to act as if they were emitting since a bit of time - useful when starting a scene for example to avoid seeing the initial particles being emitted.
2023-03-27 13:27:03 +02:00
github-actions[bot]
769de55317 Update translations [skip ci] (#5166)
Co-authored-by: D8H <D8H@users.noreply.github.com>
2023-03-27 13:22:37 +02:00
Clément Pasteau
0dfe765ae1 Increase allowed time to load the game in memory on Android before failing (#5110)
* This should help old or constrained devices to better run large games.
2023-03-27 13:21:50 +02:00
D8H
b94ce2b20d When renaming or removing an object, only refactor associated external events (#5155)
* The editor used to refactor every events that were used in the object layout even if they were associated to another layout which could result to errors.
2023-03-27 12:55:00 +02:00
599 changed files with 27338 additions and 6511 deletions

View File

@@ -1,28 +0,0 @@
---
name: "\U0001F41BBug report"
about: Create a bug report about GDevelop or the game engine
title: ''
labels: ''
assignees: ''
---
## Describe the bug
A clear and concise description of what the bug is.
Please double check that the bug is not already reported in the issues list.
## To Reproduce
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
* Please include a link to a game if possible!
* If applicable, add screenshots to help explain your problem.
## Other details
* Include any OS/browser version/smartphone that you're using
* Which version of GDevelop are you using? The desktop app or the web-app?
* Add any other context about the problem here.

71
.github/ISSUE_TEMPLATE/--bug-report.yml vendored Normal file
View File

@@ -0,0 +1,71 @@
name: 🐛Bug report
description: Create a bug report about GDevelop or the game engine
body:
- type: checkboxes
attributes:
label: Is there an existing issue for this?
options:
- label: I have searched the [existing issues](https://github.com/4ian/GDevelop/issues)
required: true
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: |
* Please include a link to a game if possible!
* If applicable, add screenshots to help explain your problem.
placeholder: |
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
validations:
required: true
- type: dropdown
attributes:
label: GDevelop platform
description: Which platform of GDevelop are you using?
multiple: true
options:
- Desktop
- Web
- Mobile
validations:
required: true
- type: input
attributes:
label: GDevelop version
description: |
Which version of GDevelop are you using?
Take a look here: [Editor Home - About GDevelop - "This version of GDevelop is: ~~~"]
placeholder: 5.1.159? 5.1.160?
validations:
required: true
- type: textarea
attributes:
label: Platform info
value: |
<details>
*OS (e.g. Windows, Linux, macOS, Android, iOS)*
>
*OS Version (e.g. Windows 10, macOS 10.15)*
>
*Browser(For Web) (e.g. Chrome, Firefox, Safari)*
>
*Device(For Mobile) (e.g. iPhone 12, Samsung Galaxy S21)*
>
</details>
- type: textarea
attributes:
label: Additional context
description: Add any other context about the problem here.

View File

@@ -1,3 +1,3 @@
This is the directory where native or WebAssembly binaries of the C++ code of GDCore and GDJS are produced.
See GDevelop.js README for the instructions to compile after a change in the C++ source code.
See GDevelop.js README for the instructions to compile after a change in the C++ source code.

View File

@@ -1,5 +1,5 @@
#This is the CMake file used to build GDevelop.
#For more information, see the Readme.md file.
#For more information, see the README.md file.
cmake_minimum_required(VERSION 2.6)
cmake_policy(SET CMP0011 NEW)

View File

@@ -38,7 +38,7 @@ void CommentEvent::SerializeTo(SerializerElement &element) const {
.SetAttribute("textB", textB);
element.AddChild("comment").SetValue(com1);
element.AddChild("comment2").SetValue(com2);
if (!com2.empty()) element.AddChild("comment2").SetValue(com2);
}
void CommentEvent::UnserializeFrom(gd::Project &project,
@@ -53,7 +53,9 @@ void CommentEvent::UnserializeFrom(gd::Project &project,
textB = colorElement.GetIntAttribute("textB");
com1 = element.GetChild("comment", 0, "Com1").GetValue().GetString();
com2 = element.GetChild("comment2", 0, "Com2").GetValue().GetString();
if (element.HasChild("comment2")) {
com2 = element.GetChild("comment2", 0, "Com2").GetValue().GetString();
}
}
} // namespace gd

View File

@@ -273,8 +273,6 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
if (instrInfos.codeExtraInformation.HasCustomCodeGenerator()) {
context.EnterCustomCondition();
conditionCode += GenerateReferenceToUpperScopeBoolean(
"conditionTrue", returnBoolean, context);
conditionCode += instrInfos.codeExtraInformation.customCodeGenerator(
condition, *this, context);
maxCustomConditionsDepth =
@@ -291,7 +289,7 @@ gd::String EventsCodeGenerator::GenerateConditionCode(
condition.SetParameters(parameters);
}
// Verify that there are no mismatchs between object type in parameters.
// Verify that there are no mismatches between object type in parameters.
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
gd::String objectInParameter =
@@ -483,7 +481,7 @@ gd::String EventsCodeGenerator::GenerateActionCode(
action.SetParameters(parameters);
}
// Verify that there are no mismatchs between object type in parameters.
// Verify that there are no mismatches between object type in parameters.
for (std::size_t pNb = 0; pNb < instrInfos.parameters.size(); ++pNb) {
if (ParameterMetadata::IsObject(instrInfos.parameters[pNb].GetType())) {
gd::String objectInParameter = action.GetParameter(pNb).GetPlainString();
@@ -729,6 +727,7 @@ gd::String EventsCodeGenerator::GenerateParameterCodes(
metadata.GetType() == "tilemapResource" ||
metadata.GetType() == "tilesetResource" ||
metadata.GetType() == "videoResource" ||
metadata.GetType() == "model3DResource" ||
// Deprecated, old parameter names:
metadata.GetType() == "password" || metadata.GetType() == "musicfile" ||
metadata.GetType() == "soundfile" || metadata.GetType() == "police") {
@@ -1240,7 +1239,7 @@ size_t EventsCodeGenerator::GenerateSingleUsageUniqueIdFor(
<< std::endl;
}
// Base the unique id on the adress in memory so that the same instruction
// Base the unique id on the address in memory so that the same instruction
// in memory will get the same id across different code generations.
size_t uniqueId = (size_t)instruction;

View File

@@ -404,6 +404,18 @@ class GD_CORE_API EventsCodeGenerator {
return boolName;
}
/**
* \brief Generate the full name for accessing to a boolean variable used for
* conditions.
*
* Default implementation just returns the boolean name passed as argument.
*/
virtual gd::String GenerateUpperScopeBooleanFullName(
const gd::String& boolName,
const gd::EventsCodeGenerationContext& context) {
return boolName;
}
/**
* \brief Must create a boolean. Its value must be false.
*
@@ -495,7 +507,7 @@ class GD_CORE_API EventsCodeGenerator {
* - string : %Text expression -> string
* - layer, color, file, stringWithSelector : Same as string
* - relationalOperator : Used to make a comparison between the function
resturn value and value of the parameter preceding the relationOperator
return value and value of the parameter preceding the relationOperator
parameter -> string
* - operator : Used to update a value using a setter and a getter -> string
* - key, mouse, objectvar, scenevar, globalvar, password, musicfile,
@@ -665,19 +677,6 @@ class GD_CORE_API EventsCodeGenerator {
return "!(" + predicat + ")";
};
/**
* \brief Must create a boolean which is a reference to a boolean declared in
* the parent scope.
*
* The default implementation generates C-style code.
*/
virtual gd::String GenerateReferenceToUpperScopeBoolean(
const gd::String& referenceName,
const gd::String& referencedBoolean,
gd::EventsCodeGenerationContext& context) {
return "bool & " + referenceName + " = " + referencedBoolean + ";\n";
}
virtual gd::String GenerateFreeCondition(
const std::vector<gd::String>& arguments,
const gd::InstructionMetadata& instrInfos,
@@ -786,7 +785,7 @@ class GD_CORE_API EventsCodeGenerator {
const gd::Project* project; ///< The project being used.
const gd::Layout* scene; ///< The scene being generated.
bool errorOccurred; ///< Must be set to true if an error occured.
bool errorOccurred; ///< Must be set to true if an error occurred.
bool compilationForRuntime; ///< Is set to true if the code generation is
///< made for runtime only.

View File

@@ -51,8 +51,8 @@ gd::String ExpressionCodeGenerator::GenerateExpressionCode(
codeGenerator.GetObjectsAndGroups(),
rootType);
node->Visit(validator);
if (!validator.GetErrors().empty()) {
std::cout << "Error: \"" << validator.GetErrors()[0]->GetMessage()
if (!validator.GetFatalErrors().empty()) {
std::cout << "Error: \"" << validator.GetFatalErrors()[0]->GetMessage()
<< "\" in: \"" << expression.GetPlainString() << "\" ("
<< rootType << ")" << std::endl;
@@ -170,7 +170,7 @@ void ExpressionCodeGenerator::OnVisitIdentifierNode(IdentifierNode& node) {
}
else {
// This is for function names that are put in IdentifierNode
// because the type is needed to tell them appart from variables.
// because the type is needed to tell them apart from variables.
output += GenerateDefaultValue(type);
}
}

View File

@@ -33,7 +33,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
"res/function32.png",
"res/function32.png")
.SetHelpPath("/events/functions/return")
.AddParameter("expression", "The number to be returned")
.AddParameter("expression", _("The number to be returned"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced();
@@ -48,7 +48,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
"res/function32.png",
"res/function32.png")
.SetHelpPath("/events/functions/return")
.AddParameter("string", "The text to be returned")
.AddParameter("string", _("The text to be returned"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced();
@@ -62,7 +62,37 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
"res/function32.png",
"res/function32.png")
.SetHelpPath("/events/functions/return")
.AddParameter("trueorfalse", "Should the condition be true or false?")
.AddParameter("trueorfalse", _("Should the condition be true or false?"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced();
extension
.AddAction("CopyArgumentToVariable",
_("Copy function parameter to variable"),
_("Copy a function parameter (also called \"argument\") to a variable. "
"The parameter type must be a variable."),
_("Copy the parameter _PARAM0_ into the variable _PARAM1_"),
"",
"res/function32.png",
"res/function32.png")
.SetHelpPath("/events/functions/return")
.AddParameter("functionParameterName", _("Parameter name"), "variable")
.AddParameter("scenevar", _("Scene variable"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced();
extension
.AddAction("CopyVariableToArgument",
_("Copy variable to function parameter"),
_("Copy a variable to function parameter (also called \"argument\"). "
"The parameter type must be a variable."),
_("Copy the variable _PARAM1_ into the parameter _PARAM0_"),
"",
"res/function32.png",
"res/function32.png")
.SetHelpPath("/events/functions/return")
.AddParameter("functionParameterName", _("Parameter name"), "variable")
.AddParameter("scenevar", _("Scene variable"))
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced();
@@ -77,7 +107,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
"",
"res/function32.png",
"res/function32.png")
.AddParameter("functionParameterName", "Parameter name")
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
.SetRelevantForFunctionEventsOnly()
.MarkAsAdvanced();
@@ -88,7 +118,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
_("Get function parameter (also called \"argument\") value."),
"",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name")
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
.SetRelevantForFunctionEventsOnly();
extension
@@ -98,7 +128,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
_("Get function parameter (also called \"argument\") text."),
"",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name")
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
.SetRelevantForFunctionEventsOnly();
extension
@@ -110,7 +140,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
"",
"res/function32.png",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name")
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
.UseStandardRelationalOperatorParameters(
"number", gd::ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();
@@ -124,7 +154,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsAdvancedExtension(
"",
"res/function32.png",
"res/function16.png")
.AddParameter("functionParameterName", "Parameter name")
.AddParameter("functionParameterName", _("Parameter name"), "number,string,boolean")
.UseStandardRelationalOperatorParameters(
"string", gd::ParameterOptions::MakeNewOptions())
.SetRelevantForFunctionEventsOnly();

View File

@@ -1588,7 +1588,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"Raycast",
_("Raycast"),
_("Sends a ray from the given source position and angle, "
"intersecting the closest object.\nThe instersected "
"intersecting the closest object.\nThe intersected "
"object will become the only one taken into account.\nIf "
"the condition is inverted, the object to be intersected "
"will be the farthest one within the ray radius."),
@@ -1619,7 +1619,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
"RaycastToPosition",
_("Raycast to position"),
_("Sends a ray from the given source position to the final point, "
"intersecting the closest object.\nThe instersected "
"intersecting the closest object.\nThe intersected "
"object will become the only one taken into account.\nIf "
"the condition is inverted, the object to be intersected "
"will be the farthest one within the ray radius."),

View File

@@ -48,7 +48,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsNetworkExtension(
.AddParameter("string", "Content type", "", true)
.SetParameterLongDescription(
"If empty, \"application/x-www-form-urlencoded\" will be used.")
.AddParameter("scenevar", "Reponse scene variable", "", true)
.AddParameter("scenevar", "Response scene variable", "", true)
.SetParameterLongDescription(
"The response of the server will be stored, as a string, in this "
"variable. If the server returns *JSON*, you may want to use the "

View File

@@ -126,8 +126,8 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
extension
.AddExpression(
"StrFindLast",
_("Search the last occurence in a text"),
_("Search the last occurence in a string (return the position of "
_("Search the last occurrence in a text"),
_("Search the last occurrence in a string (return the position of "
"the result, from the beginning of the string, or -1 if not "
"found)"),
"",
@@ -169,8 +169,8 @@ BuiltinExtensionsImplementer::ImplementsStringInstructionsExtension(
extension
.AddExpression(
"StrFindLastFrom",
_("Search the last occurence in a text, starting from a position"),
_("Search in a text the last occurence, starting from a position "
_("Search the last occurrence in a text, starting from a position"),
_("Search in a text the last occurrence, starting from a position "
"(return "
" the position of the result, from the beginning of the string, or "
"-1 if not found)"),

View File

@@ -62,7 +62,7 @@ class GD_CORE_API DependencyMetadata {
};
/**
* \brief Sets the type of dependecy (what will be used to install it)
* \brief Sets the type of dependency (what will be used to install it)
*
* This can either be "npm" or "cordova" for now.
*/

View File

@@ -7,9 +7,6 @@
namespace gd {
EffectMetadata::EffectMetadata(const gd::String& type_)
: type(type_), isMarkedAsNotWorkingForObjects(false) {}
EffectMetadata& EffectMetadata::SetIncludeFile(const gd::String& includeFile) {
includeFiles.clear();
includeFiles.push_back(includeFile);
@@ -23,9 +20,4 @@ EffectMetadata& EffectMetadata::AddIncludeFile(const gd::String& includeFile) {
return *this;
}
EffectMetadata& EffectMetadata::MarkAsNotWorkingForObjects() {
isMarkedAsNotWorkingForObjects = true;
return *this;
}
} // namespace gd

View File

@@ -3,8 +3,8 @@
* Copyright 2008-present Florian Rival (Florian.Rival@gmail.com). All rights
* reserved. This project is released under the MIT License.
*/
#ifndef EFFECTMETADATA_H
#define EFFECTMETADATA_H
#pragma once
#include <functional>
#include <map>
#include <memory>
@@ -25,21 +25,25 @@ class GD_CORE_API EffectMetadata {
/**
* \brief Construct an effect metadata, with the given type
*/
EffectMetadata(const gd::String& type_);
EffectMetadata(const gd::String &type_)
: type(type_), isMarkedAsNotWorkingForObjects(false),
isMarkedAsOnlyWorkingFor2D(false), isMarkedAsOnlyWorkingFor3D(false),
isMarkedAsUnique(false) {}
/**
* \brief Default constructor, only used for initializing `badEffectMetadata`.
*/
EffectMetadata() {}
/**
* \brief Default constructor, only used for initializing
* `badEffectMetadata`.
*/
EffectMetadata() {}
virtual ~EffectMetadata(){};
virtual ~EffectMetadata(){};
/**
* \brief Set the name shown to the user.
*/
EffectMetadata& SetFullName(const gd::String& fullname_) {
fullname = fullname_;
return *this;
/**
* \brief Set the name shown to the user.
*/
EffectMetadata &SetFullName(const gd::String &fullname_) {
fullname = fullname_;
return *this;
};
/**
@@ -69,11 +73,6 @@ class GD_CORE_API EffectMetadata {
*/
EffectMetadata& AddIncludeFile(const gd::String& includeFile);
/**
* \brief Mark the effect as not working as an object effect.
*/
EffectMetadata& MarkAsNotWorkingForObjects();
/**
* \brief Return a reference to the properties of this effect.
*/
@@ -117,10 +116,65 @@ class GD_CORE_API EffectMetadata {
return includeFiles;
}
/**
* \brief Mark the effect as not working as an object effect.
*/
EffectMetadata& MarkAsNotWorkingForObjects() {
isMarkedAsNotWorkingForObjects = true;
return *this;
}
/**
* \brief Mark the effect as only working for the 2D renderer.
*/
EffectMetadata& MarkAsOnlyWorkingFor2D() {
isMarkedAsOnlyWorkingFor2D = true;
return *this;
}
/**
* \brief Mark the effect as only working for the 3D renderer.
*/
EffectMetadata& MarkAsOnlyWorkingFor3D() {
isMarkedAsOnlyWorkingFor3D = true;
return *this;
}
/**
* \brief Mark the effect as only addable once.
*/
EffectMetadata& MarkAsUnique() {
isMarkedAsUnique = true;
return *this;
}
/**
* \brief Check if the effect is marked as not working as an object effect.
*/
bool IsMarkedAsNotWorkingForObjects() const { return isMarkedAsNotWorkingForObjects; };
bool IsMarkedAsNotWorkingForObjects() const {
return isMarkedAsNotWorkingForObjects;
};
/**
* \brief Check if the effect is marked as only working for the 2D renderer.
*/
bool IsMarkedAsOnlyWorkingFor2D() const {
return isMarkedAsOnlyWorkingFor2D;
};
/**
* \brief Check if the effect is marked as only working for the 3D renderer.
*/
bool IsMarkedAsOnlyWorkingFor3D() const {
return isMarkedAsOnlyWorkingFor3D;
};
/**
* \brief Check if the effect can only be added once.
*/
bool IsMarkedAsUnique() const {
return isMarkedAsUnique;
};
private:
gd::String extensionNamespace;
@@ -130,8 +184,10 @@ class GD_CORE_API EffectMetadata {
gd::String description;
std::vector<gd::String> includeFiles;
bool isMarkedAsNotWorkingForObjects;
bool isMarkedAsOnlyWorkingFor2D;
bool isMarkedAsOnlyWorkingFor3D;
bool isMarkedAsUnique;
std::map<gd::String, gd::PropertyDescriptor> properties;
};
} // namespace gd
#endif // EFFECTMETADATA_H

View File

@@ -57,7 +57,7 @@ class GD_CORE_API ObjectMetadata {
/**
* \brief Construct an object metadata, with a function that will be called
* to instanciate a new object.
* to instantiate a new object.
*/
ObjectMetadata(const gd::String& extensionNamespace_,
const gd::String& name_,
@@ -224,7 +224,7 @@ class GD_CORE_API ObjectMetadata {
/**
* \brief The "capabilities" that are offered by the base object that are
* *not* supported by this object, and should be hidden in the editor
* inferface.
* interface.
*/
const std::set<gd::String>& GetUnsupportedBaseObjectCapabilities() const {
return unsupportedBaseObjectCapabilities;
@@ -232,7 +232,7 @@ class GD_CORE_API ObjectMetadata {
/**
* \brief Add a "capability" that is offered by the base object that is *not*
* supported by this object, and should be hidden in the editor inferface.
* supported by this object, and should be hidden in the editor interface.
*/
ObjectMetadata& AddUnsupportedBaseObjectCapability(
const gd::String& capability) {
@@ -242,7 +242,7 @@ class GD_CORE_API ObjectMetadata {
/**
* \brief Check if a "capability" that is offered by the base object is *not*
* supported by this object, and should be hidden in the editor inferface.
* supported by this object, and should be hidden in the editor interface.
*/
bool IsUnsupportedBaseObjectCapability(const gd::String& capability) const {
return unsupportedBaseObjectCapabilities.find(capability) != unsupportedBaseObjectCapabilities.end();

View File

@@ -209,10 +209,10 @@ class GD_CORE_API ParameterMetadata {
* \brief Return the expression type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
* "sceneName") are replaced by "number" and "string".
* \deprecated Use gd::ValueTypeMetadata instead.
* \deprecated Use gd::ValueTypeMetadata or gd::GetExpressionPrimitiveValueType instead.
*/
static const gd::String &GetExpressionValueType(const gd::String &parameterType) {
return gd::ValueTypeMetadata::GetPrimitiveValueType(parameterType);
return gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(parameterType);
}
/** \name Serialization

View File

@@ -35,17 +35,40 @@ void ValueTypeMetadata::UnserializeFrom(const SerializerElement& element) {
const gd::String ValueTypeMetadata::numberType = "number";
const gd::String ValueTypeMetadata::stringType = "string";
const gd::String ValueTypeMetadata::variableType = "variable";
const gd::String ValueTypeMetadata::booleanType = "boolean";
const gd::String &ValueTypeMetadata::GetPrimitiveValueType(const gd::String &parameterType) {
if (parameterType == "number" || gd::ValueTypeMetadata::IsTypeExpression("number", parameterType)) {
const gd::String &ValueTypeMetadata::GetExpressionPrimitiveValueType(
const gd::String &parameterType) {
if (parameterType == "number" ||
gd::ValueTypeMetadata::IsTypeExpression("number", parameterType)) {
return ValueTypeMetadata::numberType;
}
if (parameterType == "string" || gd::ValueTypeMetadata::IsTypeExpression("string", parameterType)) {
if (parameterType == "string" ||
gd::ValueTypeMetadata::IsTypeExpression("string", parameterType)) {
return ValueTypeMetadata::stringType;
}
return parameterType;
}
const gd::String &
ValueTypeMetadata::GetPrimitiveValueType(const gd::String &parameterType) {
if (parameterType == "variable" ||
gd::ValueTypeMetadata::IsTypeExpression("variable", parameterType)) {
return ValueTypeMetadata::variableType;
}
if (parameterType == "boolean" || parameterType == "yesorno" ||
parameterType == "trueorfalse") {
return ValueTypeMetadata::booleanType;
}
// These 2 types are not strings from the code generator point of view,
// but it is for event-based extensions.
if (parameterType == "key" || parameterType == "mouse") {
return ValueTypeMetadata::stringType;
}
return GetExpressionPrimitiveValueType(parameterType);
}
const gd::String ValueTypeMetadata::numberValueType = "number";
const gd::String ValueTypeMetadata::booleanValueType = "boolean";
const gd::String ValueTypeMetadata::colorValueType = "color";

View File

@@ -178,6 +178,16 @@ class GD_CORE_API ValueTypeMetadata {
} else if (type == "variable") {
return parameterType == "objectvar" || parameterType == "globalvar" ||
parameterType == "scenevar";
} else if (type == "resource") {
return parameterType == "fontResource" ||
parameterType == "soundfile" ||
parameterType == "musicfile" ||
parameterType == "bitmapFontResource" ||
parameterType == "imageResource" ||
parameterType == "jsonResource" ||
parameterType == "tilemapResource" ||
parameterType == "tilesetResource" ||
parameterType == "model3DResource";
}
return false;
}
@@ -186,10 +196,23 @@ class GD_CORE_API ValueTypeMetadata {
* \brief Return the expression type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
* "sceneName") are replaced by "number" and "string".
*
* \note It only maps string and number types.
*/
static const gd::String &GetExpressionPrimitiveValueType(const gd::String &parameterType);
/**
* \brief Return the primitive type from the parameter type.
* Declinations of "number" and "string" types (like "forceMultiplier" or
* "sceneName") are replaced by "number" and "string".
*
* \note It also maps variable and boolean types.
*/
static const gd::String &GetPrimitiveValueType(const gd::String &parameterType);
static const gd::String numberType;
static const gd::String stringType;
static const gd::String variableType;
static const gd::String booleanType;
/**
* \brief Return the ValueTypeMetadata name for a property type.

View File

@@ -42,7 +42,7 @@ class GD_CORE_API DependenciesAnalyzer {
*
* You can also call then
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene to check if the
* external events can be compiled separatly and called by a scene. \see
* external events can be compiled separately and called by a scene. \see
* DependenciesAnalyzer::ExternalEventsCanBeCompiledForAScene
*/
DependenciesAnalyzer(const gd::Project& project_,

View File

@@ -77,24 +77,42 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
MetadataProvider::GetBehaviorAnyExpressionMetadata(
platform, behaviorType, function.functionName);
if (!function.objectName.empty()) {
// If the function needs a capability on the object that may not be covered
// by all objects, check it now.
if (!metadata.GetRequiredBaseObjectCapability().empty()) {
const gd::ObjectMetadata &objectMetadata =
MetadataProvider::GetObjectMetadata(platform, objectType);
Type returnType = StringToType(metadata.GetReturnType());
if (objectMetadata.IsUnsupportedBaseObjectCapability(
metadata.GetRequiredBaseObjectCapability())) {
RaiseTypeError(
_("This expression exists, but it can't be used on this object."),
function.objectNameLocation);
return StringToType(metadata.GetReturnType());
}
}
if (!function.objectName.empty() &&
!globalObjectsContainer.HasObjectNamed(function.objectName) &&
!globalObjectsContainer.GetObjectGroups().Has(function.objectName) &&
!objectsContainer.HasObjectNamed(function.objectName) &&
!objectsContainer.GetObjectGroups().Has(function.objectName)) {
RaiseTypeError(_("This object doesn't exist."),
function.objectNameLocation, /*isFatal=*/false);
return returnType;
}
Type returnType = StringToType(metadata.GetReturnType());
if (!function.behaviorName.empty() &&
!gd::HasBehaviorInObjectOrGroup(globalObjectsContainer, objectsContainer,
function.objectName,
function.behaviorName)) {
RaiseTypeError(_("This behavior is not attached to this object."),
function.behaviorNameLocation, /*isFatal=*/false);
return returnType;
}
if (!function.objectName.empty() &&
// If the function needs a capability on the object that may not be covered
// by all objects, check it now.
!metadata.GetRequiredBaseObjectCapability().empty()) {
const gd::ObjectMetadata &objectMetadata =
MetadataProvider::GetObjectMetadata(platform, objectType);
if (objectMetadata.IsUnsupportedBaseObjectCapability(
metadata.GetRequiredBaseObjectCapability())) {
RaiseTypeError(
_("This expression exists, but it can't be used on this object."),
function.objectNameLocation);
return returnType;
}
}
if (gd::MetadataProvider::IsBadExpressionMetadata(metadata)) {
RaiseError(
@@ -115,8 +133,8 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
"number to a string."),
function.location);
return returnType;
}
else if (parentType != Type::Number && parentType != Type::NumberOrString) {
} else if (parentType != Type::Number &&
parentType != Type::NumberOrString) {
RaiseTypeError(_("You tried to use an expression that returns a "
"number, but another type is expected:") +
" " + TypeToString(parentType),
@@ -131,8 +149,8 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
"string to a number."),
function.location);
return returnType;
}
else if (parentType != Type::String && parentType != Type::NumberOrString) {
} else if (parentType != Type::String &&
parentType != Type::NumberOrString) {
RaiseTypeError(_("You tried to use an expression that returns a "
"string, but another type is expected:") +
" " + TypeToString(parentType),
@@ -172,8 +190,7 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
_("You have not entered enough parameters for the expression.") + " " +
expectedCountMessage,
function.location);
}
else {
} else {
RaiseError(
"extra_parameter",
_("This parameter was not expected by this expression. Remove it "
@@ -217,8 +234,7 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(const gd::Functi
"parameter."),
parameter->location);
}
}
else if (gd::ParameterMetadata::IsObject(expectedParameterType)) {
} else if (gd::ParameterMetadata::IsObject(expectedParameterType)) {
if (dynamic_cast<IdentifierNode *>(parameter.get()) == nullptr) {
RaiseError(
"malformed_object_parameter",

View File

@@ -45,7 +45,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
/**
* \brief Helper function to check if a given node does not contain
* any error.
* any error including non-fatal ones.
*/
static bool HasNoErrors(const gd::Platform &platform,
const gd::ObjectsContainer &globalObjectsContainer,
@@ -54,16 +54,25 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
gd::ExpressionNode& node) {
gd::ExpressionValidator validator(platform, globalObjectsContainer, objectsContainer, rootType);
node.Visit(validator);
return validator.GetErrors().empty();
return validator.GetAllErrors().empty();
}
/**
* \brief Get only the fatal errors
*
* Expressions with fatal error can't be generated.
*/
const std::vector<ExpressionParserDiagnostic*>& GetFatalErrors() {
return fatalErrors;
};
/**
* \brief Get all the errors
*
* No errors means that the expression is valid.
*/
const std::vector<ExpressionParserDiagnostic*>& GetErrors() {
return errors;
const std::vector<ExpressionParserDiagnostic*>& GetAllErrors() {
return allErrors;
};
protected:
@@ -150,12 +159,29 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
void OnVisitNumberNode(NumberNode& node) override {
ReportAnyError(node);
childType = Type::Number;
CheckType(parentType, childType, node.location);
if (parentType == Type::String) {
RaiseTypeError(
_("You entered a number, but a text was expected (in quotes)."),
node.location);
} else if (parentType != Type::Number &&
parentType != Type::NumberOrString) {
RaiseTypeError(_("You entered a number, but this type was expected:") +
" " + TypeToString(parentType),
node.location);
}
}
void OnVisitTextNode(TextNode& node) override {
ReportAnyError(node);
childType = Type::String;
CheckType(parentType, childType, node.location);
if (parentType == Type::Number) {
RaiseTypeError(_("You entered a text, but a number was expected."),
node.location);
} else if (parentType != Type::String &&
parentType != Type::NumberOrString) {
RaiseTypeError(_("You entered a text, but this type was expected:") +
" " + TypeToString(parentType),
node.location);
}
}
void OnVisitVariableNode(VariableNode& node) override {
ReportAnyError(node);
@@ -163,7 +189,21 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
node.child->Visit(*this);
}
childType = Type::Variable;
CheckType(parentType, childType, node.location);
if (parentType == Type::String) {
RaiseTypeError(_("Variables must be surrounded by VariableString()."),
node.location);
} else if (parentType == Type::Number) {
RaiseTypeError(_("Variables must be surrounded by Variable()."),
node.location);
} else if (parentType == Type::NumberOrString) {
RaiseTypeError(
_("Variables must be surrounded by Variable() or VariableString()."),
node.location);
} else if (parentType != Type::Variable) {
RaiseTypeError(_("You entered a variable, but this type was expected:") +
" " + TypeToString(parentType),
node.location);
}
}
void OnVisitVariableAccessorNode(VariableAccessorNode& node) override {
ReportAnyError(node);
@@ -239,20 +279,26 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
enum Type {Unknown = 0, Number, String, NumberOrString, Variable, Object, Empty};
Type ValidateFunction(const gd::FunctionCallNode& function);
void ReportAnyError(const ExpressionNode& node) {
void ReportAnyError(const ExpressionNode& node, bool isFatal = true) {
if (node.diagnostic && node.diagnostic->IsError()) {
// Syntax errors are holden by the AST nodes.
// It's fine to give pointers on them as the AST live longer than errors
// handling.
errors.push_back(node.diagnostic.get());
allErrors.push_back(node.diagnostic.get());
if (isFatal) {
fatalErrors.push_back(node.diagnostic.get());
}
}
}
void RaiseError(const gd::String &type,
const gd::String &message, const ExpressionParserLocation &location) {
const gd::String &message, const ExpressionParserLocation &location, bool isFatal = true) {
auto diagnostic = gd::make_unique<ExpressionParserError>(
type, message, location);
errors.push_back(diagnostic.get());
allErrors.push_back(diagnostic.get());
if (isFatal) {
fatalErrors.push_back(diagnostic.get());
}
// Errors found by the validator are not holden by the AST nodes.
// They must be owned by the validator to keep living while errors are
// handled by the caller.
@@ -260,8 +306,8 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
}
void RaiseTypeError(
const gd::String &message, const ExpressionParserLocation &location) {
RaiseError("type_error", message, location);
const gd::String &message, const ExpressionParserLocation &location, bool isFatal = true) {
RaiseError("type_error", message, location, isFatal);
}
void RaiseOperatorError(
@@ -269,32 +315,6 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
RaiseError("invalid_operator", message, location);
}
void CheckType(Type expect, Type actual, const ExpressionParserLocation &location) {
if (actual == Type::String) {
if (expect == Type::Number) {
RaiseTypeError(_("You entered a text, but a number was expected."),
location);
}
else if (expect != Type::String && expect != Type::NumberOrString) {
RaiseTypeError(
_("You entered a text, but this type was expected:") + " " + TypeToString(expect),
location);
}
}
else if (actual == Type::Number) {
if (expect == Type::String) {
RaiseTypeError(
_("You entered a number, but a text was expected (in quotes)."),
location);
}
else if (expect != Type::Number && expect != Type::NumberOrString) {
RaiseTypeError(
_("You entered a number, but this type was expected:") + " " + TypeToString(expect),
location);
}
}
}
static Type StringToType(const gd::String &type);
static const gd::String &TypeToString(Type type);
static const gd::String unknownTypeString;
@@ -306,7 +326,8 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker {
static const gd::String identifierTypeString;
static const gd::String emptyTypeString;
std::vector<ExpressionParserDiagnostic*> errors;
std::vector<ExpressionParserDiagnostic*> fatalErrors;
std::vector<ExpressionParserDiagnostic*> allErrors;
std::vector<std::unique_ptr<ExpressionParserDiagnostic>> supplementalErrors;
Type childType;
Type parentType;

View File

@@ -82,7 +82,7 @@ bool UsedExtensionsFinder::DoVisitInstruction(gd::Instruction& instruction,
// Expressions scanner
// Ignore litterals nodes
// Ignore literals nodes
void UsedExtensionsFinder::OnVisitNumberNode(NumberNode& node){};
void UsedExtensionsFinder::OnVisitTextNode(TextNode& node){};

View File

@@ -144,7 +144,7 @@ void ExtensionsLoader::LoadExtension(const gd::String &fullpath,
bool forgiving) {
if (platform.GetExtensionCreateFunctionName().empty()) {
cout << "Unable to load extension " << fullpath << ":" << endl;
cout << "The plaftorm does not support extensions creation." << endl;
cout << "The platform does not support extensions creation." << endl;
return;
}

View File

@@ -35,7 +35,7 @@ class GD_CORE_API ExtensionsLoader {
* \param platform The platform the extensions belongs to.
* \param forgiving If set to true, files will try to be opened, but a failure
* when searching for the platform creation function symbol won't be logged as
* an error. (All other errors are still reparted as usual).
* an error. (All other errors are still reported as usual).
*
* \note Extensions files must have extensions *.xgd(w|l|m)(e),
* w for Windows, l for Linux, m for Mac, e for Edittime extensions.
@@ -51,7 +51,7 @@ class GD_CORE_API ExtensionsLoader {
* \param platform The platform the extension belongs to.
* \param forgiving If set to true, files will try to be opened, but a failure
* when searching for the platform creation function symbol won't be logged as
* an error. (All other errors are still reparted as usual).
* an error. (All other errors are still reported as usual).
*/
static void LoadExtension(const gd::String& fullpath,
gd::Platform& platform,

View File

@@ -45,6 +45,11 @@ void ArbitraryResourceWorker::ExposeTileset(gd::String& tilesetName){
// do.
};
void ArbitraryResourceWorker::ExposeModel3D(gd::String& resourceName){
// Nothing to do by default - each child class can define here the action to
// do.
};
void ArbitraryResourceWorker::ExposeVideo(gd::String& videoName){
// Nothing to do by default - each child class can define here the action to
// do.
@@ -66,7 +71,7 @@ void ArbitraryResourceWorker::ExposeAudio(gd::String& audioName) {
}
}
// For compatibility with older projects (where events were refering to files
// For compatibility with older projects (where events were referring to files
// directly), we consider that this resource name is a filename, and so expose
// it as a file.
ExposeFile(audioName);
@@ -83,7 +88,7 @@ void ArbitraryResourceWorker::ExposeFont(gd::String& fontName) {
}
}
// For compatibility with older projects (where events were refering to files
// For compatibility with older projects (where events were referring to files
// directly), we consider that this resource name is a filename, and so expose
// it as a file.
ExposeFile(fontName);
@@ -146,6 +151,8 @@ void ArbitraryResourceWorker::ExposeEmbeddeds(gd::String& resourceName) {
ExposeTileset(potentiallyUpdatedTargetResourceName);
} else if (targetResourceKind == "video") {
ExposeVideo(potentiallyUpdatedTargetResourceName);
} else if (targetResourceKind == "model3D") {
ExposeModel3D(potentiallyUpdatedTargetResourceName);
}
if (potentiallyUpdatedTargetResourceName != targetResourceName) {
@@ -233,6 +240,10 @@ class ResourceWorkerInEventsWorker : public ArbitraryEventsWorker {
gd::String updatedParameterValue = parameterValue;
worker.ExposeTileset(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
} else if (parameterMetadata.GetType() == "model3DResource") {
gd::String updatedParameterValue = parameterValue;
worker.ExposeModel3D(updatedParameterValue);
instruction.SetParameter(parameterIndex, updatedParameterValue);
}
});

View File

@@ -85,6 +85,11 @@ class GD_CORE_API ArbitraryResourceWorker {
*/
virtual void ExposeTileset(gd::String &tilesetName);
/**
* \brief Expose a 3D model, which is always a reference to a "model3D" resource.
*/
virtual void ExposeModel3D(gd::String &resourceName);
/**
* \brief Expose a video, which is always a reference to a "video" resource.
*/

View File

@@ -29,7 +29,7 @@ bool ProjectResourcesCopier::CopyAllResourcesTo(
originalProject.ExposeResources(absolutePathChecker);
auto projectDirectory = fs.DirNameFrom(originalProject.GetProjectFile());
std::cout << "Copying all ressources from " << projectDirectory << " to "
std::cout << "Copying all resources from " << projectDirectory << " to "
<< destinationDirectory << "..." << std::endl;
// Get the resources to be copied

View File

@@ -44,6 +44,7 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
std::set<gd::String>& GetAllTilesets() { return GetAll("tileset"); };
std::set<gd::String>& GetAllVideos() { return GetAll("video"); };
std::set<gd::String>& GetAllBitmapFonts() { return GetAll("bitmapFont"); };
std::set<gd::String>& GetAll3DModels() { return GetAll("model3D"); };
std::set<gd::String>& GetAll(const gd::String& resourceType) {
if (resourceType == "image") return allImages;
if (resourceType == "audio") return allAudios;
@@ -53,6 +54,7 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
if (resourceType == "tileset") return allTilesets;
if (resourceType == "video") return allVideos;
if (resourceType == "bitmapFont") return allBitmapFonts;
if (resourceType == "model3D") return allModel3Ds;
return emptyResources;
};
@@ -84,6 +86,9 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
virtual void ExposeBitmapFont(gd::String& bitmapFontResourceName) override {
allBitmapFonts.insert(bitmapFontResourceName);
};
virtual void ExposeModel3D(gd::String& resourceName) override {
allModel3Ds.insert(resourceName);
};
protected:
std::set<gd::String> allImages;
@@ -94,6 +99,7 @@ class ResourcesInUseHelper : public gd::ArbitraryResourceWorker {
std::set<gd::String> allTilesets;
std::set<gd::String> allVideos;
std::set<gd::String> allBitmapFonts;
std::set<gd::String> allModel3Ds;
std::set<gd::String> emptyResources;
};

View File

@@ -59,6 +59,9 @@ class ResourcesRenamer : public gd::ArbitraryResourceWorker {
virtual void ExposeBitmapFont(gd::String& bitmapFontName) override {
RenameIfNeeded(bitmapFontName);
};
virtual void ExposeModel3D(gd::String& resourceName) override {
RenameIfNeeded(resourceName);
};
private:
void RenameIfNeeded(gd::String& resourceName) {

View File

@@ -845,9 +845,21 @@ void WholeProjectRefactorer::AddBehaviorAndRequiredBehaviors(
return;
};
AddRequiredBehaviorsFor(project, object, behaviorName);
}
void WholeProjectRefactorer::AddRequiredBehaviorsFor(
gd::Project& project,
gd::Object& object,
const gd::String& behaviorName) {
if (!object.HasBehaviorNamed(behaviorName)) {
return;
};
gd::Behavior& behavior = object.GetBehavior(behaviorName);
const gd::Platform& platform = project.GetCurrentPlatform();
const gd::BehaviorMetadata& behaviorMetadata =
MetadataProvider::GetBehaviorMetadata(platform, behaviorType);
MetadataProvider::GetBehaviorMetadata(platform, behavior.GetTypeName());
if (MetadataProvider::IsBadBehaviorMetadata(behaviorMetadata)) {
// Should not happen because the behavior was added successfully (so its
// metadata are valid) - but double check anyway and bail out if the
@@ -855,7 +867,6 @@ void WholeProjectRefactorer::AddBehaviorAndRequiredBehaviors(
return;
}
gd::Behavior& behavior = object.GetBehavior(behaviorName);
for (auto const& keyValue : behavior.GetProperties()) {
const gd::String& propertyName = keyValue.first;
const gd::PropertyDescriptor& property = keyValue.second;
@@ -1382,25 +1393,12 @@ void WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
// Remove object in external events
if (removeEventsAndGroups) {
DependenciesAnalyzer analyzer(project, layout);
if (analyzer.Analyze()) {
for (auto& externalEventsName :
analyzer.GetExternalEventsDependencies()) {
auto& externalEvents = project.GetExternalEvents(externalEventsName);
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
project,
layout,
externalEvents.GetEvents(),
objectName);
}
for (auto& layoutName : analyzer.GetScenesDependencies()) {
auto& layout = project.GetLayout(layoutName);
gd::EventsRefactorer::RemoveObjectInEvents(project.GetCurrentPlatform(),
project,
layout,
layout.GetEvents(),
objectName);
}
for (auto &externalEventsName :
GetAssociatedExternalEvents(project, layout.GetName())) {
auto &externalEvents = project.GetExternalEvents(externalEventsName);
gd::EventsRefactorer::RemoveObjectInEvents(
project.GetCurrentPlatform(), project, layout,
externalEvents.GetEvents(), objectName);
}
}
@@ -1439,26 +1437,12 @@ void WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
}
// Rename object in external events
DependenciesAnalyzer analyzer(project, layout);
if (analyzer.Analyze()) {
for (auto& externalEventsName : analyzer.GetExternalEventsDependencies()) {
auto& externalEvents = project.GetExternalEvents(externalEventsName);
gd::EventsRefactorer::RenameObjectInEvents(project.GetCurrentPlatform(),
project,
layout,
externalEvents.GetEvents(),
oldName,
newName);
}
for (auto& layoutName : analyzer.GetScenesDependencies()) {
auto& layout = project.GetLayout(layoutName);
gd::EventsRefactorer::RenameObjectInEvents(project.GetCurrentPlatform(),
project,
layout,
layout.GetEvents(),
oldName,
newName);
}
for (auto &externalEventsName :
GetAssociatedExternalEvents(project, layout.GetName())) {
auto &externalEvents = project.GetExternalEvents(externalEventsName);
gd::EventsRefactorer::RenameObjectInEvents(
project.GetCurrentPlatform(), project, layout,
externalEvents.GetEvents(), oldName, newName);
}
// Rename object in external layouts

View File

@@ -212,6 +212,13 @@ class GD_CORE_API WholeProjectRefactorer {
gd::Object& object,
const gd::String& behaviorType,
const gd::String& behaviorName);
/**
* \brief Add required behaviors if necessary to fill every behavior
* properties of the given behaviors.
*/
static void AddRequiredBehaviorsFor(gd::Project& project,
gd::Object& object,
const gd::String& behaviorName);
/**
* \brief Find every behavior of the object that needs the given behaviors
@@ -325,7 +332,7 @@ class GD_CORE_API WholeProjectRefactorer {
* \brief Refactor the project after an object is renamed in a layout
*
* This will update the layout, all external layouts associated with it
* and all external events used by the layout.
* and all external events associated with it.
*/
static void ObjectOrGroupRenamedInLayout(gd::Project& project,
gd::Layout& layout,
@@ -337,7 +344,7 @@ class GD_CORE_API WholeProjectRefactorer {
* \brief Refactor the project after an object is removed in a layout
*
* This will update the layout, all external layouts associated with it
* and all external events used by the layout.
* and all external events associated with it.
*/
static void ObjectOrGroupRemovedInLayout(gd::Project& project,
gd::Layout& layout,

View File

@@ -64,7 +64,7 @@ class GD_CORE_API EffectsContainer {
std::size_t GetEffectPosition(const gd::String& name) const;
/**
* Return the number of effecst.
* Return the number of effects.
*/
std::size_t GetEffectsCount() const;

View File

@@ -217,7 +217,7 @@ class GD_CORE_API EventsFunction {
bool IsAsync() const { return isAsync; }
/**
* \brief Sets the asycronity of the function.
* \brief Sets the asynchronicity of the function.
*/
EventsFunction& SetAsync(bool _isAsync) {
isAsync = _isAsync;

View File

@@ -193,7 +193,6 @@ class GD_CORE_API InitialInstance {
* \see gd::Object
*/
///@{
#if defined(GD_IDE_ONLY)
/**
* \brief Return a map containing the properties names (as keys) and their
* values.
@@ -213,7 +212,6 @@ class GD_CORE_API InitialInstance {
const gd::String& value,
gd::Project& project,
gd::Layout& layout);
#endif
/**
* \brief Get the value of a double property stored in the instance.

View File

@@ -14,7 +14,14 @@ namespace gd {
Camera Layer::badCamera;
Layer::Layer()
: isVisible(true), isLightingLayer(false), followBaseLayerCamera(false) {}
: renderingType(""),
isVisible(true),
isLocked(false),
isLightingLayer(false),
followBaseLayerCamera(false),
camera3DNearPlaneDistance(0.1),
camera3DFarPlaneDistance(10000),
camera3DFieldOfView(45) {}
/**
* Change cameras count, automatically adding/removing them.
@@ -26,15 +33,21 @@ void Layer::SetCameraCount(std::size_t n) {
cameras.erase(cameras.begin() + cameras.size() - 1);
}
#if defined(GD_IDE_ONLY)
void Layer::SerializeTo(SerializerElement& element) const {
element.SetAttribute("name", GetName());
element.SetAttribute("renderingType", GetRenderingType());
element.SetAttribute("visibility", GetVisibility());
element.SetAttribute("isLocked", IsLocked());
element.SetAttribute("isLightingLayer", IsLightingLayer());
element.SetAttribute("followBaseLayerCamera", IsFollowingBaseLayerCamera());
element.SetAttribute("ambientLightColorR", (int)GetAmbientLightColorRed());
element.SetAttribute("ambientLightColorG", (int)GetAmbientLightColorGreen());
element.SetAttribute("ambientLightColorB", (int)GetAmbientLightColorBlue());
element.SetAttribute("camera3DNearPlaneDistance",
GetCamera3DNearPlaneDistance());
element.SetAttribute("camera3DFarPlaneDistance",
GetCamera3DFarPlaneDistance());
element.SetAttribute("camera3DFieldOfView", GetCamera3DFieldOfView());
SerializerElement& camerasElement = element.AddChild("cameras");
camerasElement.ConsiderAsArrayOf("camera");
@@ -55,20 +68,27 @@ void Layer::SerializeTo(SerializerElement& element) const {
SerializerElement& effectsElement = element.AddChild("effects");
effectsContainer.SerializeTo(effectsElement);
}
#endif
/**
* \brief Unserialize the layer.
*/
void Layer::UnserializeFrom(const SerializerElement& element) {
SetName(element.GetStringAttribute("name", "", "Name"));
SetRenderingType(element.GetStringAttribute("renderingType", ""));
SetVisibility(element.GetBoolAttribute("visibility", true, "Visibility"));
SetLocked(element.GetBoolAttribute("isLocked", false));
SetLightingLayer(element.GetBoolAttribute("isLightingLayer", false));
SetFollowBaseLayerCamera(
element.GetBoolAttribute("followBaseLayerCamera", false));
SetAmbientLightColor(element.GetIntAttribute("ambientLightColorR", 200),
element.GetIntAttribute("ambientLightColorG", 200),
element.GetIntAttribute("ambientLightColorB", 200));
SetCamera3DNearPlaneDistance(element.GetDoubleAttribute(
"camera3DNearPlaneDistance", 0.1, "threeDNearPlaneDistance"));
SetCamera3DFarPlaneDistance(element.GetDoubleAttribute(
"camera3DFarPlaneDistance", 10000, "threeDFarPlaneDistance"));
SetCamera3DFieldOfView(element.GetDoubleAttribute(
"camera3DFieldOfView", 45, "threeDFieldOfView"));
cameras.clear();
SerializerElement& camerasElement = element.GetChild("cameras");
@@ -80,24 +100,22 @@ void Layer::UnserializeFrom(const SerializerElement& element) {
camera.SetUseDefaultSize(
cameraElement.GetBoolAttribute("defaultSize", true));
camera.SetSize(cameraElement.GetDoubleAttribute("width"),
cameraElement.GetDoubleAttribute("height"));
cameraElement.GetDoubleAttribute("height"));
camera.SetUseDefaultViewport(
cameraElement.GetBoolAttribute("defaultViewport", true));
camera.SetViewport(
cameraElement.GetDoubleAttribute("viewportLeft"),
cameraElement.GetDoubleAttribute("viewportTop"),
cameraElement.GetDoubleAttribute("viewportRight"),
cameraElement.GetDoubleAttribute(
"viewportBottom"));
camera.SetViewport(cameraElement.GetDoubleAttribute("viewportLeft"),
cameraElement.GetDoubleAttribute("viewportTop"),
cameraElement.GetDoubleAttribute("viewportRight"),
cameraElement.GetDoubleAttribute("viewportBottom"));
cameras.push_back(camera);
}
if (camerasElement.GetChildrenCount() > 50) {
// Highly unlikely that we want as many cameras, as they were not even exposed in
// the editor nor used in the game engine. Must be because of a bug in the editor that
// duplicated cameras when cancelling changes on a layer.
// Reset to one camera.
// Highly unlikely that we want as many cameras, as they were not even
// exposed in the editor nor used in the game engine. Must be because of a
// bug in the editor that duplicated cameras when cancelling changes on a
// layer. Reset to one camera.
SetCameraCount(1);
}
@@ -105,9 +123,7 @@ void Layer::UnserializeFrom(const SerializerElement& element) {
effectsContainer.UnserializeFrom(effectsElement);
}
gd::EffectsContainer& Layer::GetEffects() {
return effectsContainer;
}
gd::EffectsContainer& Layer::GetEffects() { return effectsContainer; }
const gd::EffectsContainer& Layer::GetEffects() const {
return effectsContainer;

View File

@@ -47,6 +47,11 @@ class GD_CORE_API Layer {
*/
const gd::String& GetName() const { return name; }
const gd::String& GetRenderingType() const { return renderingType; }
void SetRenderingType(const gd::String& renderingType_) {
renderingType = renderingType_;
}
/**
* \brief Change if layer is displayed or not
*/
@@ -58,7 +63,17 @@ class GD_CORE_API Layer {
bool GetVisibility() const { return isVisible; }
/**
* \brief Set if the layer is a lightining layer or not.
* \brief Change if layer can be modified or not.
*/
void SetLocked(bool isLocked_) { isLocked = isLocked_; }
/**
* \brief Return true if layer can't be modified.
*/
bool IsLocked() const { return isLocked; }
/**
* \brief Set if the layer is a lighting layer or not.
*/
void SetLightingLayer(bool isLightingLayer_) {
isLightingLayer = isLightingLayer_;
@@ -81,6 +96,25 @@ class GD_CORE_API Layer {
*/
bool IsFollowingBaseLayerCamera() const { return followBaseLayerCamera; }
/** \name 3D
*/
///@{
double GetCamera3DNearPlaneDistance() const {
return camera3DNearPlaneDistance;
}
void SetCamera3DNearPlaneDistance(double distance) {
camera3DNearPlaneDistance = distance;
}
double GetCamera3DFarPlaneDistance() const {
return camera3DFarPlaneDistance;
}
void SetCamera3DFarPlaneDistance(double distance) {
camera3DFarPlaneDistance = distance;
}
double GetCamera3DFieldOfView() const { return camera3DFieldOfView; }
void SetCamera3DFieldOfView(double angle) { camera3DFieldOfView = angle; }
///@}
/** \name Cameras
*/
///@{
@@ -164,12 +198,10 @@ class GD_CORE_API Layer {
const EffectsContainer& GetEffects() const;
///@}
#if defined(GD_IDE_ONLY)
/**
* \brief Serialize layer.
*/
void SerializeTo(SerializerElement& element) const;
#endif
/**
* \brief Unserialize the layer.
@@ -177,16 +209,22 @@ class GD_CORE_API Layer {
void UnserializeFrom(const SerializerElement& element);
private:
gd::String name; ///< The name of the layer
bool isVisible; ///< True if the layer is visible
gd::String name; ///< The name of the layer
gd::String renderingType; ///< The rendering type: "" (empty), "2d", "3d" or
///< "2d+3d".
bool isVisible; ///< True if the layer is visible
bool isLocked; ///< True if the layer is locked
bool isLightingLayer; ///< True if the layer is used to display lights and
///< renders an ambient light.
bool followBaseLayerCamera; ///< True if the layer automatically follows the
///< base layer
unsigned int ambientLightColorR; ///< Ambient light color Red component
unsigned int ambientLightColorG; ///< Ambient light color Green component
unsigned int ambientLightColorB; ///< Ambient light color Blue component
std::vector<gd::Camera> cameras; ///< The camera displayed by the layer
double camera3DNearPlaneDistance; ///< 3D camera frustum near plan distance
double camera3DFarPlaneDistance; ///< 3D camera frustum far plan distance
double camera3DFieldOfView; ///< 3D camera field of view (fov) in degrees
unsigned int ambientLightColorR; ///< Ambient light color Red component
unsigned int ambientLightColorG; ///< Ambient light color Green component
unsigned int ambientLightColorB; ///< Ambient light color Blue component
std::vector<gd::Camera> cameras; ///< The camera displayed by the layer
gd::EffectsContainer effectsContainer; ///< The effects applied to the layer.
static gd::Camera badCamera;

View File

@@ -507,6 +507,44 @@ gd::String GD_CORE_API GetTypeOfObject(const gd::ObjectsContainer& project,
return type;
}
bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
const gd::ObjectsContainer &layout,
const gd::String &objectOrGroupName,
const gd::String &behaviorName,
bool searchInGroups) {
// Search in objects
if (layout.HasObjectNamed(objectOrGroupName)) {
return layout.GetObject(objectOrGroupName).HasBehaviorNamed(behaviorName);
}
if (project.HasObjectNamed(objectOrGroupName)) {
return project.GetObject(objectOrGroupName).HasBehaviorNamed(behaviorName);
}
// Search in groups
const gd::ObjectsContainer *container;
if (layout.GetObjectGroups().Has(objectOrGroupName)) {
container = &layout;
} else if (project.GetObjectGroups().Has(objectOrGroupName)) {
container = &project;
} else {
return false;
}
const vector<gd::String> &groupsObjects =
container->GetObjectGroups().Get(objectOrGroupName).GetAllObjectsNames();
// Empty groups don't contain any behavior.
if (groupsObjects.empty()) {
return false;
}
// Check that all objects have the same type.
for (auto &&object : groupsObjects) {
if (!HasBehaviorInObjectOrGroup(project, layout, object, behaviorName,
false)) {
return false;
}
}
return true;
}
gd::String GD_CORE_API GetTypeOfBehavior(const gd::ObjectsContainer& project,
const gd::ObjectsContainer& layout,
gd::String name,

View File

@@ -442,7 +442,14 @@ gd::String GD_CORE_API GetTypeOfObject(const ObjectsContainer& game,
const ObjectsContainer& layout,
gd::String objectName,
bool searchInGroups = true);
/**
* \brief Check if an object or all object of a group has a behavior.
*/
bool GD_CORE_API HasBehaviorInObjectOrGroup(const gd::ObjectsContainer &project,
const gd::ObjectsContainer &layout,
const gd::String &objectOrGroupName,
const gd::String &behaviorName,
bool searchInGroups = true);
/**
* \brief Get a type from a behavior name
* @return Type of the behavior.

View File

@@ -91,6 +91,8 @@ std::shared_ptr<Resource> ResourcesManager::CreateResource(
return std::make_shared<TilesetResource>();
else if (kind == "bitmapFont")
return std::make_shared<BitmapFontResource>();
else if (kind == "model3D")
return std::make_shared<Model3DResource>();
std::cout << "Bad resource created (type: " << kind << ")" << std::endl;
return std::make_shared<Resource>();
@@ -736,6 +738,20 @@ void BitmapFontResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("file", GetFile());
}
void Model3DResource::SetFile(const gd::String& newFile) {
file = NormalizePathSeparator(newFile);
}
void Model3DResource::UnserializeFrom(const SerializerElement& element) {
SetUserAdded(element.GetBoolAttribute("userAdded"));
SetFile(element.GetStringAttribute("file"));
}
void Model3DResource::SerializeTo(SerializerElement& element) const {
element.SetAttribute("userAdded", IsUserAdded());
element.SetAttribute("file", GetFile());
}
ResourceFolder::ResourceFolder(const ResourceFolder& other) { Init(other); }
ResourceFolder& ResourceFolder::operator=(const ResourceFolder& other) {

View File

@@ -142,7 +142,7 @@ class GD_CORE_API Resource {
virtual void SerializeTo(SerializerElement& element) const {};
/**
* \brief Unserialize the objectt.
* \brief Unserialize the object.
*/
virtual void UnserializeFrom(const SerializerElement& element){};
@@ -195,7 +195,7 @@ class GD_CORE_API ImageResource : public Resource {
void SerializeTo(SerializerElement& element) const override;
/**
* \brief Unserialize the objectt.
* \brief Unserialize the object.
*/
void UnserializeFrom(const SerializerElement& element) override;
@@ -481,6 +481,32 @@ class GD_CORE_API BitmapFontResource : public Resource {
gd::String file;
};
/**
* \brief Describe a 3D model file used by a project.
*
* \see Resource
* \ingroup ResourcesManagement
*/
class GD_CORE_API Model3DResource : public Resource {
public:
Model3DResource() : Resource() { SetKind("model3D"); };
virtual ~Model3DResource(){};
virtual Model3DResource* Clone() const override {
return new Model3DResource(*this);
}
virtual const gd::String& GetFile() const override { return file; };
virtual void SetFile(const gd::String& newFile) override;
virtual bool UseFile() const override { return true; }
void SerializeTo(SerializerElement& element) const override;
void UnserializeFrom(const SerializerElement& element) override;
private:
gd::String file;
};
/**
* \brief Inventory all resources used by a project
*
@@ -637,7 +663,7 @@ class GD_CORE_API ResourcesManager {
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the objectt.
* \brief Unserialize the object.
*/
void UnserializeFrom(const SerializerElement& element);
@@ -714,7 +740,7 @@ class GD_CORE_API ResourceFolder {
void SerializeTo(SerializerElement& element) const;
/**
* \brief Unserialize the objectt.
* \brief Unserialize the object.
*/
void UnserializeFrom(const SerializerElement& element,
gd::ResourcesManager& parentManager);

View File

@@ -2280,7 +2280,7 @@ public:
//!@name Handling parse errors
//!@{
//! Whether a parse error has occured in the last parsing.
//! Whether a parse error has occurred in the last parsing.
bool HasParseError() const { return parseResult_.IsError(); }
//! Get the \ref ParseErrorCode of last parsing.

View File

@@ -527,7 +527,7 @@ public:
return Parse<kParseDefaultFlags>(is, handler);
}
//! Whether a parse error has occured in the last parsing.
//! Whether a parse error has occurred in the last parsing.
bool HasParseError() const { return parseResult_.IsError(); }
//! Get the \ref ParseErrorCode of last parsing.

View File

@@ -584,32 +584,32 @@ public:
String substr( size_type start = 0, size_type length = npos ) const;
/**
* \return the position of the first occurence of **search** starting from **pos**.
* \return the position of the first occurrence of **search** starting from **pos**.
*/
size_type find( const String &search, size_type pos = 0 ) const;
/**
* \return the position of the first occurence of **search** starting from **pos**.
* \return the position of the first occurrence of **search** starting from **pos**.
*/
size_type find( const char *search, size_type pos = 0 ) const;
/**
* \return the position of the first occurence of **search** starting from **pos**.
* \return the position of the first occurrence of **search** starting from **pos**.
*/
size_type find( const value_type search, size_type pos = 0 ) const;
/**
* \return the position of the last occurence of **search** starting before **pos**.
* \return the position of the last occurrence of **search** starting before **pos**.
*/
size_type rfind( const String &search, size_type pos = npos ) const;
/**
* \return the position of the last occurence of **search** starting before **pos**.
* \return the position of the last occurrence of **search** starting before **pos**.
*/
size_type rfind( const char *search, size_type pos = npos ) const;
/**
* \return the position of the last occurence of **search** starting before **pos**.
* \return the position of the last occurrence of **search** starting before **pos**.
*/
size_type rfind( const value_type &search, size_type pos = npos ) const;
@@ -658,7 +658,7 @@ public:
/**
* \brief Do a case-insensitive search
* \return the position of the first occurence of **search** starting from **pos**.
* \return the position of the first occurrence of **search** starting from **pos**.
*
* \note This method isn't very efficient as it is linear on the string size times the
* search string size

View File

@@ -577,7 +577,7 @@ public:
#endif
/** Add a new node related to this. Adds a child past the LastChild.
Returns a pointer to the new object or NULL if an error occured.
Returns a pointer to the new object or NULL if an error occurred.
*/
TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
@@ -594,17 +594,17 @@ public:
TiXmlNode* LinkEndChild( TiXmlNode* addThis );
/** Add a new node related to this. Adds a child before the specified child.
Returns a pointer to the new object or NULL if an error occured.
Returns a pointer to the new object or NULL if an error occurred.
*/
TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
/** Add a new node related to this. Adds a child after the specified child.
Returns a pointer to the new object or NULL if an error occured.
Returns a pointer to the new object or NULL if an error occurred.
*/
TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis );
/** Replace a child of this node.
Returns a pointer to the new object or NULL if an error occured.
Returns a pointer to the new object or NULL if an error occurred.
*/
TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
@@ -1471,7 +1471,7 @@ public:
@sa SetTabSize, Row, Column
*/
int ErrorRow() const { return errorLocation.row+1; }
int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow()
int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occurred. See ErrorRow()
/** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol())
to report the correct values for row and column. It does not change the output

View File

@@ -99,7 +99,7 @@ Everything in GDevelop.js is meant to create a "bridge" allowing us to run and u
### I want all the gory details about GDevelop.js 🧐
All the required C++ files are imported into this huge list: https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Wrapper.cpp#L1-L79. C++ compilation is done with a "build system" called CMake, which is using [this file](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/CMakeLists.txt#L82-L101) to see what to compile.
All the required C++ files are imported into this huge list: <https://github.com/4ian/GDevelop/blob/master/GDevelop.js/Bindings/Wrapper.cpp#L1-L79>. C++ compilation is done with a "build system" called CMake, which is using [this file](https://github.com/4ian/GDevelop/blob/master/GDevelop.js/CMakeLists.txt#L82-L101) to see what to compile.
> In an ideal world, there would be something to do this automatically, so the GDevelop.js folder would not even exist 😉
> If you're interested and want to know more about GDevelop.js bridging between C++ and JavaScript, look at [this talk from GDevelop original author](https://www.youtube.com/watch?v=6La7jSCnYyk).

View File

@@ -12,16 +12,16 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
External libraries used by GDevelop Core

View File

@@ -6,7 +6,7 @@ GDevelop Core is a portable C++ library, compiled to be used in JavaScript in th
## 1) Getting started 🤓
First, take a look at the _Readme.md_ at the root of the repository and the [developer documentation](https://docs.gdevelop.io/).
First, take a look at the _[README.md](../README.md)_ at the root of the repository and the [developer documentation](https://docs.gdevelop.io/).
## 2) How to contribute 😎
@@ -21,5 +21,5 @@ or any pull request so as to add a nice feature, do not hesitate to get in touch
## License
GDevelop Core is distributed under the MIT license: see license.txt for
GDevelop Core is distributed under the MIT license: see [LICENSE.md](LICENSE.md) for
more information.

View File

@@ -124,6 +124,14 @@ void SetupProjectWithDummyPlatform(gd::Project& project,
.AddParameter("expression", _("Number parameter"))
.SetRequiresBaseObjectCapability("effect")
.SetFunctionName("getSomethingRequiringEffectCapability");
baseObject
.AddExpression("GetFromBaseExpression",
"This works on any object.",
"",
"",
"")
.AddParameter("object", _("Object"), "")
.SetFunctionName("getFromBaseExpression");
// Create an extension with various stuff inside.
std::shared_ptr<gd::PlatformExtension> extension =

View File

@@ -197,14 +197,14 @@ TEST_CASE("ExpressionNodeLocationFinder", "[common][events]") {
}
}
SECTION("Numbers and texts mismatchs") {
SECTION("Numbers and texts mismatches") {
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(parser, "12+\"hello\"", 0) == true);
REQUIRE(CheckNodeAtLocationIs<gd::NumberNode>(parser, "12+\"hello\"", 1) == true);
REQUIRE(CheckNodeAtLocationIs<gd::OperatorNode>(parser, "12+\"hello\"", 2) == true);
REQUIRE(CheckNodeAtLocationIs<gd::TextNode>(parser, "12+\"hello\"", 3) == true);
}
SECTION("Numbers and texts mismatchs (parent node)") {
SECTION("Numbers and texts mismatches (parent node)") {
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "12+\"hello\"", 0) == true);
REQUIRE(CheckParentNodeAtLocationIs<gd::OperatorNode>(parser, "12+\"hello\"", 1) == true);
REQUIRE(CheckNoParentNodeAtLocation(parser, "12+\"hello\"", 2) == true);

File diff suppressed because it is too large Load Diff

View File

@@ -132,11 +132,11 @@ TEST_CASE("ExpressionParser2NodePrinter", "[common][events]") {
testPrinter("number", "123 !!! 456", "123 ! !! 456");
}
SECTION("Numbers and texts mismatchs") {
SECTION("Numbers and texts mismatches") {
testPrinter("number", "123 + \"hello world\"");
testPrinter("string", "\"hello world\" + 123");
}
SECTION("Numbers and texts mismatchs with parenthesis") {
SECTION("Numbers and texts mismatches with parenthesis") {
testPrinter("number", "((123)) + (\"hello world\")");
testPrinter("string", "((\"hello world\") + (123))");
}

View File

@@ -56,6 +56,12 @@ GetEventFirstActionFirstParameterString(const gd::BaseEvent &event) {
return actions.Get(0).GetParameter(0).GetPlainString();
}
bool
AreActionsEmpty(const gd::BaseEvent &event) {
auto &actions = EnsureStandardEvent(event).GetActions();
return actions.IsEmpty();
}
const gd::String &GetEventFirstConditionType(const gd::BaseEvent &event) {
auto &conditions = EnsureStandardEvent(event).GetConditions();
REQUIRE(conditions.IsEmpty() == false);
@@ -78,6 +84,8 @@ enum TestEvent {
FreeActionWithOperator,
FreeFunctionWithObjects,
FreeFunctionWithObjectExpression,
FreeFunctionWithGroup,
FreeFunctionWithObjectExpressionOnGroup,
BehaviorAction,
BehaviorPropertyAction,
@@ -107,11 +115,18 @@ enum TestEvent {
ObjectActionWithOperator,
};
const std::vector<const gd::EventsList *> GetEventsLists(gd::Project &project) {
const std::vector<const gd::EventsList *> GetEventsListsAssociatedToScene(gd::Project &project) {
std::vector<const gd::EventsList *> eventLists;
auto &scene = project.GetLayout("Scene").GetEvents();
auto &externalEvents =
project.GetExternalEvents("ExternalEvents").GetEvents();
eventLists.push_back(&scene);
eventLists.push_back(&externalEvents);
return eventLists;
}
const std::vector<const gd::EventsList *> GetEventsListsNotAssociatedToScene(gd::Project &project) {
std::vector<const gd::EventsList *> eventLists;
auto &eventsExtension = project.GetEventsFunctionsExtension("MyEventsExtension");
auto &objectFunctionEvents =
eventsExtension
@@ -128,14 +143,24 @@ const std::vector<const gd::EventsList *> GetEventsLists(gd::Project &project) {
.GetEvents();
auto &freeFunctionEvents =
eventsExtension.GetEventsFunction("MyOtherEventsFunction").GetEvents();
eventLists.push_back(&scene);
eventLists.push_back(&externalEvents);
eventLists.push_back(&objectFunctionEvents);
eventLists.push_back(&behaviorFunctionEvents);
eventLists.push_back(&freeFunctionEvents);
return eventLists;
}
const std::vector<const gd::EventsList *> GetEventsLists(gd::Project &project) {
std::vector<const gd::EventsList *> eventLists;
for (auto *eventsList : GetEventsListsAssociatedToScene(project)) {
eventLists.push_back(eventsList);
}
for (auto *eventsList : GetEventsListsNotAssociatedToScene(project)) {
eventLists.push_back(eventsList);
}
return eventLists;
}
const void SetupEvents(gd::EventsList &eventList) {
// Add some free functions usages in events
@@ -285,6 +310,38 @@ const void SetupEvents(gd::EventsList &eventList) {
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeFunctionWithGroup) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to a group.
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyExtension::DoSomethingWithObjects");
action.SetParametersCount(2);
action.SetParameter(0, gd::Expression("GroupWithMyBehavior"));
action.SetParameter(1, gd::Expression("MyCustomObject"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
if (eventList.GetEventsCount() != FreeFunctionWithObjectExpressionOnGroup) {
throw std::logic_error("Invalid events setup: " + std::to_string(eventList.GetEventsCount()));
}
// Create an event referring to a group in an expression.
{
gd::StandardEvent event;
gd::Instruction action;
action.SetType("MyExtension::DoSomething");
action.SetParametersCount(1);
action.SetParameter(
0,
gd::Expression(
"GroupWithMyBehavior.GetObjectNumber()"));
event.GetActions().Insert(action);
eventList.InsertEvent(event);
}
}
// Add some events based behavior usages in events
@@ -821,7 +878,7 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
behaviorAction.GetParameters().push_back(
gd::ParameterMetadata()
.SetName("OtherObject")
.SetName("ObjectWithMyBehavior")
.SetType("object")
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
behaviorAction.GetParameters().push_back(
@@ -829,6 +886,8 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
.SetName("OtherBehavior")
.SetType("behavior")
.SetExtraInfo("MyEventsExtension::MyEventsBasedBehavior"));
auto &group = behaviorAction.GetObjectGroups().InsertNew("GroupWithMyBehavior");
group.AddObject("ObjectWithMyBehavior");
auto &behaviorExpression =
behaviorEventsFunctions
@@ -1009,6 +1068,8 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
auto &childObject = eventsBasedObject.InsertNewObject(
project, "MyExtension::Sprite", "ObjectWithMyBehavior", 0);
childObject.AddNewBehavior(project, "MyEventsExtension::MyEventsBasedBehavior", "MyBehavior");
auto &group = eventsBasedObject.GetObjectGroups().InsertNew("GroupWithMyBehavior");
group.AddObject(childObject.GetName());
eventsBasedObject.InsertNewObject(
project, "MyEventsExtension::MyEventsBasedObject", "MyCustomObject", 1);
@@ -1079,6 +1140,8 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
.SetName("MyCustomObject")
.SetType("object")
.SetExtraInfo("MyEventsExtension::MyEventsBasedObject"));
auto &group = action.GetObjectGroups().InsertNew("GroupWithMyBehavior");
group.AddObject("ObjectWithMyBehavior");
}
// Add some usages in events
@@ -1091,6 +1154,8 @@ SetupProjectWithEventsFunctionExtension(gd::Project &project) {
auto &object = layout.InsertNewObject(project, "MyExtension::Sprite",
"ObjectWithMyBehavior", 0);
object.AddNewBehavior(project, "MyEventsExtension::MyEventsBasedBehavior", "MyBehavior");
auto &group = layout.GetObjectGroups().InsertNew("GroupWithMyBehavior", 0);
group.AddObject("ObjectWithMyBehavior");
auto &globalObject = project.InsertNewObject(
project, "MyExtension::Sprite", "GlobalObjectWithMyBehavior", 0);
@@ -1229,6 +1294,29 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(externalLayout2.GetInitialInstances().HasInstancesOfObject(
"GlobalObject1") == false);
}
SECTION("Events") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &layout = project.GetLayout("Scene");
// Trigger the refactoring after removing an object
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
project, layout, "ObjectWithMyBehavior", /* isObjectGroup=*/false);
for (auto *eventsList : GetEventsListsAssociatedToScene(project)) {
// Check actions with the object in parameters have been removed.
REQUIRE(
AreActionsEmpty(eventsList->GetEvent(FreeFunctionWithObjects)));
// Check actions with the object in expressions have been removed.
REQUIRE(AreActionsEmpty(
eventsList->GetEvent(FreeFunctionWithObjectExpression)));
}
}
}
SECTION("Object renamed (in layout)") {
@@ -1347,7 +1435,7 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
REQUIRE(externalLayout2.GetInitialInstances().HasInstancesOfObject(
"GlobalObject3") == true);
}
SECTION("Events") {
gd::Project project;
gd::Platform platform;
@@ -1358,57 +1446,145 @@ TEST_CASE("WholeProjectRefactorer", "[common]") {
// Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
project, layout, "ObjectWithMyBehavior", "RenamedObjectWithMyBehavior",
project, layout, "ObjectWithMyBehavior",
"RenamedObjectWithMyBehavior",
/* isObjectGroup=*/false);
// Check object name has been renamed in action parameters.
REQUIRE(GetEventFirstActionFirstParameterString(
layout.GetEvents().GetEvent(FreeFunctionWithObjects)) ==
"RenamedObjectWithMyBehavior");
for (auto *eventsList : GetEventsListsAssociatedToScene(project)) {
// Check object name has been renamed in action parameters.
REQUIRE(GetEventFirstActionFirstParameterString(eventsList->GetEvent(
FreeFunctionWithObjects)) == "RenamedObjectWithMyBehavior");
// Check object name has been renamed in expressions.
REQUIRE(GetEventFirstActionFirstParameterString(
layout.GetEvents().GetEvent(FreeFunctionWithObjectExpression)) ==
// Check object name has been renamed in expressions.
REQUIRE(GetEventFirstActionFirstParameterString(
eventsList->GetEvent(FreeFunctionWithObjectExpression)) ==
"RenamedObjectWithMyBehavior.GetObjectNumber()");
}
}
}
SECTION("Group deleted (in layout)") {
SECTION("Events") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &layout = project.GetLayout("Scene");
// Trigger the refactoring after removing a group
gd::WholeProjectRefactorer::ObjectOrGroupRemovedInLayout(
project, layout, "GroupWithMyBehavior", /* isObjectGroup=*/true);
for (auto *eventsList : GetEventsListsAssociatedToScene(project)) {
// Check actions with the group in parameters have been removed.
REQUIRE(AreActionsEmpty(eventsList->GetEvent(FreeFunctionWithGroup)));
// Check actions with the group in expressions have been removed.
REQUIRE(AreActionsEmpty(
eventsList->GetEvent(FreeFunctionWithObjectExpressionOnGroup)));
}
}
}
SECTION("Group renamed (in layout)") {
SECTION("Events") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &layout = project.GetLayout("Scene");
// Trigger the refactoring after the renaming of a group
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInLayout(
project, layout, "GroupWithMyBehavior", "RenamedGroupWithMyBehavior",
/* isObjectGroup=*/true);
for (auto *eventsList : GetEventsListsAssociatedToScene(project)) {
// Check group name has been renamed in action parameters.
REQUIRE(GetEventFirstActionFirstParameterString(eventsList->GetEvent(
FreeFunctionWithGroup)) == "RenamedGroupWithMyBehavior");
// Check group name has been renamed in expressions.
REQUIRE(GetEventFirstActionFirstParameterString(eventsList->GetEvent(
FreeFunctionWithObjectExpressionOnGroup)) ==
"RenamedGroupWithMyBehavior.GetObjectNumber()");
}
}
}
SECTION("Object renamed (in events function)") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
SECTION("Group") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension =
project.InsertNewEventsFunctionsExtension("MyEventsExtension", 0);
// Add a (free) function with an object group
gd::EventsFunction &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyEventsFunction", 0);
gd::ObjectGroup &objectGroup =
eventsFunction.GetObjectGroups().InsertNew("MyGroup", 0);
objectGroup.AddObject("Object1");
objectGroup.AddObject("Object2");
// In theory, we would add the object parameters, but we're not testing
// events in this test.
// Add a (free) function with an object group
gd::EventsFunction &eventsFunction =
eventsExtension.InsertNewEventsFunction("MyEventsFunction", 0);
gd::ObjectGroup &objectGroup =
eventsFunction.GetObjectGroups().InsertNew("MyGroup", 0);
objectGroup.AddObject("Object1");
objectGroup.AddObject("Object2");
// In theory, we would add the object parameters, but we're not testing
// events in this test.
// Create the objects container for the events function
gd::ObjectsContainer globalObjectsContainer;
gd::ObjectsContainer objectsContainer;
gd::ParameterMetadataTools::ParametersToObjectsContainer(
project, eventsFunction.GetParameters(), objectsContainer);
// (this is strictly not necessary because we're not testing events in this
// test)
// Create the objects container for the events function
gd::ObjectsContainer globalObjectsContainer;
gd::ObjectsContainer objectsContainer;
gd::ParameterMetadataTools::ParametersToObjectsContainer(
project, eventsFunction.GetParameters(), objectsContainer);
// (this is strictly not necessary because we're not testing events in
// this test)
// Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project, eventsFunction, globalObjectsContainer, objectsContainer,
"Object1", "RenamedObject1",
/* isObjectGroup=*/false);
// Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project, eventsFunction, globalObjectsContainer, objectsContainer,
"Object1", "RenamedObject1",
/* isObjectGroup=*/false);
REQUIRE(objectGroup.Find("Object1") == false);
REQUIRE(objectGroup.Find("RenamedObject1") == true);
REQUIRE(objectGroup.Find("Object2") == true);
REQUIRE(objectGroup.Find("Object1") == false);
REQUIRE(objectGroup.Find("RenamedObject1") == true);
REQUIRE(objectGroup.Find("Object2") == true);
// Events are not tested
// Events are not tested
}
SECTION("Events") {
gd::Project project;
gd::Platform platform;
SetupProjectWithDummyPlatform(project, platform);
auto &eventsExtension = SetupProjectWithEventsFunctionExtension(project);
auto &eventsFunction =
eventsExtension.GetEventsFunction("MyOtherEventsFunction");
// Create the objects container for the events function
gd::ObjectsContainer globalObjectsContainer;
gd::ObjectsContainer objectsContainer;
gd::ParameterMetadataTools::ParametersToObjectsContainer(
project, eventsFunction.GetParameters(), objectsContainer);
// Trigger the refactoring after the renaming of an object
gd::WholeProjectRefactorer::ObjectOrGroupRenamedInEventsFunction(
project, eventsFunction, globalObjectsContainer, objectsContainer,
"ObjectWithMyBehavior", "RenamedObjectWithMyBehavior",
/* isObjectGroup=*/false);
// Check object name has been renamed in action parameters.
REQUIRE(
GetEventFirstActionFirstParameterString(
eventsFunction.GetEvents().GetEvent(FreeFunctionWithObjects)) ==
"RenamedObjectWithMyBehavior");
// Check object name has been renamed in expressions.
REQUIRE(GetEventFirstActionFirstParameterString(
eventsFunction.GetEvents().GetEvent(
FreeFunctionWithObjectExpression)) ==
"RenamedObjectWithMyBehavior.GetObjectNumber()");
}
}
SECTION("Object renamed (in events-based object)") {

View File

@@ -0,0 +1,474 @@
namespace gdjs {
export interface Object3DDataContent {
width: float;
height: float;
depth: float;
}
/** Base parameters for {@link gdjs.RuntimeObject3D} */
export interface Object3DData extends ObjectData {
/** The base parameters of the RuntimeObject3D */
content: Object3DDataContent;
}
const getValidDimensionValue = (value: float | undefined) =>
value === undefined ? 100 : value <= 0 ? 1 : value;
/**
* Base class for 3D objects.
*/
export abstract class RuntimeObject3D extends gdjs.RuntimeObject {
/**
* Position on the Z axis.
*/
private _z: float = 0;
/**
* `_width` takes this value when the scale equals 1.
*
* It can't be 0.
*/
private _originalWidth: float;
/**
* `_height` takes this value when the scale equals 1.
*
* It can't be 0.
*/
private _originalHeight: float;
/**
* `depth` takes this value when the scale equals 1.
*
* It can't be 0.
*/
private _originalDepth: float;
private _width: float;
private _height: float;
private _depth: float;
private _flippedX: boolean = false;
private _flippedY: boolean = false;
private _flippedZ: boolean = false;
/**
* Euler angle with the `ZYX` order.
*
* Note that `_rotationZ` is `angle` from `gdjs.RuntimeObject`.
*/
private _rotationX: float = 0;
/**
* Euler angle with the `ZYX` order.
*
* Note that `_rotationZ` is `angle` from `gdjs.RuntimeObject`.
*/
private _rotationY: float = 0;
private static _temporaryVector = new THREE.Vector3();
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
objectData: Object3DData
) {
super(instanceContainer, objectData);
// TODO Should 0 be replaced by 0.01 instead of using the default value?
this._width = this._originalWidth = getValidDimensionValue(
objectData.content.width
);
this._height = this._originalHeight = getValidDimensionValue(
objectData.content.height
);
this._depth = this._originalDepth = getValidDimensionValue(
objectData.content.depth
);
}
abstract getRenderer(): gdjs.RuntimeObject3DRenderer;
getRendererObject() {
return null;
}
get3DRendererObject() {
return this.getRenderer().get3DRendererObject();
}
updateFromObjectData(
oldObjectData: Object3DData,
newObjectData: Object3DData
): boolean {
// There is no need to check if they changed because events can't modify them.
this._setOriginalWidth(
getValidDimensionValue(newObjectData.content.width)
);
this._setOriginalHeight(
getValidDimensionValue(newObjectData.content.height)
);
this._setOriginalDepth(
getValidDimensionValue(newObjectData.content.depth)
);
return true;
}
extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {
if (initialInstanceData.customSize) {
this.setWidth(initialInstanceData.width);
this.setHeight(initialInstanceData.height);
}
initialInstanceData.numberProperties.forEach((property) => {
if (property.name === 'z') {
this.setZ(property.value);
} else if (property.name === 'depth') {
if (initialInstanceData.customSize) {
this.setDepth(property.value);
}
} else if (property.name === 'rotationX') {
this.setRotationX(property.value);
} else if (property.name === 'rotationY') {
this.setRotationY(property.value);
}
});
}
setX(x: float): void {
super.setX(x);
this.getRenderer().updatePosition();
}
setY(y: float): void {
super.setY(y);
this.getRenderer().updatePosition();
}
/**
* Set the object position on the Z axis.
*/
setZ(z: float): void {
if (z === this._z) return;
this._z = z;
this.getRenderer().updatePosition();
}
/**
* Get the object position on the Z axis.
*/
getZ(): float {
return this._z;
}
setAngle(angle: float): void {
super.setAngle(angle);
this.getRenderer().updateRotation();
}
/**
* Set the object rotation on the X axis.
*
* This is an Euler angle. Objects use the `ZYX` order.
*/
setRotationX(angle: float): void {
this._rotationX = angle;
this.getRenderer().updateRotation();
}
/**
* Set the object rotation on the Y axis.
*
* This is an Euler angle. Objects use the `ZYX` order.
*/
setRotationY(angle: float): void {
this._rotationY = angle;
this.getRenderer().updateRotation();
}
/**
* Get the object rotation on the X axis.
*
* This is an Euler angle. Objects use the `ZYX` order.
*/
getRotationX(): float {
return this._rotationX;
}
/**
* Get the object rotation on the Y axis.
*
* This is an Euler angle. Objects use the `ZYX` order.
*/
getRotationY(): float {
return this._rotationY;
}
/**
* Turn the object around the scene x axis at its center.
* @param deltaAngle the rotation angle
*/
turnAroundX(deltaAngle: float): void {
const axisX = gdjs.RuntimeObject3D._temporaryVector;
axisX.set(1, 0, 0);
const mesh = this.getRenderer().get3DRendererObject();
mesh.rotateOnWorldAxis(axisX, gdjs.toRad(deltaAngle));
this._rotationX = gdjs.toDegrees(mesh.rotation.x);
this._rotationY = gdjs.toDegrees(mesh.rotation.y);
this.setAngle(gdjs.toDegrees(mesh.rotation.z));
}
/**
* Turn the object around the scene y axis at its center.
* @param deltaAngle the rotation angle
*/
turnAroundY(deltaAngle: float): void {
const axisY = gdjs.RuntimeObject3D._temporaryVector;
axisY.set(0, 1, 0);
const mesh = this.getRenderer().get3DRendererObject();
mesh.rotateOnWorldAxis(axisY, gdjs.toRad(deltaAngle));
this._rotationX = gdjs.toDegrees(mesh.rotation.x);
this._rotationY = gdjs.toDegrees(mesh.rotation.y);
this.setAngle(gdjs.toDegrees(mesh.rotation.z));
}
/**
* Turn the object around the scene z axis at its center.
* @param deltaAngle the rotation angle
*/
turnAroundZ(deltaAngle: float): void {
const axisZ = gdjs.RuntimeObject3D._temporaryVector;
axisZ.set(0, 0, 1);
const mesh = this.getRenderer().get3DRendererObject();
mesh.rotateOnWorldAxis(axisZ, gdjs.toRad(deltaAngle));
this._rotationX = gdjs.toDegrees(mesh.rotation.x);
this._rotationY = gdjs.toDegrees(mesh.rotation.y);
this.setAngle(gdjs.toDegrees(mesh.rotation.z));
}
getWidth(): float {
return this._width;
}
getHeight(): float {
return this._height;
}
/**
* Get the object size on the Z axis (called "depth").
*/
getDepth(): float {
return this._depth;
}
setWidth(width: float): void {
if (this._width === width) return;
this._width = width;
this.getRenderer().updateSize();
this.invalidateHitboxes();
}
setHeight(height: float): void {
if (this._height === height) return;
this._height = height;
this.getRenderer().updateSize();
this.invalidateHitboxes();
}
/**
* Set the object size on the Z axis (called "depth").
*/
setDepth(depth: float): void {
if (this._depth === depth) return;
this._depth = depth;
this.getRenderer().updateSize();
}
/**
* Return the width of the object for a scale of 1.
*
* It can't be 0.
*/
_getOriginalWidth(): float {
return this._originalWidth;
}
/**
* Return the height of the object for a scale of 1.
*
* It can't be 0.
*/
_getOriginalHeight(): float {
return this._originalHeight;
}
/**
* Return the object size on the Z axis (called "depth") when the scale equals 1.
*/
_getOriginalDepth(): float {
return this._originalDepth;
}
/**
* Set the width of the object for a scale of 1.
*/
_setOriginalWidth(originalWidth: float): void {
if (originalWidth <= 0) {
originalWidth = 1;
}
const oldOriginalWidth = this._originalWidth;
this._originalWidth = originalWidth;
if (oldOriginalWidth === this._width) {
this.setWidth(originalWidth);
}
}
/**
* Set the height of the object for a scale of 1.
*/
_setOriginalHeight(originalHeight: float): void {
if (originalHeight <= 0) {
originalHeight = 1;
}
const oldOriginalHeight = this._originalHeight;
this._originalHeight = originalHeight;
if (oldOriginalHeight === this._height) {
this.setHeight(originalHeight);
}
}
/**
* Set the object size on the Z axis (called "depth") when the scale equals 1.
*/
_setOriginalDepth(originalDepth: float): void {
if (originalDepth <= 0) {
originalDepth = 1;
}
const oldOriginalDepth = this._originalDepth;
this._originalDepth = originalDepth;
if (oldOriginalDepth === this._depth) {
this.setDepth(originalDepth);
}
}
/**
* Change the scale on X, Y and Z axis of the object.
*
* @param newScale The new scale (must be greater than 0).
*/
setScale(newScale: number): void {
this.setScaleX(newScale);
this.setScaleY(newScale);
this.setScaleZ(newScale);
}
/**
* Change the scale on X axis of the object (changing its width).
*
* @param newScale The new scale (must be greater than 0).
*/
setScaleX(newScale: number): void {
if (newScale < 0) {
newScale = 0;
}
this.setWidth(this._originalWidth * newScale);
}
/**
* Change the scale on Y axis of the object (changing its height).
*
* @param newScale The new scale (must be greater than 0).
*/
setScaleY(newScale: number): void {
if (newScale < 0) {
newScale = 0;
}
this.setHeight(this._originalHeight * newScale);
}
/**
* Change the scale on Z axis of the object (changing its height).
*
* @param newScale The new scale (must be greater than 0).
*/
setScaleZ(newScale: number): void {
if (newScale < 0) {
newScale = 0;
}
this.setDepth(this._originalDepth * newScale);
}
/**
* Get the scale of the object (or the geometric average of X, Y and Z scale in case they are different).
*
* @return the scale of the object (or the geometric average of X, Y and Z scale in case they are different).
*/
getScale(): number {
const scaleX = this.getScaleX();
const scaleY = this.getScaleY();
const scaleZ = this.getScaleZ();
return scaleX === scaleY && scaleX === scaleZ
? scaleX
: Math.pow(scaleX * scaleY * scaleZ, 1 / 3);
}
/**
* Get the scale of the object on X axis.
*
* @return the scale of the object on X axis
*/
getScaleX(): float {
return Math.abs(this._width / this._originalWidth);
}
/**
* Get the scale of the object on Y axis.
*
* @return the scale of the object on Y axis
*/
getScaleY(): float {
return Math.abs(this._height / this._originalHeight);
}
/**
* Get the scale of the object on Z axis.
*
* @return the scale of the object on Z axis
*/
getScaleZ(): float {
return Math.abs(this._depth / this._originalDepth);
}
flipX(enable: boolean) {
if (enable !== this._flippedX) {
this._flippedX = enable;
this.getRenderer().updateSize();
}
}
flipY(enable: boolean) {
if (enable !== this._flippedY) {
this._flippedY = enable;
this.getRenderer().updateSize();
}
}
flipZ(enable: boolean) {
if (enable !== this._flippedZ) {
this._flippedZ = enable;
this.getRenderer().updateSize();
}
}
isFlippedX(): boolean {
return this._flippedX;
}
isFlippedY(): boolean {
return this._flippedY;
}
isFlippedZ(): boolean {
return this._flippedZ;
}
hide(enable: boolean): void {
super.hide(enable);
this.getRenderer().updateVisibility();
}
}
}

View File

@@ -0,0 +1,55 @@
namespace gdjs {
export abstract class RuntimeObject3DRenderer {
protected _object: gdjs.RuntimeObject3D;
private _threeObject3D: THREE.Object3D;
constructor(
runtimeObject: gdjs.RuntimeObject3D,
instanceContainer: gdjs.RuntimeInstanceContainer,
threeObject3D: THREE.Object3D
) {
this._object = runtimeObject;
this._threeObject3D = threeObject3D;
this._threeObject3D.rotation.order = 'ZYX';
instanceContainer
.getLayer('')
.getRenderer()
.add3DRendererObject(this._threeObject3D);
}
get3DRendererObject() {
return this._threeObject3D;
}
updatePosition() {
this._threeObject3D.position.set(
this._object.x + this._object.getWidth() / 2,
this._object.y + this._object.getHeight() / 2,
this._object.getZ() + this._object.getDepth() / 2
);
}
updateRotation() {
this._threeObject3D.rotation.set(
gdjs.toRad(this._object.getRotationX()),
gdjs.toRad(this._object.getRotationY()),
gdjs.toRad(this._object.angle)
);
}
updateSize() {
const object = this._object;
this._threeObject3D.scale.set(
object.isFlippedX() ? -object.getWidth() : object.getWidth(),
object.isFlippedY() ? -object.getHeight() : object.getHeight(),
object.isFlippedZ() ? -object.getDepth() : object.getDepth()
);
this.updatePosition();
}
updateVisibility() {
this._threeObject3D.visible = !this._object.isHidden();
}
}
}

View File

@@ -0,0 +1,73 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::AmbientLight',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
makeFilter(
target: EffectsTarget,
effectData: EffectData
): gdjs.PixiFiltersTools.Filter {
return new (class implements gdjs.PixiFiltersTools.Filter {
light: THREE.AmbientLight;
_isEnabled: boolean;
constructor() {
this.light = new THREE.AmbientLight();
this._isEnabled = false;
}
isEnabled(target: EffectsTarget): boolean {
return this._isEnabled;
}
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
if (this._isEnabled === enabled) {
return true;
}
if (enabled) {
return this.applyEffect(target);
} else {
return this.removeEffect(target);
}
}
applyEffect(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
if (!scene) {
return false;
}
scene.add(this.light);
this._isEnabled = true;
return true;
}
removeEffect(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
if (!scene) {
return false;
}
scene.remove(this.light);
this._isEnabled = false;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'intensity') {
this.light.intensity = value;
}
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.light.color = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
);
}
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
})();
}
})()
);
}

View File

@@ -0,0 +1,440 @@
namespace gdjs {
/** Base parameters for {@link gdjs.Cube3DRuntimeObject} */
export interface Cube3DObjectData extends Object3DData {
/** The base parameters of the Cube3D object */
content: Object3DDataContent & {
enableTextureTransparency: boolean;
facesOrientation: 'Y' | 'Z';
frontFaceResourceName: string;
backFaceResourceName: string;
backFaceUpThroughWhichAxisRotation: 'X' | 'Y';
leftFaceResourceName: string;
rightFaceResourceName: string;
topFaceResourceName: string;
bottomFaceResourceName: string;
frontFaceResourceRepeat: boolean;
backFaceResourceRepeat: boolean;
leftFaceResourceRepeat: boolean;
rightFaceResourceRepeat: boolean;
topFaceResourceRepeat: boolean;
bottomFaceResourceRepeat: boolean;
frontFaceVisible: boolean;
backFaceVisible: boolean;
leftFaceVisible: boolean;
rightFaceVisible: boolean;
topFaceVisible: boolean;
bottomFaceVisible: boolean;
materialType: 'Basic' | 'StandardWithoutMetalness';
};
}
type FaceName = 'front' | 'back' | 'left' | 'right' | 'top' | 'bottom';
const faceNameToBitmaskIndex = {
front: 0,
back: 1,
left: 2,
right: 3,
top: 4,
bottom: 5,
};
/**
* Shows a 3D box object.
*/
export class Cube3DRuntimeObject extends gdjs.RuntimeObject3D {
private _renderer: Cube3DRuntimeObjectRenderer;
private _facesOrientation: 'Y' | 'Z';
private _backFaceUpThroughWhichAxisRotation: 'X' | 'Y';
private _shouldUseTransparentTexture: boolean;
// `_rotationZ` is `angle` from `gdjs.RuntimeObject`.
private _visibleFacesBitmask: integer;
private _textureRepeatFacesBitmask: integer;
private _faceResourceNames: [
string,
string,
string,
string,
string,
string
];
_materialType: gdjs.Cube3DRuntimeObject.MaterialType =
gdjs.Cube3DRuntimeObject.MaterialType.Basic;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
objectData: Cube3DObjectData
) {
super(instanceContainer, objectData);
this._shouldUseTransparentTexture =
objectData.content.enableTextureTransparency || false;
this._facesOrientation = objectData.content.facesOrientation || 'Y';
this._visibleFacesBitmask = 0;
if (objectData.content.frontFaceVisible)
this._visibleFacesBitmask |= 1 << faceNameToBitmaskIndex['front'];
if (objectData.content.backFaceVisible)
this._visibleFacesBitmask |= 1 << faceNameToBitmaskIndex['back'];
if (objectData.content.leftFaceVisible)
this._visibleFacesBitmask |= 1 << faceNameToBitmaskIndex['left'];
if (objectData.content.rightFaceVisible)
this._visibleFacesBitmask |= 1 << faceNameToBitmaskIndex['right'];
if (objectData.content.topFaceVisible)
this._visibleFacesBitmask |= 1 << faceNameToBitmaskIndex['top'];
if (objectData.content.bottomFaceVisible)
this._visibleFacesBitmask |= 1 << faceNameToBitmaskIndex['bottom'];
this._textureRepeatFacesBitmask = 0;
if (objectData.content.frontFaceResourceRepeat)
this._textureRepeatFacesBitmask |= 1 << faceNameToBitmaskIndex['front'];
if (objectData.content.backFaceResourceRepeat)
this._textureRepeatFacesBitmask |= 1 << faceNameToBitmaskIndex['back'];
if (objectData.content.leftFaceResourceRepeat)
this._textureRepeatFacesBitmask |= 1 << faceNameToBitmaskIndex['left'];
if (objectData.content.rightFaceResourceRepeat)
this._textureRepeatFacesBitmask |= 1 << faceNameToBitmaskIndex['right'];
if (objectData.content.topFaceResourceRepeat)
this._textureRepeatFacesBitmask |= 1 << faceNameToBitmaskIndex['top'];
if (objectData.content.bottomFaceResourceRepeat)
this._textureRepeatFacesBitmask |=
1 << faceNameToBitmaskIndex['bottom'];
this._backFaceUpThroughWhichAxisRotation =
objectData.content.backFaceUpThroughWhichAxisRotation || 'X';
this._faceResourceNames = [
objectData.content.frontFaceResourceName,
objectData.content.backFaceResourceName,
objectData.content.leftFaceResourceName,
objectData.content.rightFaceResourceName,
objectData.content.topFaceResourceName,
objectData.content.bottomFaceResourceName,
];
this._materialType = this._convertMaterialType(
objectData.content.materialType
);
this._renderer = new gdjs.Cube3DRuntimeObjectRenderer(
this,
instanceContainer
);
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
}
/**
* Sets the visibility of a face of the 3D box.
*
* @param faceName - The name of the face to set visibility for.
* @param value - The visibility value to set.
*/
setFaceVisibility(faceName: FaceName, enable: boolean) {
const faceIndex = faceNameToBitmaskIndex[faceName];
if (faceIndex === undefined) {
return;
}
if (enable === this.isFaceAtIndexVisible(faceIndex)) {
return;
}
if (enable) {
this._visibleFacesBitmask |= 1 << faceIndex;
} else {
this._visibleFacesBitmask &= ~(1 << faceIndex);
}
this._renderer.updateFace(faceIndex);
}
/**
* Sets the texture repeat of a face of the 3D box.
*
* @param faceName - The name of the face to set visibility for.
* @param value - The visibility value to set.
*/
setRepeatTextureOnFace(faceName: FaceName, enable: boolean) {
const faceIndex = faceNameToBitmaskIndex[faceName];
if (faceIndex === undefined) {
return;
}
if (enable === this.shouldRepeatTextureOnFaceAtIndex(faceIndex)) {
return;
}
if (enable) {
this._textureRepeatFacesBitmask |= 1 << faceIndex;
} else {
this._textureRepeatFacesBitmask &= ~(1 << faceIndex);
}
this._renderer.updateFace(faceIndex);
}
isFaceVisible(faceName: FaceName): boolean {
const faceIndex = faceNameToBitmaskIndex[faceName];
if (faceIndex === undefined) {
return false;
}
return this.isFaceAtIndexVisible(faceIndex);
}
/** @internal */
isFaceAtIndexVisible(faceIndex): boolean {
return (this._visibleFacesBitmask & (1 << faceIndex)) !== 0;
}
/** @internal */
shouldRepeatTextureOnFaceAtIndex(faceIndex): boolean {
return (this._textureRepeatFacesBitmask & (1 << faceIndex)) !== 0;
}
setFaceResourceName(faceName: FaceName, resourceName: string): void {
const faceIndex = faceNameToBitmaskIndex[faceName];
if (faceIndex === undefined) {
return;
}
if (this._faceResourceNames[faceIndex] === resourceName) {
return;
}
this._faceResourceNames[faceIndex] = resourceName;
this._renderer.updateFace(faceIndex);
}
/** @internal */
getFaceAtIndexResourceName(faceIndex: integer): string {
return this._faceResourceNames[faceIndex];
}
getRenderer(): gdjs.RuntimeObject3DRenderer {
return this._renderer;
}
getBackFaceUpThroughWhichAxisRotation(): 'X' | 'Y' {
return this._backFaceUpThroughWhichAxisRotation;
}
setBackFaceUpThroughWhichAxisRotation(axis: 'X' | 'Y'): void {
this._backFaceUpThroughWhichAxisRotation = axis;
this._renderer.updateFace(faceNameToBitmaskIndex['back']);
}
getFacesOrientation(): 'Y' | 'Z' {
return this._facesOrientation;
}
setFacesOrientation(orientation: 'Y' | 'Z'): void {
this._facesOrientation = orientation;
this._renderer.updateFace(faceNameToBitmaskIndex['left']);
this._renderer.updateFace(faceNameToBitmaskIndex['right']);
this._renderer.updateFace(faceNameToBitmaskIndex['top']);
// Bottom texture should not change based on that setting.
}
updateFromObjectData(
oldObjectData: Cube3DObjectData,
newObjectData: Cube3DObjectData
): boolean {
super.updateFromObjectData(oldObjectData, newObjectData);
if (
oldObjectData.content.frontFaceVisible !==
newObjectData.content.frontFaceVisible
) {
this.setFaceVisibility('front', newObjectData.content.frontFaceVisible);
}
if (
oldObjectData.content.backFaceVisible !==
newObjectData.content.backFaceVisible
) {
this.setFaceVisibility('back', newObjectData.content.backFaceVisible);
}
if (
oldObjectData.content.leftFaceVisible !==
newObjectData.content.leftFaceVisible
) {
this.setFaceVisibility('left', newObjectData.content.leftFaceVisible);
}
if (
oldObjectData.content.rightFaceVisible !==
newObjectData.content.rightFaceVisible
) {
this.setFaceVisibility('right', newObjectData.content.rightFaceVisible);
}
if (
oldObjectData.content.topFaceVisible !==
newObjectData.content.topFaceVisible
) {
this.setFaceVisibility('top', newObjectData.content.topFaceVisible);
}
if (
oldObjectData.content.bottomFaceVisible !==
newObjectData.content.bottomFaceVisible
) {
this.setFaceVisibility(
'bottom',
newObjectData.content.bottomFaceVisible
);
}
if (
oldObjectData.content.frontFaceResourceName !==
newObjectData.content.frontFaceResourceName
) {
this.setFaceResourceName(
'front',
newObjectData.content.frontFaceResourceName
);
}
if (
oldObjectData.content.backFaceResourceName !==
newObjectData.content.backFaceResourceName
) {
this.setFaceResourceName(
'back',
newObjectData.content.backFaceResourceName
);
}
if (
oldObjectData.content.leftFaceResourceName !==
newObjectData.content.leftFaceResourceName
) {
this.setFaceResourceName(
'left',
newObjectData.content.leftFaceResourceName
);
}
if (
oldObjectData.content.rightFaceResourceName !==
newObjectData.content.rightFaceResourceName
) {
this.setFaceResourceName(
'right',
newObjectData.content.rightFaceResourceName
);
}
if (
oldObjectData.content.topFaceResourceName !==
newObjectData.content.topFaceResourceName
) {
this.setFaceResourceName(
'top',
newObjectData.content.topFaceResourceName
);
}
if (
oldObjectData.content.bottomFaceResourceName !==
newObjectData.content.bottomFaceResourceName
) {
this.setFaceResourceName(
'bottom',
newObjectData.content.bottomFaceResourceName
);
}
if (
oldObjectData.content.frontFaceResourceRepeat !==
newObjectData.content.frontFaceResourceRepeat
) {
this.setRepeatTextureOnFace(
'front',
newObjectData.content.frontFaceResourceRepeat
);
}
if (
oldObjectData.content.backFaceResourceRepeat !==
newObjectData.content.backFaceResourceRepeat
) {
this.setRepeatTextureOnFace(
'back',
newObjectData.content.backFaceResourceRepeat
);
}
if (
oldObjectData.content.leftFaceResourceRepeat !==
newObjectData.content.leftFaceResourceRepeat
) {
this.setRepeatTextureOnFace(
'left',
newObjectData.content.leftFaceResourceRepeat
);
}
if (
oldObjectData.content.rightFaceResourceRepeat !==
newObjectData.content.rightFaceResourceRepeat
) {
this.setRepeatTextureOnFace(
'right',
newObjectData.content.rightFaceResourceRepeat
);
}
if (
oldObjectData.content.topFaceResourceRepeat !==
newObjectData.content.topFaceResourceRepeat
) {
this.setRepeatTextureOnFace(
'top',
newObjectData.content.topFaceResourceRepeat
);
}
if (
oldObjectData.content.bottomFaceResourceRepeat !==
newObjectData.content.bottomFaceResourceRepeat
) {
this.setRepeatTextureOnFace(
'bottom',
newObjectData.content.bottomFaceResourceRepeat
);
}
if (
oldObjectData.content.backFaceUpThroughWhichAxisRotation !==
newObjectData.content.backFaceUpThroughWhichAxisRotation
) {
this.setBackFaceUpThroughWhichAxisRotation(
newObjectData.content.backFaceUpThroughWhichAxisRotation
);
}
if (
oldObjectData.content.facesOrientation !==
newObjectData.content.facesOrientation
) {
this.setFacesOrientation(newObjectData.content.facesOrientation);
}
if (
oldObjectData.content.materialType !==
newObjectData.content.materialType
) {
this.setMaterialType(newObjectData.content.materialType);
}
return true;
}
/**
* Return true if the texture transparency should be enabled.
*/
shouldUseTransparentTexture(): boolean {
return this._shouldUseTransparentTexture;
}
_convertMaterialType(
materialTypeString: string
): gdjs.Cube3DRuntimeObject.MaterialType {
if (materialTypeString === 'StandardWithoutMetalness') {
return gdjs.Cube3DRuntimeObject.MaterialType.StandardWithoutMetalness;
} else {
return gdjs.Cube3DRuntimeObject.MaterialType.Basic;
}
}
setMaterialType(materialTypeString: string) {
const newMaterialType = this._convertMaterialType(materialTypeString);
if (this._materialType === newMaterialType) {
return;
}
this._materialType = newMaterialType;
this._renderer._updateMaterials();
}
}
export namespace Cube3DRuntimeObject {
export enum MaterialType {
Basic,
StandardWithoutMetalness,
}
}
gdjs.registerObject('Scene3D::Cube3DObject', gdjs.Cube3DRuntimeObject);
}

View File

@@ -0,0 +1,319 @@
namespace gdjs {
// Three.js materials for a cube and the order of faces in the object is different,
// so we keep the mapping from one to the other.
const faceIndexToMaterialIndex = {
3: 0, // right
2: 1, // left
5: 2, // bottom
4: 3, // top
0: 4, // front
1: 5, // back
};
const materialIndexToFaceIndex = {
0: 3,
1: 2,
2: 5,
3: 4,
4: 0,
5: 1,
};
const noRepeatTextureVertexIndexToUvMapping = {
0: [0, 0],
1: [1, 0],
2: [0, 1],
3: [1, 1],
};
const noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ = {
0: [0, 1],
1: [0, 0],
2: [1, 1],
3: [1, 0],
};
let transparentMaterial: THREE.MeshBasicMaterial;
const getTransparentMaterial = () => {
if (!transparentMaterial)
transparentMaterial = new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0,
// Set the alpha test to to ensure the faces behind are rendered
// (no "back face culling" that would still be done if alphaTest is not set).
alphaTest: 1,
});
return transparentMaterial;
};
const getFaceMaterial = (
runtimeObject: gdjs.Cube3DRuntimeObject,
faceIndex: integer
) => {
if (!runtimeObject.isFaceAtIndexVisible(faceIndex))
return getTransparentMaterial();
return runtimeObject
.getInstanceContainer()
.getGame()
.getImageManager()
.getThreeMaterial(runtimeObject.getFaceAtIndexResourceName(faceIndex), {
useTransparentTexture: runtimeObject.shouldUseTransparentTexture(),
forceBasicMaterial:
runtimeObject._materialType ===
gdjs.Cube3DRuntimeObject.MaterialType.Basic,
});
};
class Cube3DRuntimeObjectPixiRenderer extends gdjs.RuntimeObject3DRenderer {
private _cube3DRuntimeObject: gdjs.Cube3DRuntimeObject;
private _boxMesh: THREE.Mesh;
constructor(
runtimeObject: gdjs.Cube3DRuntimeObject,
instanceContainer: gdjs.RuntimeInstanceContainer
) {
const geometry = new THREE.BoxGeometry(1, 1, 1);
// TODO (3D) - feature: support color instead of texture?
const materials = [
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[0]),
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[1]),
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[2]),
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[3]),
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[4]),
getFaceMaterial(runtimeObject, materialIndexToFaceIndex[5]),
];
const boxMesh = new THREE.Mesh(geometry, materials);
super(runtimeObject, instanceContainer, boxMesh);
this._boxMesh = boxMesh;
this._cube3DRuntimeObject = runtimeObject;
this.updateSize();
this.updatePosition();
this.updateRotation();
}
updateFace(faceIndex: integer) {
const materialIndex = faceIndexToMaterialIndex[faceIndex];
if (materialIndex === undefined) return;
this._boxMesh.material[materialIndex] = getFaceMaterial(
this._cube3DRuntimeObject,
faceIndex
);
if (this._cube3DRuntimeObject.isFaceAtIndexVisible(faceIndex)) {
this.updateTextureUvMapping(faceIndex);
}
}
updateSize(): void {
super.updateSize();
this.updateTextureUvMapping();
}
/**
* Updates the UV mapping of the geometry in order to repeat a material
* over the different faces of the cube.
* The mesh must be configured with a list of materials in order
* for the method to work.
* @param faceIndex The face index to update. If undefined, updates all the faces.
*/
updateTextureUvMapping(faceIndex?: number) {
// @ts-ignore - position is stored as a Float32BufferAttribute
const pos: THREE.BufferAttribute = this._boxMesh.geometry.getAttribute(
'position'
);
// @ts-ignore - uv is stored as a Float32BufferAttribute
const uvMapping: THREE.BufferAttribute = this._boxMesh.geometry.getAttribute(
'uv'
);
const startIndex =
faceIndex === undefined ? 0 : faceIndexToMaterialIndex[faceIndex] * 4;
const endIndex =
faceIndex === undefined
? 23
: faceIndexToMaterialIndex[faceIndex] * 4 + 3;
for (
let vertexIndex = startIndex;
vertexIndex <= endIndex;
vertexIndex++
) {
const materialIndex = Math.floor(
vertexIndex /
// Each face of the cube has 4 points
4
);
const material = this._boxMesh.material[materialIndex];
if (!material || !material.map) {
continue;
}
const shouldRepeatTexture = this._cube3DRuntimeObject.shouldRepeatTextureOnFaceAtIndex(
materialIndexToFaceIndex[materialIndex]
);
const shouldOrientateFacesTowardsY =
this._cube3DRuntimeObject.getFacesOrientation() === 'Y';
let x: float, y: float;
switch (materialIndex) {
case 0:
// Right face
if (shouldRepeatTexture) {
if (shouldOrientateFacesTowardsY) {
x =
-(this._boxMesh.scale.z / material.map.source.data.width) *
(pos.getZ(vertexIndex) - 0.5);
y =
-(this._boxMesh.scale.y / material.map.source.data.height) *
(pos.getY(vertexIndex) + 0.5);
} else {
x =
-(this._boxMesh.scale.y / material.map.source.data.width) *
(pos.getY(vertexIndex) - 0.5);
y =
(this._boxMesh.scale.z / material.map.source.data.height) *
(pos.getZ(vertexIndex) - 0.5);
}
} else {
if (shouldOrientateFacesTowardsY) {
[x, y] = noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
} else {
[
x,
y,
] = noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
}
}
break;
case 1:
// Left face
if (shouldRepeatTexture) {
if (shouldOrientateFacesTowardsY) {
x =
(this._boxMesh.scale.z / material.map.source.data.width) *
(pos.getZ(vertexIndex) + 0.5);
y =
-(this._boxMesh.scale.y / material.map.source.data.height) *
(pos.getY(vertexIndex) + 0.5);
} else {
x =
(this._boxMesh.scale.y / material.map.source.data.width) *
(pos.getY(vertexIndex) + 0.5);
y =
(this._boxMesh.scale.z / material.map.source.data.height) *
(pos.getZ(vertexIndex) - 0.5);
}
} else {
if (shouldOrientateFacesTowardsY) {
[x, y] = noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
} else {
[
x,
y,
] = noRepeatTextureVertexIndexToUvMappingForLeftAndRightFacesTowardsZ[
vertexIndex % 4
];
x = -x;
y = -y;
}
}
break;
case 2:
// Bottom face
if (shouldRepeatTexture) {
x =
(this._boxMesh.scale.x / material.map.source.data.width) *
(pos.getX(vertexIndex) + 0.5);
y =
(this._boxMesh.scale.z / material.map.source.data.height) *
(pos.getZ(vertexIndex) - 0.5);
} else {
[x, y] = noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
}
break;
case 3:
// Top face
if (shouldRepeatTexture) {
if (shouldOrientateFacesTowardsY) {
x =
(this._boxMesh.scale.x / material.map.source.data.width) *
(pos.getX(vertexIndex) + 0.5);
y =
-(this._boxMesh.scale.z / material.map.source.data.height) *
(pos.getZ(vertexIndex) + 0.5);
} else {
x =
-(this._boxMesh.scale.x / material.map.source.data.width) *
(pos.getX(vertexIndex) - 0.5);
y =
(this._boxMesh.scale.z / material.map.source.data.height) *
(pos.getZ(vertexIndex) - 0.5);
}
} else {
[x, y] = noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
if (!shouldOrientateFacesTowardsY) {
x = -x;
y = -y;
}
}
break;
case 4:
// Front face
if (shouldRepeatTexture) {
x =
(this._boxMesh.scale.x / material.map.source.data.width) *
(pos.getX(vertexIndex) + 0.5);
y =
-(this._boxMesh.scale.y / material.map.source.data.height) *
(pos.getY(vertexIndex) + 0.5);
} else {
[x, y] = noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
}
break;
case 5:
// Back face
const shouldBackFaceBeUpThroughXAxisRotation =
this._cube3DRuntimeObject.getBackFaceUpThroughWhichAxisRotation() ===
'X';
if (shouldRepeatTexture) {
x =
(shouldBackFaceBeUpThroughXAxisRotation ? 1 : -1) *
(this._boxMesh.scale.x / material.map.source.data.width) *
(pos.getX(vertexIndex) +
(shouldBackFaceBeUpThroughXAxisRotation ? 1 : -1) * 0.5);
y =
(shouldBackFaceBeUpThroughXAxisRotation ? 1 : -1) *
(this._boxMesh.scale.y / material.map.source.data.height) *
(pos.getY(vertexIndex) +
(shouldBackFaceBeUpThroughXAxisRotation ? -1 : 1) * 0.5);
} else {
[x, y] = noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
if (shouldBackFaceBeUpThroughXAxisRotation) {
x = -x;
y = -y;
}
}
break;
default:
[x, y] = noRepeatTextureVertexIndexToUvMapping[vertexIndex % 4];
}
uvMapping.setXY(vertexIndex, x, y);
}
uvMapping.needsUpdate = true;
}
_updateMaterials() {
for (let index = 0; index < 6; index++) {
this.updateFace(index);
}
}
}
export const Cube3DRuntimeObjectRenderer = Cube3DRuntimeObjectPixiRenderer;
export type Cube3DRuntimeObjectRenderer = Cube3DRuntimeObjectPixiRenderer;
}

View File

@@ -0,0 +1,101 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::DirectionalLight',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
makeFilter(
target: EffectsTarget,
effectData: EffectData
): gdjs.PixiFiltersTools.Filter {
return new (class implements gdjs.PixiFiltersTools.Filter {
light: THREE.DirectionalLight;
rotationObject: THREE.Group;
_isEnabled: boolean = false;
top: string = 'Y-';
elevation: float = 45;
rotation: float = 0;
constructor() {
this.light = new THREE.DirectionalLight();
this.light.position.set(1, 0, 0);
this.rotationObject = new THREE.Group();
this.rotationObject.add(this.light);
this.updateRotation();
}
isEnabled(target: EffectsTarget): boolean {
return this._isEnabled;
}
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
if (this._isEnabled === enabled) {
return true;
}
if (enabled) {
return this.applyEffect(target);
} else {
return this.removeEffect(target);
}
}
applyEffect(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
if (!scene) {
return false;
}
scene.add(this.rotationObject);
this._isEnabled = true;
return true;
}
removeEffect(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
if (!scene) {
return false;
}
scene.remove(this.rotationObject);
this._isEnabled = false;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'intensity') {
this.light.intensity = value;
} else if (parameterName === 'elevation') {
this.elevation = value;
this.updateRotation();
} else if (parameterName === 'rotation') {
this.rotation = value;
this.updateRotation();
}
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.light.color = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
);
}
if (parameterName === 'top') {
this.top = value;
this.updateRotation();
}
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
updateRotation() {
if (this.top === 'Z+') {
// 0° is a light from the right of the screen.
this.rotationObject.rotation.z = gdjs.toRad(this.rotation);
this.rotationObject.rotation.y = -gdjs.toRad(this.elevation);
} else {
// 0° becomes a light from Z+.
this.rotationObject.rotation.y = gdjs.toRad(this.rotation) - 90;
this.rotationObject.rotation.z = -gdjs.toRad(this.elevation);
}
}
})();
}
})()
);
}

View File

@@ -0,0 +1,70 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::ExponentialFog',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
makeFilter(
target: EffectsTarget,
effectData: EffectData
): gdjs.PixiFiltersTools.Filter {
return new (class implements gdjs.PixiFiltersTools.Filter {
fog: THREE.FogExp2;
constructor() {
this.fog = new THREE.FogExp2(0xffffff);
}
isEnabled(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
return scene ? scene.fog === this.fog : false;
}
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
if (enabled) {
return this.applyEffect(target);
} else {
return this.removeEffect(target);
}
}
applyEffect(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
if (!scene || scene.fog === undefined) {
return false;
}
scene.fog = this.fog;
return true;
}
removeEffect(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
if (!scene || scene.fog === undefined) {
return false;
}
scene.fog = null;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'density') {
this.fog.density = value;
}
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.fog.color = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
);
}
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
})();
}
})()
);
}

2687
Extensions/3D/JsExtension.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,72 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator(
'Scene3D::LinearFog',
new (class implements gdjs.PixiFiltersTools.FilterCreator {
makeFilter(
target: EffectsTarget,
effectData: EffectData
): gdjs.PixiFiltersTools.Filter {
return new (class implements gdjs.PixiFiltersTools.Filter {
fog: THREE.Fog;
constructor() {
this.fog = new THREE.Fog(0xffffff);
}
isEnabled(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
return scene ? scene.fog === this.fog : false;
}
setEnabled(target: EffectsTarget, enabled: boolean): boolean {
if (enabled) {
return this.applyEffect(target);
} else {
return this.removeEffect(target);
}
}
applyEffect(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
if (!scene || scene.fog === undefined) {
return false;
}
scene.fog = this.fog;
return true;
}
removeEffect(target: EffectsTarget): boolean {
const scene = target.get3DRendererObject() as
| THREE.Scene
| null
| undefined;
if (!scene || scene.fog === undefined) {
return false;
}
scene.fog = null;
return true;
}
updatePreRender(target: gdjs.EffectsTarget): any {}
updateDoubleParameter(parameterName: string, value: number): void {
if (parameterName === 'near') {
this.fog.near = value;
} else if (parameterName === 'far') {
this.fog.far = value;
}
}
updateStringParameter(parameterName: string, value: string): void {
if (parameterName === 'color') {
this.fog.color = new THREE.Color(
gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value)
);
}
}
updateBooleanParameter(parameterName: string, value: boolean): void {}
})();
}
})()
);
}

View File

@@ -0,0 +1,116 @@
namespace gdjs {
/** Base parameters for {@link gdjs.Cube3DRuntimeObject} */
export interface Model3DObjectData extends Object3DData {
/** The base parameters of the Model3D object */
content: Object3DDataContent & {
modelResourceName: string;
rotationX: number;
rotationY: number;
rotationZ: number;
keepAspectRatio: boolean;
materialType: 'Basic' | 'StandardWithoutMetalness' | 'KeepOriginal';
};
}
/**
* A 3D object which displays a 3D model.
*/
export class Model3DRuntimeObject extends gdjs.RuntimeObject3D {
_renderer: gdjs.Model3DRuntimeObjectRenderer;
_modelResourceName: string;
_materialType: gdjs.Model3DRuntimeObject.MaterialType =
gdjs.Model3DRuntimeObject.MaterialType.Basic;
constructor(
instanceContainer: gdjs.RuntimeInstanceContainer,
objectData: Model3DObjectData
) {
super(instanceContainer, objectData);
this._modelResourceName = objectData.content.modelResourceName;
this._renderer = new gdjs.Model3DRuntimeObjectRenderer(
this,
instanceContainer
);
this._updateMaterialType(objectData);
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
this.onCreated();
}
updateFromObjectData(
oldObjectData: Model3DObjectData,
newObjectData: Model3DObjectData
): boolean {
super.updateFromObjectData(oldObjectData, newObjectData);
if (
oldObjectData.content.width !== newObjectData.content.width ||
oldObjectData.content.height !== newObjectData.content.height ||
oldObjectData.content.depth !== newObjectData.content.depth ||
oldObjectData.content.rotationX !== newObjectData.content.rotationX ||
oldObjectData.content.rotationY !== newObjectData.content.rotationY ||
oldObjectData.content.rotationZ !== newObjectData.content.rotationZ ||
oldObjectData.content.keepAspectRatio !==
newObjectData.content.keepAspectRatio
) {
this._updateDefaultTransformation(newObjectData);
}
if (
oldObjectData.content.materialType !==
newObjectData.content.materialType
) {
this._updateMaterialType(newObjectData);
}
return true;
}
_updateDefaultTransformation(objectData: Model3DObjectData) {
const rotationX = objectData.content.rotationX || 0;
const rotationY = objectData.content.rotationY || 0;
const rotationZ = objectData.content.rotationZ || 0;
const keepAspectRatio = objectData.content.keepAspectRatio;
this._renderer._updateDefaultTransformation(
rotationX,
rotationY,
rotationZ,
this._getOriginalWidth(),
this._getOriginalHeight(),
this._getOriginalDepth(),
keepAspectRatio
);
}
getRenderer(): RuntimeObject3DRenderer {
return this._renderer;
}
_convertMaterialType(
materialTypeString: string
): gdjs.Model3DRuntimeObject.MaterialType {
if (materialTypeString === 'KeepOriginal') {
return gdjs.Model3DRuntimeObject.MaterialType.KeepOriginal;
} else if (materialTypeString === 'StandardWithoutMetalness') {
return gdjs.Model3DRuntimeObject.MaterialType.StandardWithoutMetalness;
} else {
return gdjs.Model3DRuntimeObject.MaterialType.Basic;
}
}
_updateMaterialType(objectData: Model3DObjectData) {
this._materialType = this._convertMaterialType(
objectData.content.materialType
);
this._renderer._updateMaterials();
this._updateDefaultTransformation(objectData);
}
}
export namespace Model3DRuntimeObject {
export enum MaterialType {
Basic,
StandardWithoutMetalness,
KeepOriginal,
}
}
gdjs.registerObject('Scene3D::Model3DObject', gdjs.Model3DRuntimeObject);
}

View File

@@ -0,0 +1,184 @@
namespace gdjs {
class Model3DRuntimeObject3DRenderer extends gdjs.RuntimeObject3DRenderer {
private _model3DRuntimeObject: gdjs.Model3DRuntimeObject;
/**
* The 3D model stretched in a 1x1x1 cube.
*/
private _threeObject: THREE.Object3D;
constructor(
runtimeObject: gdjs.Model3DRuntimeObject,
instanceContainer: gdjs.RuntimeInstanceContainer
) {
// @ts-ignore It can't be null if THREE exists.
const originalModelMesh: THREE.Object3D = instanceContainer
.getGame()
.getModel3DManager()
.getModel(runtimeObject._modelResourceName);
const modelObject3D = originalModelMesh.clone();
// Create a group to transform the object according to
// position, angle and dimensions.
const group = new THREE.Group();
group.rotation.order = 'ZYX';
group.add(modelObject3D);
super(runtimeObject, instanceContainer, group);
this._model3DRuntimeObject = runtimeObject;
this._threeObject = modelObject3D;
this.updateSize();
this.updatePosition();
this.updateRotation();
}
_updateDefaultTransformation(
rotationX: float,
rotationY: float,
rotationZ: float,
originalWidth: float,
originalHeight: float,
originalDepth: float,
keepAspectRatio: boolean
) {
const boundingBox = this._getModelAABB(rotationX, rotationY, rotationZ);
// Center the model.
this._threeObject.position.set(
-(boundingBox.min.x + boundingBox.max.x) / 2,
(this._threeObject.position.y =
-(boundingBox.min.y + boundingBox.max.y) / 2),
(this._threeObject.position.z =
-(boundingBox.min.z + boundingBox.max.z) / 2)
);
// Rotate the model.
this._threeObject.scale.set(1, 1, 1);
this._threeObject.rotation.set(
gdjs.toRad(rotationX),
gdjs.toRad(rotationY),
gdjs.toRad(rotationZ)
);
// Stretch the model in a 1x1x1 cube.
const modelWidth = boundingBox.max.x - boundingBox.min.x;
const modelHeight = boundingBox.max.y - boundingBox.min.y;
const modelDepth = boundingBox.max.z - boundingBox.min.z;
const scaleX = 1 / modelWidth;
const scaleY = 1 / modelHeight;
const scaleZ = 1 / modelDepth;
const scaleMatrix = new THREE.Matrix4();
scaleMatrix.makeScale(scaleX, scaleY, scaleZ);
this._threeObject.updateMatrix();
this._threeObject.applyMatrix4(scaleMatrix);
if (keepAspectRatio) {
// Reduce the object dimensions to keep aspect ratio.
const widthRatio = originalWidth / modelWidth;
const heightRatio = originalHeight / modelHeight;
const depthRatio = originalDepth / modelDepth;
const scaleRatio = Math.min(widthRatio, heightRatio, depthRatio);
this._object._setOriginalWidth(scaleRatio * modelWidth);
this._object._setOriginalHeight(scaleRatio * modelHeight);
this._object._setOriginalDepth(scaleRatio * modelDepth);
}
this._threeObject.updateMatrix();
}
private _getModelAABB(
rotationX: float,
rotationY: float,
rotationZ: float
) {
// The original model is used because `setFromObject` is working in
// world transformation.
// @ts-ignore It can't be null if THREE exists.
const originalModelMesh: THREE.Object3D = this._object
.getInstanceContainer()
.getGame()
.getModel3DManager()
.getModel(this._model3DRuntimeObject._modelResourceName);
originalModelMesh.rotation.set(
gdjs.toRad(rotationX),
gdjs.toRad(rotationY),
gdjs.toRad(rotationZ)
);
const aabb = new THREE.Box3().setFromObject(originalModelMesh);
// Revert changes.
originalModelMesh.rotation.set(0, 0, 0);
return aabb;
}
_updateMaterials() {
// @ts-ignore It can't be null if THREE exists.
const originalModelMesh: THREE.Object3D = this._model3DRuntimeObject
.getInstanceContainer()
.getGame()
.getModel3DManager()
.getModel(this._model3DRuntimeObject._modelResourceName);
const modelObject3D = originalModelMesh.clone();
this.get3DRendererObject().remove(this._threeObject);
this.get3DRendererObject().add(modelObject3D);
this._threeObject = modelObject3D;
this._replaceMaterials();
}
/**
* Replace materials to better work with lights (or no light).
*/
_replaceMaterials() {
if (
this._model3DRuntimeObject._materialType ===
gdjs.Model3DRuntimeObject.MaterialType.StandardWithoutMetalness
) {
this._threeObject.traverse((node) => {
if (node.type === 'Mesh') {
const mesh = node as THREE.Mesh;
const material = mesh.material as THREE.MeshStandardMaterial;
//@ts-ignore
if (material.metalness) {
//@ts-ignore
material.metalness = 0;
}
}
});
} else if (
this._model3DRuntimeObject._materialType ===
gdjs.Model3DRuntimeObject.MaterialType.Basic
) {
this._threeObject.traverse((node) => {
if (node.type === 'Mesh') {
const mesh = node as THREE.Mesh;
const basicMaterial = new THREE.MeshBasicMaterial();
//@ts-ignore
if (mesh.material.color) {
//@ts-ignore
basicMaterial.color = mesh.material.color;
}
//@ts-ignore
if (mesh.material.map) {
//@ts-ignore
basicMaterial.map = mesh.material.map;
}
mesh.material = basicMaterial;
}
});
}
}
}
export const Model3DRuntimeObjectRenderer = Model3DRuntimeObject3DRenderer;
export type Model3DRuntimeObjectRenderer = Model3DRuntimeObject3DRenderer;
}

View File

@@ -0,0 +1,235 @@
namespace gdjs {
export namespace scene3d {
const assumedFovIn2D = 45;
export namespace camera {
export const getCameraZ = (
runtimeScene: RuntimeScene,
layerName: string,
cameraIndex: integer
): float => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
const fov = threeCamera ? threeCamera.fov : assumedFovIn2D;
return layer.getCameraZ(fov, cameraIndex);
};
export const setCameraZ = (
runtimeScene: RuntimeScene,
z: float,
layerName: string,
cameraIndex: integer
) => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
const fov = threeCamera ? threeCamera.fov : assumedFovIn2D;
layer.setCameraZ(z, fov, cameraIndex);
};
export const getCameraRotationX = (
runtimeScene: RuntimeScene,
layerName: string,
cameraIndex: integer
): float => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return 0;
return gdjs.toDegrees(threeCamera.rotation.x);
};
export const setCameraRotationX = (
runtimeScene: RuntimeScene,
angle: float,
layerName: string,
cameraIndex: integer
) => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return;
threeCamera.rotation.x = gdjs.toRad(angle);
};
export const getCameraRotationY = (
runtimeScene: RuntimeScene,
layerName: string,
cameraIndex: integer
): float => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return 0;
return gdjs.toDegrees(threeCamera.rotation.y);
};
export const setCameraRotationY = (
runtimeScene: RuntimeScene,
angle: float,
layerName: string,
cameraIndex: integer
) => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return;
threeCamera.rotation.y = gdjs.toRad(angle);
};
export const turnCameraTowardObject = (
runtimeScene: RuntimeScene,
object: gdjs.RuntimeObject,
layerName: string,
cameraIndex: integer,
isStandingOnY: boolean
) => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return;
if (isStandingOnY) {
threeCamera.up.set(0, 1, 0);
} else {
threeCamera.up.set(0, 0, 1);
}
threeCamera.lookAt(
object.getCenterXInScene(),
-object.getCenterYInScene(),
//@ts-ignore
object.getZ ? object.getZ() : 0
);
// The layer angle takes over the 3D camera Z rotation.
layer.setCameraRotation(gdjs.toDegrees(-threeCamera.rotation.z));
};
export const turnCameraTowardPosition = (
runtimeScene: RuntimeScene,
x: float,
y: float,
z: float,
layerName: string,
cameraIndex: integer,
isStandingOnY: boolean
) => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return;
if (isStandingOnY) {
threeCamera.up.set(0, 1, 0);
} else {
threeCamera.up.set(0, 0, 1);
}
threeCamera.lookAt(x, -y, z);
// The layer angle takes over the 3D camera Z rotation.
layer.setCameraRotation(gdjs.toDegrees(-threeCamera.rotation.z));
};
export const getNearPlane = (
runtimeScene: RuntimeScene,
layerName: string,
cameraIndex: integer
): float => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return 0;
return threeCamera.near;
};
export const setNearPlane = (
runtimeScene: RuntimeScene,
distance: float,
layerName: string,
cameraIndex: integer
) => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return;
threeCamera.near = Math.min(
// 0 is not a valid value for three js perspective camera:
// https://threejs.org/docs/#api/en/cameras/PerspectiveCamera.
Math.max(distance, 0.0001),
// Near value cannot exceed far value.
threeCamera.far
);
layerRenderer.setThreeCameraDirty(true);
};
export const getFarPlane = (
runtimeScene: RuntimeScene,
layerName: string,
cameraIndex: integer
): float => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return 0;
return threeCamera.far;
};
export const setFarPlane = (
runtimeScene: RuntimeScene,
distance: float,
layerName: string,
cameraIndex: integer
) => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return;
// Far value cannot be lower than near value
threeCamera.far = Math.max(distance, threeCamera.near);
layerRenderer.setThreeCameraDirty(true);
};
export const getFov = (
runtimeScene: RuntimeScene,
layerName: string,
cameraIndex: integer
): float => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return 45;
return threeCamera.fov;
};
export const setFov = (
runtimeScene: RuntimeScene,
angle: float,
layerName: string,
cameraIndex: integer
) => {
const layer = runtimeScene.getLayer(layerName);
const layerRenderer = layer.getRenderer();
const threeCamera = layerRenderer.getThreeCamera();
if (!threeCamera) return;
threeCamera.fov = Math.min(Math.max(angle, 0), 180);
layerRenderer.setThreeCameraDirty(true);
};
}
}
}

View File

@@ -181,7 +181,7 @@ module.exports = {
)
.addParameter(
'yesorno',
_('Display in landscape? (portait otherwise)'),
_('Display in landscape? (portrait otherwise)'),
'',
false
)

View File

@@ -117,7 +117,7 @@ namespace gdjs {
await admob.start();
logger.info('AdMob succesfully started.');
logger.info('AdMob successfully started.');
admobStarted = true;
},
false

View File

@@ -439,7 +439,7 @@ module.exports = {
/**
* You can optionally add sanity tests that will check the basic working
* of your extension behaviors/objects by instanciating behaviors/objects
* of your extension behaviors/objects by instantiating behaviors/objects
* and setting the property to a given value.
*
* If you don't have any tests, you can simply return an empty array.

View File

@@ -12,31 +12,32 @@ In the example below, we are defining 4 text styles.
```js
let text = new MultiStyleText("Let's make some <ml>multiline</ml>\nand <ms>multistyle</ms> text for\n<pixi>Pixi.js!</pixi>",
{
"default": {
fontFamily: "Arial",
fontSize: "24px",
fill: "#cccccc",
align: "center"
},
"ml": {
fontStyle: "italic",
fill: "#ff8888"
},
"ms": {
fontStyle: "italic",
fill: "#4488ff"
},
"pixi": {
fontSize: "64px",
fill: "#efefef"
}
"default": {
fontFamily: "Arial",
fontSize: "24px",
fill: "#cccccc",
align: "center"
},
"ml": {
fontStyle: "italic",
fill: "#ff8888"
},
"ms": {
fontStyle: "italic",
fill: "#4488ff"
},
"pixi": {
fontSize: "64px",
fill: "#efefef"
}
});
```
## Build instructions
```
$ yarn install
$ yarn build
```bash
yarn install
yarn build
```
## Usage
@@ -46,6 +47,7 @@ $ yarn build
Creates a new `MultiStyleText` with the given text and styles.
#### `textStyles`
Type: `{ [key: string]: ExtendedTextStyle }`
Each key of this dictionary should match with a tag in the text. Use the key `default` for the default style.
@@ -57,10 +59,10 @@ The `align`, `wordWrap`, `wordWrapWidth`, and `breakWord` properties are ignored
If text is rendered without any value assigned to a given parameter, Pixi's defaults are used.
## Demo
```
$ yarn demo
```
```bash
yarn demo
```
## License

View File

@@ -392,7 +392,7 @@ module.exports = {
/**
* You can optionally add sanity tests that will check the basic working
* of your extension behaviors/objects by instanciating behaviors/objects
* of your extension behaviors/objects by instantiating behaviors/objects
* and setting the property to a given value.
*
* If you don't have any tests, you can simply return an empty array.
@@ -554,7 +554,7 @@ module.exports = {
if (!texture.valid) {
// Post pone texture update if texture is not loaded.
// (otherwise, the bitmap font would not get updated when the
// texture is loaded and udpated).
// texture is loaded and updated).
return new Promise((resolve) => {
texture.once('update', () => {
resolve(loadBitmapFont());

View File

@@ -14,7 +14,7 @@ namespace gdjs {
/**
* Vibrate the mobile device in a pattern.
* You can add multiple comma separated values where every second one determines the silence between vibrations.
* Example: "200,1000,500" (200ms vibration, 1sec silense, 500ms vibration)
* Example: "200,1000,500" (200ms vibration, 1sec silence, 500ms vibration)
* @param intervals Comma separated list of values (in ms).
*/
export const startVibrationPattern = function (intervals: string) {

View File

@@ -1,11 +1,12 @@
# bondage.js [![Build Status](https://travis-ci.org/jhayley/bondage.js.svg?branch=master)](https://travis-ci.org/jhayley/bondage.js)
[Yarn](https://github.com/InfiniteAmmoInc/Yarn) parser for Javascript, in the same vein as [YarnSpinner](https://github.com/thesecretlab/YarnSpinner).
# Usage
#### As a Web Tool
To run through your yarn files in your browser, go to http://hayley.zone/bondage.js, paste your yarn data in the field, then hit "compile".
To run through your yarn files in your browser, go to <http://hayley.zone/bondage.js>, paste your yarn data in the field, then hit "compile".
#### As a Command Line Tool
Installation: `npm install -g bondage`

View File

@@ -103,7 +103,7 @@ namespace gdjs {
return;
}
// Autoscroll commands so the user doesnt have to press again
// Autoscroll commands so the user doesn't have to press again.
if (
gdjs.dialogueTree._isLineTypeCommand() &&
this.dialogueDataType === 'text' &&
@@ -458,7 +458,7 @@ namespace gdjs {
*
* There are three types:
* - text - regular dialogue text is being parsed at the moment
* - options - the player has reached a branching choise moment where they must select one of multiple options
* - options - the player has reached a branching choice moment where they must select one of multiple options
* - command - a <<command>> was called in the background, that can be used to trigger game events, but will not be displayed in the dialogue box.
*
* @param type The type you want to check for ( one of the three above )
@@ -485,7 +485,7 @@ namespace gdjs {
};
/**
* Check if a branch exists. It is also used internaly whenever you use the start from action.
* Check if a branch exists. It is also used internally whenever you use the start from action.
* @param branchName The Dialogue Branch name you want to check.
*/
gdjs.dialogueTree.hasDialogueBranch = function (branchName: string) {

View File

@@ -48,6 +48,7 @@ module.exports = {
'Adjust gamma, contrast, saturation, brightness, alpha or color-channel shift.'
)
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-adjustment.js')
.addIncludeFile('Extensions/Effects/adjustment-pixi-filter.js');
const adjustmentProperties = adjustmentEffect.getProperties();
@@ -96,6 +97,7 @@ module.exports = {
.addEffect('AdvancedBloom')
.setFullName(_('Advanced bloom'))
.setDescription(_('Applies a bloom effect.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-kawase-blur.js')
.addIncludeFile(
'Extensions/Effects/pixi-filters/filter-advanced-bloom.js'
@@ -138,6 +140,7 @@ module.exports = {
.addEffect('Ascii')
.setFullName(_('ASCII'))
.setDescription(_('Render the image with ASCII characters only.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-ascii.js')
.addIncludeFile('Extensions/Effects/ascii-pixi-filter.js');
const asciiProperties = asciiEffect.getProperties();
@@ -151,6 +154,7 @@ module.exports = {
.addEffect('Bevel')
.setFullName(_('Beveled edges'))
.setDescription(_('Add beveled edges around the rendered image.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-bevel.js')
.addIncludeFile('Extensions/Effects/bevel-pixi-filter.js');
const bevelProperties = bevelEffect.getProperties();
@@ -194,6 +198,7 @@ module.exports = {
.addEffect('BlackAndWhite')
.setFullName(_('Black and White'))
.setDescription(_('Alter the colors to make the image black and white'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/black-and-white-pixi-filter.js');
const blackAndWhiteProperties = blackAndWhiteEffect.getProperties();
blackAndWhiteProperties
@@ -208,6 +213,7 @@ module.exports = {
.setDescription(
_('Alter the rendered image with the specified blend mode.')
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/blending-mode-pixi-filter.js');
const blendingModeProperties = blendingModeEffect.getProperties();
blendingModeProperties
@@ -225,6 +231,7 @@ module.exports = {
.addEffect('Blur')
.setFullName(_('Blur (Gaussian, slow - prefer to use Kawase blur)'))
.setDescription(_('Blur the rendered image. This is slow, so prefer to use Kawase blur in most cases.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/blur-pixi-filter.js');
const blurProperties = blurEffect.getProperties();
blurProperties
@@ -256,6 +263,7 @@ module.exports = {
.addEffect('Brightness')
.setFullName(_('Brightness'))
.setDescription(_('Make the image brighter.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/brightness-pixi-filter.js');
const brightnessProperties = brightnessEffect.getProperties();
brightnessProperties
@@ -268,6 +276,7 @@ module.exports = {
.addEffect('BulgePinch')
.setFullName(_('Bulge Pinch'))
.setDescription(_('Bulges or pinches the image in a circle.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-bulge-pinch.js')
.addIncludeFile('Extensions/Effects/bulge-pinch-pixi-filter.js');
const bulgePinchProperties = bulgePinchEffect.getProperties();
@@ -299,6 +308,7 @@ module.exports = {
.addEffect('ColorMap')
.setFullName(_('Color Map'))
.setDescription(_('Change the color rendered on screen.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/color-map-pixi-filter.js')
.addIncludeFile('Extensions/Effects/pixi-filters/filter-color-map.js');
const colorMapProperties = colorMapEffect.getProperties();
@@ -309,7 +319,7 @@ module.exports = {
.setLabel(_('Color map texture for the effect'))
.setDescription(
_(
'You can change colors of pixels by modifing a reference color image, containing each colors, called the *Color Map Texture*. To get started, **download** [a default color map texture here](https://wiki.gdevelop.io/gdevelop5/interface/scene-editor/layer-effects).'
'You can change colors of pixels by modifying a reference color image, containing each colors, called the *Color Map Texture*. To get started, **download** [a default color map texture here](https://wiki.gdevelop.io/gdevelop5/interface/scene-editor/layer-effects).'
)
);
colorMapProperties
@@ -328,6 +338,7 @@ module.exports = {
.addEffect('ColorReplace')
.setFullName(_('Color Replace'))
.setDescription(_('Effect replacing a color (or similar) by another.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-color-replace.js')
.addIncludeFile('Extensions/Effects/color-replace-pixi-filter.js');
const colorReplaceProperties = colorReplaceEffect.getProperties();
@@ -358,6 +369,7 @@ module.exports = {
.addEffect('CRT')
.setFullName(_('CRT'))
.setDescription(_('Apply an effect resembling old CRT monitors.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-crt.js')
.addIncludeFile('Extensions/Effects/crt-pixi-filter.js');
const crtProperties = crtEffect.getProperties();
@@ -435,6 +447,7 @@ module.exports = {
'Uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object.'
)
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/displacement-pixi-filter.js');
const displacementProperties = displacementEffect.getProperties();
displacementProperties
@@ -466,6 +479,7 @@ module.exports = {
'Applies a dotscreen effect making objects appear to be made out of black and white halftone dots like an old printer.'
)
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-dot.js')
.addIncludeFile('Extensions/Effects/dot-pixi-filter.js');
const dotProperties = dotEffect.getProperties();
@@ -486,6 +500,7 @@ module.exports = {
.addEffect('DropShadow')
.setFullName(_('Drop shadow'))
.setDescription(_('Add a shadow around the rendered image.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-kawase-blur.js')
.addIncludeFile('Extensions/Effects/pixi-filters/filter-drop-shadow.js')
.addIncludeFile('Extensions/Effects/drop-shadow-pixi-filter.js');
@@ -536,6 +551,7 @@ module.exports = {
.addEffect('Glitch')
.setFullName(_('Glitch'))
.setDescription(_('Applies a glitch effect to an object.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-glitch.js')
.addIncludeFile('Extensions/Effects/glitch-pixi-filter.js');
const glitchProperties = glitchEffect.getProperties();
@@ -626,6 +642,7 @@ module.exports = {
.addEffect('Glow')
.setFullName(_('Glow'))
.setDescription(_('Add a glow effect around the rendered image.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-glow.js')
.addIncludeFile('Extensions/Effects/glow-pixi-filter.js');
const glowProperties = glowEffect.getProperties();
@@ -654,6 +671,7 @@ module.exports = {
.addEffect('Godray')
.setFullName(_('Godray'))
.setDescription(_('Apply and animate atmospheric light rays.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-godray.js')
.addIncludeFile('Extensions/Effects/godray-pixi-filter.js');
const godrayProperties = godrayEffect.getProperties();
@@ -713,6 +731,7 @@ module.exports = {
.setDescription(
_('Blur the rendered image, with much better performance than Gaussian blur.')
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-kawase-blur.js')
.addIncludeFile('Extensions/Effects/kawase-blur-pixi-filter.js');
const kawaseBlurProperties = kawaseBlurEffect.getProperties();
@@ -747,6 +766,7 @@ module.exports = {
.addEffect('LightNight')
.setFullName(_('Light Night'))
.setDescription(_('Alter the colors to simulate night.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/light-night-pixi-filter.js');
const lightNightProperties = lightNightEffect.getProperties();
lightNightProperties
@@ -759,6 +779,7 @@ module.exports = {
.addEffect('Night')
.setFullName(_('Dark Night'))
.setDescription(_('Alter the colors to simulate a dark night.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/night-pixi-filter.js');
const nightProperties = nightEffect.getProperties();
nightProperties
@@ -776,6 +797,7 @@ module.exports = {
.addEffect('Noise')
.setFullName(_('Noise'))
.setDescription(_('Add some noise on the rendered image.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/noise-pixi-filter.js');
const noiseProperties = noiseEffect.getProperties();
noiseProperties
@@ -788,6 +810,7 @@ module.exports = {
.addEffect('OldFilm')
.setFullName(_('Old Film'))
.setDescription(_('Add a Old film effect around the rendered image.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-old-film.js')
.addIncludeFile('Extensions/Effects/old-film-pixi-filter.js');
const oldFilmProperties = oldFilmEffect.getProperties();
@@ -858,6 +881,7 @@ module.exports = {
.addEffect('Outline')
.setFullName(_('Outline'))
.setDescription(_('Draws an outline around the rendered image.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-outline.js')
.addIncludeFile('Extensions/Effects/outline-pixi-filter.js');
const outlineProperties = outlineEffect.getProperties();
@@ -884,6 +908,7 @@ module.exports = {
.setDescription(
_("Applies a pixelate effect, making display objects appear 'blocky'.")
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-pixelate.js')
.addIncludeFile('Extensions/Effects/pixelate-pixi-filter.js');
const pixelateProperties = pixelateEffect.getProperties();
@@ -898,6 +923,7 @@ module.exports = {
.addEffect('RadialBlur')
.setFullName(_('Radial Blur'))
.setDescription(_('Applies a Motion blur to an object.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-radial-blur.js')
.addIncludeFile('Extensions/Effects/radial-blur-pixi-filter.js')
.markAsNotWorkingForObjects(); // See https://github.com/pixijs/filters/issues/304
@@ -945,6 +971,7 @@ module.exports = {
'Applies a reflection effect to simulate the reflection on water with waves.'
)
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-reflection.js')
.addIncludeFile('Extensions/Effects/reflection-pixi-filter.js');
const reflectionProperties = reflectionEffect.getProperties();
@@ -1014,6 +1041,7 @@ module.exports = {
.setDescription(
_('Applies a RGB split effect also known as chromatic aberration.')
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-rgb-split.js')
.addIncludeFile('Extensions/Effects/rgb-split-pixi-filter.js');
const rgbSplitProperties = rgbSplitEffect.getProperties();
@@ -1052,6 +1080,7 @@ module.exports = {
.addEffect('Sepia')
.setFullName(_('Sepia'))
.setDescription(_('Alter the colors to sepia.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/sepia-pixi-filter.js');
const sepiaProperties = sepiaEffect.getProperties();
sepiaProperties
@@ -1064,6 +1093,7 @@ module.exports = {
.addEffect('TiltShift')
.setFullName(_('Tilt shift'))
.setDescription(_('Render a tilt-shift-like camera effect.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-tilt-shift.js')
.addIncludeFile('Extensions/Effects/tilt-shift-pixi-filter.js');
const tiltShiftProperties = tiltShiftEffect.getProperties();
@@ -1086,6 +1116,7 @@ module.exports = {
'Applies a twist effect making objects appear twisted in the given direction.'
)
)
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-twist.js')
.addIncludeFile('Extensions/Effects/twist-pixi-filter.js')
.markAsNotWorkingForObjects(); // See https://github.com/pixijs/filters/issues/304
@@ -1123,6 +1154,7 @@ module.exports = {
.addEffect('ZoomBlur')
.setFullName(_('Zoom blur'))
.setDescription(_('Applies a Zoom blur.'))
.markAsOnlyWorkingFor2D()
.addIncludeFile('Extensions/Effects/pixi-filters/filter-zoom-blur.js')
.addIncludeFile('Extensions/Effects/zoom-blur-pixi-filter.js')
.markAsNotWorkingForObjects(); // See https://github.com/pixijs/filters/issues/304

View File

@@ -1,31 +1,34 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('Adjustment', {
makePIXIFilter: function (target, effectData) {
const adjustmentFilter = new PIXI.filters.AdjustmentFilter();
return adjustmentFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
if (parameterName === 'gamma') {
adjustmentFilter.gamma = value;
} else if (parameterName === 'saturation') {
adjustmentFilter.saturation = value;
} else if (parameterName === 'contrast') {
adjustmentFilter.contrast = value;
} else if (parameterName === 'brightness') {
adjustmentFilter.brightness = value;
} else if (parameterName === 'red') {
adjustmentFilter.red = value;
} else if (parameterName === 'green') {
adjustmentFilter.green = value;
} else if (parameterName === 'blue') {
adjustmentFilter.blue = value;
} else if (parameterName === 'alpha') {
adjustmentFilter.alpha = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'Adjustment',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const adjustmentFilter = new PIXI.filters.AdjustmentFilter();
return adjustmentFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const adjustmentFilter = (filter as unknown) as PIXI.filters.AdjustmentFilter;
if (parameterName === 'gamma') {
adjustmentFilter.gamma = value;
} else if (parameterName === 'saturation') {
adjustmentFilter.saturation = value;
} else if (parameterName === 'contrast') {
adjustmentFilter.contrast = value;
} else if (parameterName === 'brightness') {
adjustmentFilter.brightness = value;
} else if (parameterName === 'red') {
adjustmentFilter.red = value;
} else if (parameterName === 'green') {
adjustmentFilter.green = value;
} else if (parameterName === 'blue') {
adjustmentFilter.blue = value;
} else if (parameterName === 'alpha') {
adjustmentFilter.alpha = value;
}
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,27 +1,30 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('AdvancedBloom', {
makePIXIFilter: function (target, effectData) {
const advancedBloomFilter = new PIXI.filters.AdvancedBloomFilter();
return advancedBloomFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
if (parameterName === 'threshold') {
advancedBloomFilter.threshold = value;
} else if (parameterName === 'bloomScale') {
advancedBloomFilter.bloomScale = value;
} else if (parameterName === 'brightness') {
advancedBloomFilter.brightness = value;
} else if (parameterName === 'blur') {
advancedBloomFilter.blur = value;
} else if (parameterName === 'quality') {
advancedBloomFilter.quality = value;
} else if (parameterName === 'padding') {
advancedBloomFilter.padding = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'AdvancedBloom',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const advancedBloomFilter = new PIXI.filters.AdvancedBloomFilter();
return advancedBloomFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const advancedBloomFilter = (filter as unknown) as PIXI.filters.AdvancedBloomFilter;
if (parameterName === 'threshold') {
advancedBloomFilter.threshold = value;
} else if (parameterName === 'bloomScale') {
advancedBloomFilter.bloomScale = value;
} else if (parameterName === 'brightness') {
advancedBloomFilter.brightness = value;
} else if (parameterName === 'blur') {
advancedBloomFilter.blur = value;
} else if (parameterName === 'quality') {
advancedBloomFilter.quality = value;
} else if (parameterName === 'padding') {
advancedBloomFilter.padding = value;
}
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,17 +1,20 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('Ascii', {
makePIXIFilter: function (target, effectData) {
const asciiFilter = new PIXI.filters.AsciiFilter();
return asciiFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter;
if (parameterName === 'size') {
asciiFilter.size = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'Ascii',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const asciiFilter = new PIXI.filters.AsciiFilter();
return asciiFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const asciiFilter = (filter as unknown) as PIXI.filters.AsciiFilter;
if (parameterName === 'size') {
asciiFilter.size = value;
}
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,38 +1,41 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('Bevel', {
makePIXIFilter: function (target, effectData) {
const bevelFilter = new PIXI.filters.BevelFilter();
return bevelFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
if (parameterName === 'rotation') {
bevelFilter.rotation = value;
} else if (parameterName === 'thickness') {
bevelFilter.thickness = value;
} else if (parameterName === 'distance') {
// @ts-ignore
bevelFilter.distance = value;
} else if (parameterName === 'lightAlpha') {
bevelFilter.lightAlpha = value;
} else if (parameterName === 'shadowAlpha') {
bevelFilter.shadowAlpha = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'Bevel',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const bevelFilter = new PIXI.filters.BevelFilter();
return bevelFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
if (parameterName === 'lightColor') {
bevelFilter.lightColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
if (parameterName === 'rotation') {
bevelFilter.rotation = value;
} else if (parameterName === 'thickness') {
bevelFilter.thickness = value;
} else if (parameterName === 'distance') {
// @ts-ignore
bevelFilter.distance = value;
} else if (parameterName === 'lightAlpha') {
bevelFilter.lightAlpha = value;
} else if (parameterName === 'shadowAlpha') {
bevelFilter.shadowAlpha = value;
}
}
if (parameterName === 'shadowColor') {
bevelFilter.shadowColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
updateStringParameter(filter, parameterName, value) {
const bevelFilter = (filter as unknown) as PIXI.filters.BevelFilter;
if (parameterName === 'lightColor') {
bevelFilter.lightColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
}
if (parameterName === 'shadowColor') {
bevelFilter.shadowColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
}
}
},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,21 +1,24 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('BlackAndWhite', {
makePIXIFilter: function (target, effectData) {
const colorMatrix = new PIXI.filters.ColorMatrixFilter();
colorMatrix.blackAndWhite(false);
return colorMatrix;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const colorMatrix = (filter as unknown) as PIXI.filters.ColorMatrixFilter;
if (parameterName !== 'opacity') {
return;
gdjs.PixiFiltersTools.registerFilterCreator(
'BlackAndWhite',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const colorMatrix = new PIXI.filters.ColorMatrixFilter();
colorMatrix.blackAndWhite(false);
return colorMatrix;
}
colorMatrix.alpha = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const colorMatrix = (filter as unknown) as PIXI.filters.ColorMatrixFilter;
if (parameterName !== 'opacity') {
return;
}
colorMatrix.alpha = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,21 +1,24 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('BlendingMode', {
makePIXIFilter: function (target, effectData) {
const blendingModeFilter = new PIXI.filters.AlphaFilter();
return blendingModeFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const blendingModeFilter = (filter as unknown) as PIXI.filters.AlphaFilter;
if (parameterName === 'alpha') {
blendingModeFilter.alpha = value;
} else if (parameterName === 'blendmode') {
blendingModeFilter.blendMode = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'BlendingMode',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const blendingModeFilter = new PIXI.filters.AlphaFilter();
return blendingModeFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const blendingModeFilter = (filter as unknown) as PIXI.filters.AlphaFilter;
if (parameterName === 'alpha') {
blendingModeFilter.alpha = value;
} else if (parameterName === 'blendmode') {
blendingModeFilter.blendMode = value;
}
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,26 +1,29 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('Blur', {
makePIXIFilter: function (target, effectData) {
const blur = new PIXI.filters.BlurFilter();
return blur;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
if (
parameterName !== 'blur' &&
parameterName !== 'quality' &&
parameterName !== 'kernelSize' &&
parameterName !== 'resolution'
) {
return;
gdjs.PixiFiltersTools.registerFilterCreator(
'Blur',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const blur = new PIXI.filters.BlurFilter();
return blur;
}
if (parameterName === 'kernelSize') {
value = gdjs.PixiFiltersTools.clampKernelSize(value, 5, 15);
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
if (
parameterName !== 'blur' &&
parameterName !== 'quality' &&
parameterName !== 'kernelSize' &&
parameterName !== 'resolution'
) {
return;
}
if (parameterName === 'kernelSize') {
value = gdjs.PixiFiltersTools.clampKernelSize(value, 5, 15);
}
filter[parameterName] = value;
}
filter[parameterName] = value;
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,24 +1,27 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('Brightness', {
makePIXIFilter: function (target, effectData) {
const brightness = new PIXI.filters.ColorMatrixFilter();
brightness.brightness(1, false);
return brightness;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const brightnessFilter = (filter as unknown) as PIXI.filters.ColorMatrixFilter;
if (parameterName !== 'brightness') {
return;
gdjs.PixiFiltersTools.registerFilterCreator(
'Brightness',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const brightness = new PIXI.filters.ColorMatrixFilter();
brightness.brightness(1, false);
return brightness;
}
brightnessFilter.brightness(
gdjs.PixiFiltersTools.clampValue(value, 0, 1),
false
);
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const brightnessFilter = (filter as unknown) as PIXI.filters.ColorMatrixFilter;
if (parameterName !== 'brightness') {
return;
}
brightnessFilter.brightness(
gdjs.PixiFiltersTools.clampValue(value, 0, 1),
false
);
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,27 +1,30 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('BulgePinch', {
makePIXIFilter: function (target, effectData) {
const bulgePinchFilter = new PIXI.filters.BulgePinchFilter();
return bulgePinchFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter;
if (parameterName === 'centerX') {
bulgePinchFilter.center[0] = value;
} else if (parameterName === 'centerY') {
bulgePinchFilter.center[1] = value;
} else if (parameterName === 'radius') {
bulgePinchFilter.radius = value;
} else if (parameterName === 'strength') {
bulgePinchFilter.strength = gdjs.PixiFiltersTools.clampValue(
value,
-1,
1
);
gdjs.PixiFiltersTools.registerFilterCreator(
'BulgePinch',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const bulgePinchFilter = new PIXI.filters.BulgePinchFilter();
return bulgePinchFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const bulgePinchFilter = (filter as unknown) as PIXI.filters.BulgePinchFilter;
if (parameterName === 'centerX') {
bulgePinchFilter.center[0] = value;
} else if (parameterName === 'centerY') {
bulgePinchFilter.center[1] = value;
} else if (parameterName === 'radius') {
bulgePinchFilter.radius = value;
} else if (parameterName === 'strength') {
bulgePinchFilter.strength = gdjs.PixiFiltersTools.clampValue(
value,
-1,
1
);
}
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,39 +1,42 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('ColorMap', {
makePIXIFilter: function (target, effectData) {
const colorMapTexture = target
.getRuntimeScene()
.getGame()
.getImageManager()
.getPIXITexture(effectData.stringParameters.colorMapTexture);
const colorMapFilter = new PIXI.filters.ColorMapFilter(
colorMapTexture,
effectData.booleanParameters.nearest,
gdjs.PixiFiltersTools.clampValue(
effectData.doubleParameters.mix / 100,
0,
1
)
);
return colorMapFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
if (parameterName === 'mix') {
colorMapFilter.mix = gdjs.PixiFiltersTools.clampValue(
value / 100,
0,
1
gdjs.PixiFiltersTools.registerFilterCreator(
'ColorMap',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const colorMapTexture = target
.getRuntimeScene()
.getGame()
.getImageManager()
.getPIXITexture(effectData.stringParameters.colorMapTexture);
const colorMapFilter = new PIXI.filters.ColorMapFilter(
colorMapTexture,
effectData.booleanParameters.nearest,
gdjs.PixiFiltersTools.clampValue(
effectData.doubleParameters.mix / 100,
0,
1
)
);
return colorMapFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
if (parameterName === 'nearest') {
colorMapFilter.nearest = value;
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
if (parameterName === 'mix') {
colorMapFilter.mix = gdjs.PixiFiltersTools.clampValue(
value / 100,
0,
1
);
}
}
},
});
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {
const colorMapFilter = (filter as unknown) as PIXI.filters.ColorMapFilter;
if (parameterName === 'nearest') {
colorMapFilter.nearest = value;
}
}
})()
);
}

View File

@@ -1,28 +1,31 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('ColorReplace', {
makePIXIFilter: function (target, effectData) {
const colorReplaceFilter = new PIXI.filters.ColorReplaceFilter();
return colorReplaceFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter;
if (parameterName === 'epsilon') {
colorReplaceFilter.epsilon = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'ColorReplace',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const colorReplaceFilter = new PIXI.filters.ColorReplaceFilter();
return colorReplaceFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter;
if (parameterName === 'originalColor') {
colorReplaceFilter.originalColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
} else if (parameterName === 'newColor') {
colorReplaceFilter.newColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter;
if (parameterName === 'epsilon') {
colorReplaceFilter.epsilon = value;
}
}
},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updateStringParameter(filter, parameterName, value) {
const colorReplaceFilter = (filter as unknown) as PIXI.filters.ColorReplaceFilter;
if (parameterName === 'originalColor') {
colorReplaceFilter.originalColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
} else if (parameterName === 'newColor') {
colorReplaceFilter.newColor = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
}
}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,56 +1,59 @@
// @ts-nocheck - TODO: fix typings in this file
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('CRT', {
makePIXIFilter: function (layer, effectData) {
const crtFilter = new PIXI.filters.CRTFilter();
crtFilter._animationTimer = 0;
return crtFilter;
},
updatePreRender: function (filter, target) {
if (filter.animationSpeed !== 0) {
// Multiply by 10 so that the default value is a sensible speed
filter.time +=
(target.getElapsedTime() / 1000) * 10 * filter.animationSpeed;
gdjs.PixiFiltersTools.registerFilterCreator(
'CRT',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
const crtFilter = new PIXI.filters.CRTFilter();
crtFilter._animationTimer = 0;
return crtFilter;
}
if (filter.animationFrequency !== 0) {
filter._animationTimer += target.getElapsedTime() / 1000;
if (filter._animationTimer >= 1 / filter.animationFrequency) {
filter.seed = Math.random();
filter._animationTimer = 0;
updatePreRender(filter, target) {
if (filter.animationSpeed !== 0) {
// Multiply by 10 so that the default value is a sensible speed
filter.time +=
(target.getElapsedTime() / 1000) * 10 * filter.animationSpeed;
}
if (filter.animationFrequency !== 0) {
filter._animationTimer += target.getElapsedTime() / 1000;
if (filter._animationTimer >= 1 / filter.animationFrequency) {
filter.seed = Math.random();
filter._animationTimer = 0;
}
}
}
},
updateDoubleParameter: function (filter, parameterName, value) {
if (parameterName === 'lineWidth') {
filter.lineWidth = value;
} else if (parameterName === 'lineContrast') {
filter.lineContrast = value;
} else if (parameterName === 'noise') {
filter.noise = value;
} else if (parameterName === 'curvature') {
filter.curvature = value;
} else if (parameterName === 'noiseSize') {
filter.noiseSize = value;
} else if (parameterName === 'vignetting') {
filter.vignetting = value;
} else if (parameterName === 'vignettingAlpha') {
filter.vignettingAlpha = value;
} else if (parameterName === 'vignettingBlur') {
filter.vignettingBlur = value;
} else if (parameterName === 'animationSpeed') {
filter.animationSpeed = value;
} else if (parameterName === 'animationFrequency') {
filter.animationFrequency = value;
} else if (parameterName === 'padding') {
filter.padding = value;
updateDoubleParameter(filter, parameterName, value) {
if (parameterName === 'lineWidth') {
filter.lineWidth = value;
} else if (parameterName === 'lineContrast') {
filter.lineContrast = value;
} else if (parameterName === 'noise') {
filter.noise = value;
} else if (parameterName === 'curvature') {
filter.curvature = value;
} else if (parameterName === 'noiseSize') {
filter.noiseSize = value;
} else if (parameterName === 'vignetting') {
filter.vignetting = value;
} else if (parameterName === 'vignettingAlpha') {
filter.vignettingAlpha = value;
} else if (parameterName === 'vignettingBlur') {
filter.vignettingBlur = value;
} else if (parameterName === 'animationSpeed') {
filter.animationSpeed = value;
} else if (parameterName === 'animationFrequency') {
filter.animationFrequency = value;
} else if (parameterName === 'padding') {
filter.padding = value;
}
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {
if (parameterName === 'verticalLine') {
filter.verticalLine = value;
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {
if (parameterName === 'verticalLine') {
filter.verticalLine = value;
}
}
},
});
})()
);
}

View File

@@ -1,31 +1,34 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('Displacement', {
makePIXIFilter: function (target, effectData) {
const displacementMapTexture = target
.getRuntimeScene()
.getGame()
.getImageManager()
.getPIXITexture(effectData.stringParameters.displacementMapTexture);
displacementMapTexture.baseTexture.wrapMode = PIXI.WRAP_MODES.REPEAT;
const displacementSprite = new PIXI.Sprite(displacementMapTexture);
const displacementFilter = new PIXI.filters.DisplacementFilter(
displacementSprite
);
return displacementFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const displacementFilter = (filter as unknown) as PIXI.filters.DisplacementFilter;
if (parameterName === 'scaleX') {
displacementFilter.scale.x = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'Displacement',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const displacementMapTexture = target
.getRuntimeScene()
.getGame()
.getImageManager()
.getPIXITexture(effectData.stringParameters.displacementMapTexture);
displacementMapTexture.baseTexture.wrapMode = PIXI.WRAP_MODES.REPEAT;
const displacementSprite = new PIXI.Sprite(displacementMapTexture);
const displacementFilter = new PIXI.filters.DisplacementFilter(
displacementSprite
);
return displacementFilter;
}
if (parameterName === 'scaleY') {
displacementFilter.scale.y = value;
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const displacementFilter = (filter as unknown) as PIXI.filters.DisplacementFilter;
if (parameterName === 'scaleX') {
displacementFilter.scale.x = value;
}
if (parameterName === 'scaleY') {
displacementFilter.scale.y = value;
}
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,19 +1,22 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('Dot', {
makePIXIFilter: function (target, effectData) {
const dotFilter = new PIXI.filters.DotFilter();
return dotFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const dotFilter = (filter as unknown) as PIXI.filters.DotFilter;
if (parameterName === 'scale') {
dotFilter.scale = value;
} else if (parameterName === 'angle') {
dotFilter.angle = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'Dot',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const dotFilter = new PIXI.filters.DotFilter();
return dotFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const dotFilter = (filter as unknown) as PIXI.filters.DotFilter;
if (parameterName === 'scale') {
dotFilter.scale = value;
} else if (parameterName === 'angle') {
dotFilter.angle = value;
}
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,39 +1,42 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('DropShadow', {
makePIXIFilter: function (target, effectData) {
const dropShadowFilter = new PIXI.filters.DropShadowFilter();
return dropShadowFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'blur') {
dropShadowFilter.blur = value;
} else if (parameterName === 'quality') {
dropShadowFilter.quality = value;
} else if (parameterName === 'alpha') {
dropShadowFilter.alpha = value;
} else if (parameterName === 'distance') {
dropShadowFilter.distance = value;
} else if (parameterName === 'rotation') {
dropShadowFilter.rotation = value;
} else if (parameterName === 'padding') {
dropShadowFilter.padding = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'DropShadow',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const dropShadowFilter = new PIXI.filters.DropShadowFilter();
return dropShadowFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'color') {
dropShadowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'blur') {
dropShadowFilter.blur = value;
} else if (parameterName === 'quality') {
dropShadowFilter.quality = value;
} else if (parameterName === 'alpha') {
dropShadowFilter.alpha = value;
} else if (parameterName === 'distance') {
dropShadowFilter.distance = value;
} else if (parameterName === 'rotation') {
dropShadowFilter.rotation = value;
} else if (parameterName === 'padding') {
dropShadowFilter.padding = value;
}
}
},
updateBooleanParameter: function (filter, parameterName, value) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'shadowOnly') {
dropShadowFilter.shadowOnly = value;
updateStringParameter(filter, parameterName, value) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'color') {
dropShadowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
}
}
},
});
updateBooleanParameter(filter, parameterName, value) {
const dropShadowFilter = (filter as unknown) as PIXI.filters.DropShadowFilter;
if (parameterName === 'shadowOnly') {
dropShadowFilter.shadowOnly = value;
}
}
})()
);
}

View File

@@ -1,55 +1,58 @@
// @ts-nocheck - TODO: fix typings in this file
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('Glitch', {
makePIXIFilter: function (layer, effectData) {
const glitchFilter = new PIXI.filters.GlitchFilter();
glitchFilter._animationTimer = 0;
return glitchFilter;
},
updatePreRender: function (filter, target) {
if (filter.animationFrequency !== 0) {
filter._animationTimer += target.getElapsedTime() / 1000;
if (filter._animationTimer >= 1 / filter.animationFrequency) {
filter.seed = Math.random();
filter._animationTimer = 0;
gdjs.PixiFiltersTools.registerFilterCreator(
'Glitch',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
const glitchFilter = new PIXI.filters.GlitchFilter();
glitchFilter._animationTimer = 0;
return glitchFilter;
}
updatePreRender(filter, target) {
if (filter.animationFrequency !== 0) {
filter._animationTimer += target.getElapsedTime() / 1000;
if (filter._animationTimer >= 1 / filter.animationFrequency) {
filter.seed = Math.random();
filter._animationTimer = 0;
}
}
}
},
updateDoubleParameter: function (filter, parameterName, value) {
if (parameterName === 'slices') {
filter.slices = value;
} else if (parameterName === 'offset') {
filter.offset = value;
} else if (parameterName === 'direction') {
filter.direction = value;
} else if (parameterName === 'fillMode') {
filter.fillMode = value;
} else if (parameterName === 'minSize') {
filter.minSize = value;
} else if (parameterName === 'sampleSize') {
filter.sampleSize = value;
} else if (parameterName === 'redX') {
filter.red.x = value;
} else if (parameterName === 'redY') {
filter.red.y = value;
} else if (parameterName === 'greenX') {
filter.green.x = value;
} else if (parameterName === 'greenY') {
filter.green.y = value;
} else if (parameterName === 'blueX') {
filter.blue.x = value;
} else if (parameterName === 'blueY') {
filter.blue.y = value;
} else if (parameterName === 'animationFrequency') {
filter.animationFrequency = value;
updateDoubleParameter(filter, parameterName, value) {
if (parameterName === 'slices') {
filter.slices = value;
} else if (parameterName === 'offset') {
filter.offset = value;
} else if (parameterName === 'direction') {
filter.direction = value;
} else if (parameterName === 'fillMode') {
filter.fillMode = value;
} else if (parameterName === 'minSize') {
filter.minSize = value;
} else if (parameterName === 'sampleSize') {
filter.sampleSize = value;
} else if (parameterName === 'redX') {
filter.red.x = value;
} else if (parameterName === 'redY') {
filter.red.y = value;
} else if (parameterName === 'greenX') {
filter.green.x = value;
} else if (parameterName === 'greenY') {
filter.green.y = value;
} else if (parameterName === 'blueX') {
filter.blue.x = value;
} else if (parameterName === 'blueY') {
filter.blue.y = value;
} else if (parameterName === 'animationFrequency') {
filter.animationFrequency = value;
}
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {
if (parameterName === 'average') {
filter.average = value;
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {
if (parameterName === 'average') {
filter.average = value;
}
}
},
});
})()
);
}

View File

@@ -1,27 +1,30 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('Glow', {
makePIXIFilter: function (target, effectData) {
const glowFilter = new PIXI.filters.GlowFilter();
return glowFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter;
if (parameterName === 'innerStrength') {
glowFilter.innerStrength = value;
} else if (parameterName === 'outerStrength') {
glowFilter.outerStrength = value;
} else if (parameterName === 'distance') {
// @ts-ignore
glowFilter.distance = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'Glow',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const glowFilter = new PIXI.filters.GlowFilter();
return glowFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter;
if (parameterName === 'color') {
glowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value);
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter;
if (parameterName === 'innerStrength') {
glowFilter.innerStrength = value;
} else if (parameterName === 'outerStrength') {
glowFilter.outerStrength = value;
} else if (parameterName === 'distance') {
// @ts-ignore
glowFilter.distance = value;
}
}
},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updateStringParameter(filter, parameterName, value) {
const glowFilter = (filter as unknown) as PIXI.filters.GlowFilter;
if (parameterName === 'color') {
glowFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value);
}
}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,40 +1,44 @@
// @ts-nocheck - TODO: fix typings in this file
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('Godray', {
makePIXIFilter: function (layer, effectData) {
const godrayFilter = new PIXI.filters.GodrayFilter();
return godrayFilter;
},
updatePreRender: function (filter, target) {
if (filter.animationSpeed !== 0) {
filter.time += (target.getElapsedTime() / 1000) * filter.animationSpeed;
gdjs.PixiFiltersTools.registerFilterCreator(
'Godray',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
const godrayFilter = new PIXI.filters.GodrayFilter();
return godrayFilter;
}
},
updateDoubleParameter: function (filter, parameterName, value) {
if (parameterName === 'lacunarity') {
filter.lacunarity = value;
} else if (parameterName === 'angle') {
filter.angle = value;
} else if (parameterName === 'gain') {
filter.gain = value;
} else if (parameterName === 'light') {
filter.light = value;
} else if (parameterName === 'x') {
filter.x = value;
} else if (parameterName === 'y') {
filter.y = value;
} else if (parameterName === 'animationSpeed') {
filter.animationSpeed = value;
} else if (parameterName === 'padding') {
filter.padding = value;
updatePreRender(filter, target) {
if (filter.animationSpeed !== 0) {
filter.time +=
(target.getElapsedTime() / 1000) * filter.animationSpeed;
}
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {
if (parameterName === 'parallel') {
filter.parallel = value;
updateDoubleParameter(filter, parameterName, value) {
if (parameterName === 'lacunarity') {
filter.lacunarity = value;
} else if (parameterName === 'angle') {
filter.angle = value;
} else if (parameterName === 'gain') {
filter.gain = value;
} else if (parameterName === 'light') {
filter.light = value;
} else if (parameterName === 'x') {
filter.x = value;
} else if (parameterName === 'y') {
filter.y = value;
} else if (parameterName === 'animationSpeed') {
filter.animationSpeed = value;
} else if (parameterName === 'padding') {
filter.padding = value;
}
}
},
});
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {
if (parameterName === 'parallel') {
filter.parallel = value;
}
}
})()
);
}

View File

@@ -1,25 +1,28 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('KawaseBlur', {
makePIXIFilter: function (target, effectData) {
const kawaseBlurFilter = new PIXI.filters.KawaseBlurFilter();
return kawaseBlurFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter;
if (parameterName === 'pixelizeX') {
// @ts-ignore: fix these wrong parameters
kawaseBlurFilter.pixelizeX = value;
} else if (parameterName === 'pixelizeY') {
// @ts-ignore: fix these wrong parameters
kawaseBlurFilter.pixelizeY = value;
} else if (parameterName === 'blur') {
kawaseBlurFilter.blur = value;
} else if (parameterName === 'quality') {
kawaseBlurFilter.quality = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'KawaseBlur',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const kawaseBlurFilter = new PIXI.filters.KawaseBlurFilter();
return kawaseBlurFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const kawaseBlurFilter = (filter as unknown) as PIXI.filters.KawaseBlurFilter;
if (parameterName === 'pixelizeX') {
// @ts-ignore: fix these wrong parameters
kawaseBlurFilter.pixelizeX = value;
} else if (parameterName === 'pixelizeY') {
// @ts-ignore: fix these wrong parameters
kawaseBlurFilter.pixelizeY = value;
} else if (parameterName === 'blur') {
kawaseBlurFilter.blur = value;
} else if (parameterName === 'quality') {
kawaseBlurFilter.quality = value;
}
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -22,19 +22,22 @@ namespace gdjs {
}
}
LightNightPixiFilter.prototype.constructor = gdjs.LightNightPixiFilter;
gdjs.PixiFiltersTools.registerFilterCreator('LightNight', {
makePIXIFilter: function (target, effectData) {
const filter = new gdjs.LightNightPixiFilter();
return filter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
if (parameterName !== 'opacity') {
return;
gdjs.PixiFiltersTools.registerFilterCreator(
'LightNight',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const filter = new gdjs.LightNightPixiFilter();
return filter;
}
filter.uniforms.opacity = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
if (parameterName !== 'opacity') {
return;
}
filter.uniforms.opacity = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -26,23 +26,26 @@ namespace gdjs {
}
}
NightPixiFilter.prototype.constructor = gdjs.NightPixiFilter;
gdjs.PixiFiltersTools.registerFilterCreator('Night', {
makePIXIFilter: function (target, effectData) {
const filter = new gdjs.NightPixiFilter();
return filter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
if (parameterName !== 'intensity' && parameterName !== 'opacity') {
return;
gdjs.PixiFiltersTools.registerFilterCreator(
'Night',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const filter = new gdjs.NightPixiFilter();
return filter;
}
filter.uniforms[parameterName] = gdjs.PixiFiltersTools.clampValue(
value,
0,
1
);
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
if (parameterName !== 'intensity' && parameterName !== 'opacity') {
return;
}
filter.uniforms[parameterName] = gdjs.PixiFiltersTools.clampValue(
value,
0,
1
);
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,20 +1,23 @@
namespace gdjs {
import PIXI = GlobalPIXIModule.PIXI;
gdjs.PixiFiltersTools.registerFilterCreator('Noise', {
makePIXIFilter: function (target, effectData) {
const noiseFilter = new PIXI.filters.NoiseFilter();
return noiseFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const noiseFilter = (filter as unknown) as PIXI.filters.NoiseFilter;
if (parameterName !== 'noise') {
return;
gdjs.PixiFiltersTools.registerFilterCreator(
'Noise',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const noiseFilter = new PIXI.filters.NoiseFilter();
return noiseFilter;
}
noiseFilter.noise = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
// @ts-ignore - unsure why PIXI.filters is not recognised.
const noiseFilter = (filter as unknown) as PIXI.filters.NoiseFilter;
if (parameterName !== 'noise') {
return;
}
noiseFilter.noise = gdjs.PixiFiltersTools.clampValue(value, 0, 1);
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,45 +1,48 @@
// @ts-nocheck - TODO: fix typings in this file
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('OldFilm', {
makePIXIFilter: function (layer, effectData) {
const oldFilmFilter = new PIXI.filters.OldFilmFilter();
oldFilmFilter._animationTimer = 0;
return oldFilmFilter;
},
updatePreRender: function (filter, target) {
if (filter.animationFrequency !== 0) {
filter._animationTimer += target.getElapsedTime() / 1000;
if (filter._animationTimer >= 1 / filter.animationFrequency) {
filter.seed = Math.random();
filter._animationTimer = 0;
gdjs.PixiFiltersTools.registerFilterCreator(
'OldFilm',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(layer, effectData) {
const oldFilmFilter = new PIXI.filters.OldFilmFilter();
oldFilmFilter._animationTimer = 0;
return oldFilmFilter;
}
updatePreRender(filter, target) {
if (filter.animationFrequency !== 0) {
filter._animationTimer += target.getElapsedTime() / 1000;
if (filter._animationTimer >= 1 / filter.animationFrequency) {
filter.seed = Math.random();
filter._animationTimer = 0;
}
}
}
},
updateDoubleParameter: function (filter, parameterName, value) {
if (parameterName === 'sepia') {
filter.sepia = value;
} else if (parameterName === 'noise') {
filter.noise = value;
} else if (parameterName === 'noiseSize') {
filter.noiseSize = value;
} else if (parameterName === 'scratch') {
filter.scratch = value;
} else if (parameterName === 'scratchDensity') {
filter.scratchDensity = value;
} else if (parameterName === 'scratchWidth') {
filter.scratchWidth = value;
} else if (parameterName === 'vignetting') {
filter.vignetting = value;
} else if (parameterName === 'vignettingAlpha') {
filter.vignettingAlpha = value;
} else if (parameterName === 'vignettingBlur') {
filter.vignettingBlur = value;
} else if (parameterName === 'animationFrequency') {
filter.animationFrequency = value;
updateDoubleParameter(filter, parameterName, value) {
if (parameterName === 'sepia') {
filter.sepia = value;
} else if (parameterName === 'noise') {
filter.noise = value;
} else if (parameterName === 'noiseSize') {
filter.noiseSize = value;
} else if (parameterName === 'scratch') {
filter.scratch = value;
} else if (parameterName === 'scratchDensity') {
filter.scratchDensity = value;
} else if (parameterName === 'scratchWidth') {
filter.scratchWidth = value;
} else if (parameterName === 'vignetting') {
filter.vignetting = value;
} else if (parameterName === 'vignettingAlpha') {
filter.vignettingAlpha = value;
} else if (parameterName === 'vignettingBlur') {
filter.vignettingBlur = value;
} else if (parameterName === 'animationFrequency') {
filter.animationFrequency = value;
}
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,24 +1,29 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('Outline', {
makePIXIFilter: function (target, effectData) {
const outlineFilter = new PIXI.filters.OutlineFilter();
return outlineFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
if (parameterName === 'thickness') {
outlineFilter.thickness = value;
} else if (parameterName === 'padding') {
outlineFilter.padding = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'Outline',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const outlineFilter = new PIXI.filters.OutlineFilter();
return outlineFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
if (parameterName === 'color') {
outlineFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(value);
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
if (parameterName === 'thickness') {
outlineFilter.thickness = value;
} else if (parameterName === 'padding') {
outlineFilter.padding = value;
}
}
},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updateStringParameter(filter, parameterName, value) {
const outlineFilter = (filter as unknown) as PIXI.filters.OutlineFilter;
if (parameterName === 'color') {
outlineFilter.color = gdjs.PixiFiltersTools.rgbOrHexToHexNumber(
value
);
}
}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

View File

@@ -1,19 +1,22 @@
namespace gdjs {
gdjs.PixiFiltersTools.registerFilterCreator('Pixelate', {
makePIXIFilter: function (target, effectData) {
const pixelateFilter = new PIXI.filters.PixelateFilter(
effectData.doubleParameters.size
);
return pixelateFilter;
},
updatePreRender: function (filter, target) {},
updateDoubleParameter: function (filter, parameterName, value) {
const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter;
if (parameterName === 'size') {
pixelateFilter.size = value;
gdjs.PixiFiltersTools.registerFilterCreator(
'Pixelate',
new (class extends gdjs.PixiFiltersTools.PixiFilterCreator {
makePIXIFilter(target, effectData) {
const pixelateFilter = new PIXI.filters.PixelateFilter(
effectData.doubleParameters.size
);
return pixelateFilter;
}
},
updateStringParameter: function (filter, parameterName, value) {},
updateBooleanParameter: function (filter, parameterName, value) {},
});
updatePreRender(filter, target) {}
updateDoubleParameter(filter, parameterName, value) {
const pixelateFilter = (filter as unknown) as PIXI.filters.PixelateFilter;
if (parameterName === 'size') {
pixelateFilter.size = value;
}
}
updateStringParameter(filter, parameterName, value) {}
updateBooleanParameter(filter, parameterName, value) {}
})()
);
}

Some files were not shown because too many files have changed in this diff Show More