From c3244e21b9a948d7e95e8fc80c07fb56c56e4d6c Mon Sep 17 00:00:00 2001 From: Relintai Date: Sun, 8 Oct 2023 14:46:30 +0200 Subject: [PATCH] Added the world generator addons from Broken Seals. --- .gitignore | 27 + LICENSE | 19 + README.md | 394 +++++++++++++ addons/world_generator/plugin.cfg | 7 + addons/world_generator/plugin.gd | 77 +++ .../raycast/world_gen_raycast.gd | 32 ++ addons/world_generator/resources/continent.gd | 38 ++ .../resources/sub_zone_prop.gd | 7 + addons/world_generator/resources/subzone.gd | 38 ++ .../resources/world_gen_base_resource.gd | 219 +++++++ .../resources/world_gen_world.gd | 62 ++ .../resources/world_generator_settings.gd | 104 ++++ addons/world_generator/resources/zone.gd | 38 ++ addons/world_generator/test/test_world.tres | 351 ++++++++++++ addons/world_generator/ui/DataList.gd | 201 +++++++ addons/world_generator/ui/DataList.tscn | 57 ++ addons/world_generator/ui/MainScreen.gd | 73 +++ addons/world_generator/ui/MainScreen.tscn | 44 ++ addons/world_generator/ui/RectEditor.gd | 34 ++ addons/world_generator/ui/RectEditor.tscn | 48 ++ addons/world_generator/ui/RectView.gd | 139 +++++ addons/world_generator/ui/RectViewNode.gd | 232 ++++++++ addons/world_generator/ui/RectViewNode.tscn | 11 + .../ui/ResourcePropertyList.gd | 540 ++++++++++++++++++ .../ui/ResourcePropertyList.tscn | 81 +++ addons/world_generator/ui/WorldTab.gd | 29 + addons/world_generator/ui/tabs/Continent.gd | 78 +++ addons/world_generator/ui/tabs/Continent.tscn | 100 ++++ addons/world_generator/ui/tabs/SubZone.gd | 168 ++++++ addons/world_generator/ui/tabs/SubZone.tscn | 107 ++++ addons/world_generator/ui/tabs/SubZoneProp.gd | 204 +++++++ .../world_generator/ui/tabs/SubZoneProp.tscn | 51 ++ addons/world_generator/ui/tabs/World.tscn | 79 +++ addons/world_generator/ui/tabs/Zone.gd | 122 ++++ addons/world_generator/ui/tabs/Zone.tscn | 105 ++++ .../widgets/EditorResourceWidget.gd | 80 +++ .../widgets/EditorResourceWidget.tscn | 23 + .../widgets/EditorZoomWidget.gd | 220 +++++++ .../widgets/EditorZoomWidget.tscn | 8 + default_env.tres | 7 + icon.png | Bin 0 -> 3218 bytes icon.png.import | 35 ++ project.pandemonium | 76 +++ 43 files changed, 4365 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 addons/world_generator/plugin.cfg create mode 100644 addons/world_generator/plugin.gd create mode 100644 addons/world_generator/raycast/world_gen_raycast.gd create mode 100644 addons/world_generator/resources/continent.gd create mode 100644 addons/world_generator/resources/sub_zone_prop.gd create mode 100644 addons/world_generator/resources/subzone.gd create mode 100644 addons/world_generator/resources/world_gen_base_resource.gd create mode 100644 addons/world_generator/resources/world_gen_world.gd create mode 100644 addons/world_generator/resources/world_generator_settings.gd create mode 100644 addons/world_generator/resources/zone.gd create mode 100644 addons/world_generator/test/test_world.tres create mode 100644 addons/world_generator/ui/DataList.gd create mode 100644 addons/world_generator/ui/DataList.tscn create mode 100644 addons/world_generator/ui/MainScreen.gd create mode 100644 addons/world_generator/ui/MainScreen.tscn create mode 100644 addons/world_generator/ui/RectEditor.gd create mode 100644 addons/world_generator/ui/RectEditor.tscn create mode 100644 addons/world_generator/ui/RectView.gd create mode 100644 addons/world_generator/ui/RectViewNode.gd create mode 100644 addons/world_generator/ui/RectViewNode.tscn create mode 100644 addons/world_generator/ui/ResourcePropertyList.gd create mode 100644 addons/world_generator/ui/ResourcePropertyList.tscn create mode 100644 addons/world_generator/ui/WorldTab.gd create mode 100644 addons/world_generator/ui/tabs/Continent.gd create mode 100644 addons/world_generator/ui/tabs/Continent.tscn create mode 100644 addons/world_generator/ui/tabs/SubZone.gd create mode 100644 addons/world_generator/ui/tabs/SubZone.tscn create mode 100644 addons/world_generator/ui/tabs/SubZoneProp.gd create mode 100644 addons/world_generator/ui/tabs/SubZoneProp.tscn create mode 100644 addons/world_generator/ui/tabs/World.tscn create mode 100644 addons/world_generator/ui/tabs/Zone.gd create mode 100644 addons/world_generator/ui/tabs/Zone.tscn create mode 100644 addons/world_generator/widgets/EditorResourceWidget.gd create mode 100644 addons/world_generator/widgets/EditorResourceWidget.tscn create mode 100644 addons/world_generator/widgets/EditorZoomWidget.gd create mode 100644 addons/world_generator/widgets/EditorZoomWidget.tscn create mode 100644 default_env.tres create mode 100644 icon.png create mode 100644 icon.png.import create mode 100644 project.pandemonium diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60817db --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ + +*.d +*.o +*.meta +.import +.sconsign.dblite +.DS_Store + +export/** +release/** + +.vs/* +.kdev4/* +.vscode/* + +TestRWTextures + +_build/* +_binaries/* +game/android/build/* + +*.blend1 +.dir-locals.el + +build.config + +__pycache__/* \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a638113 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-Present Péter Magyar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +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 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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fba9e42 --- /dev/null +++ b/README.md @@ -0,0 +1,394 @@ +# Broken Seals + +A 3D third person RPG. With both multiplayer, and singleplayer capabilities. + +The main gameplay-loop goal is to create an experience with enough complexity and depth, that can rival the more old-school MMO- and action rpgs, because nowadays I feel like that is something that got lost. + +I want the game to run on every platform, but the game design is PC first. From the testing I've done this is not going to be an issue. + +This project now uses the ![Pandemonium Engine](https://github.com/Relintai/pandemonium_engine), which is my custom version of godot. + +The project also requires the presence of some of my engine modules, Pandemonium contains all of them. See [here](https://github.com/Relintai/pandemonium_engine/tree/master/modules). + +You can get binaries under the releases tab [here](https://github.com/Relintai/broken_seals/releases).\ +You can also try a live version running in the browser [here](https://relintai.github.io/broken_seals/). + +Unfortunately the web editor needs custom http headers to work and I can't set that up from github.io, so right now I don't have a hosted version of that. + +#### On Desktop + +![Broken Seals desktop](pictures/screenshot_desktop.jpg) + +#### On Touchscreens + +![Broken Seals on touchscreen](pictures/screenshot_touchscreen.jpg) + +## Status + +Currently I'm working content. + +Roadmap: + +- Have a few animal models that can be used for the starter zones. +- Have a few building models. +- Add in sounds. +- Have some ambient sounds as music. +- Set up a proper starter zone. +- Implement the naturalist's spells. +- Add a male model. +- Add more classes. +- Then start working on new zones. + +Some planned changes that will come sometime: + +- Add support for multiple worlds. (Just have to make use of the new World class in the engine. The scene tree (Entities and Entity Bodies) will need small changes.) +- Fix up multiplayer, and implement missing things (like animations for clients). + +## Features + +### Multiplayer + +Multiplayer was broken for quite a while, so currently it has a lot more bugs than it used to. +Most of these should be relativelty easy to fix. + +- Authoritative server implementation. (Except for movement. Can be done though, I'll do it eventually, but it's usual for these kind of games to not have authoritative movement.) +- You can create, and join to servers. +- Movement works. Although the animations are not yet implemented for client players. +- Targeting works. +- Spell casting works. Although auras have some issues for now. +- Inventory management should work. +- Stuff like vendors, trainers should all work. I think except for crafting (I think just some methods need to be marked as RPCs, and they might need to need to call eachother via rpcs). + +### Characters + +- Main and secondary stats all work, they are also customizable in the project settings. They also have built in maint stat -> secondary stat conversion support. +- Character resources all work and scriptable. (Like health, mana, speed, energy etc) Also you can dynamically add it and remove it from a character. +- Tab targeting support. +- Spells, and auras are fully scriptable, and networked. +- Actionbar, actionbar profiles support. +- Spell learning support. +- Talent support, with multiple talent trees. +- Loot. +- Equipment support including weapons, clothes, and also other attachments. Although starter gear support is not yet finished. +- Support for changing clothes via texture merging. +- Support for changing parts of models via clothes. Also support for alternative bodyparts. +- Character models are using merged meshes and textures, with generated LOD. (This is also multi threaded, with fallback when no threads are available.) This system is using MeshDataResources. +- Generic attach point support for the character skeletons. (Spell cast particle effects, spell effects, also the same system can be used for weapons.) +- Interaction support. +- Vendors, Trainers, Crafting. +- Fully scriptable ai support. +- Control over characters can be easily changed by the server, and the character bodies are scripted to act accordingly automatically. For example mind control effects can be implemented with this system. + +Missing: + +- Character customization support (like selecting hairtyles). It's mostly there, but some crucial features are still missing. +- Temporary character model change support. Think of polymorph effects, toys etc. Needs the character customization support first. + +### Terrain + +- Multi threaded chunk generation. It will even work when no threads are available, also in this case the work gets distributed over multiple frames (although one of the steps is a bit too long, so it still needs some tweaks). +- Prop system. You can serialize scenes into a more efficient format than scene files. These can contain lights, meshes, rooms and portals, etc. The system is scriptable. Scene hierarchies also work. +- PropInstance node, which can mesh and put props into the scene. +- Per chunk material support, even for props. +- Terraman: prop support, even with mesh (and texture) merging. If you look at the trees on the screenshot, they are merged together per chunk, and they all share the material, and shader. Also if you serialize lights into props, they are added as vertex lights. +- Terraman and PropInstance: Vertex light support. You can add vertex lights (via code, or the prop system), and the meshes will be colored with it. +- Terraman: Vertex colors are also used to achieve the main style. (RAO). +- Terraman: Vertex color based ambient occlusion support. +- Terraman and PropInstance: LOD support, even for prop meshes. +- Terraman and PropInstance: LOD generation is scriptable. +- Dungeon generation support via a separate prop based system. Dungeons are generated below ground, with teleporters to them. Also support for turning off the terrain engine while the player is inside a dungeon. +- Procedural world generation. [See here](#terrain-generation). + +### UI + +- Menu and game ui. +- Actionbars with keybind support. Also the icons that you drag to the actionbar actually gets merged into the ui's texture to reduce drawcalls. +- Character, talent, spellbook, crafting, inventory, loot, vendor, trainer windows are all implemented. +- Easily scriptable options dialog. +- The menu has full support for keyboard / controller navigation. (The game itself misses it at the moment though.) +- Touchscreen controls. It will also make the buttons bigger if it detects a touchscreen. +- Keybind window. +- About window. + +## Project overview + +As stated in the opening section the project uses a custom version of the godot engine. + +The project's workflow has been set up so you can easily compile this version for yourself if you want to. + +See the [Compiling](#compiling) section if you want to know how to do this. + +### Engine + +Currently the [Pandemonium Engine](https://github.com/Relintai/pandemonium_engine) is used as the engine for this project, +which is a heavily customized verison of the [Godot game engine](https://godotengine.org/). It's based on the 3.x branch. + +It contains my engine modules, and also it got significanlty slimmed down compared to vanilla Godot. + +For a more in-depth changelog see it's changelog file [here](https://github.com/Relintai/pandemonium_engine/blob/master/CHANGELOG.md). + +### Game + +The [/game](https://github.com/Relintai/broken_seals/tree/master/game) folder contains the game's code and assets. +This is the folder you are supposed to open in the editor. + +The game's folder structure should be (hopefully) mostly self explanatory. + +#### Game Modules + +I designed the game's code to be highly modular, so I created a [loader module](https://github.com/Relintai/broken_seals/tree/master/game/scripts/game_modules). \ +It will look for files named ` game_module.tres ` and call methods on them on certain events.\ +This system uses the [DataManager](https://github.com/Relintai/broken_seals/blob/master/game/scripts/game_modules/DataManager.gd) singleton. + +For example this is how the ui initializes itself: + +The player's [body](https://github.com/Relintai/broken_seals/blob/master/game/player/Body.gd) script requests the ui nodes from the [DataManager](https://github.com/Relintai/broken_seals/blob/master/game/scripts/game_modules/DataManager.gd) singleton (like ` var ui = DataManager.request_instance(DataManager.PLAYER_UI_INSTANCE) `). And then the data manager will instance it's ` player_ui ` scene, and call all module's ` on_request_instance ` methods, while also passing the newly instanced scene to them. + +These module scripts are also responsible for collecting all spells and then setting them into the ESS singleton, so they are actually castable. + +This is the [module](https://github.com/Relintai/broken_seals/blob/master/game/modules/entity_classes/naturalist/game_module.tres) for the naturalist, and it's [ResourceDB](https://github.com/Relintai/broken_seals/blob/master/game/modules/entity_classes/naturalist/resource_db.tres), which will be merged into a central resource db for ESS. + +Note that the module resources are sorted by their resource paths, so spells should always get the same id, on every platform every time. +This is to optimize networkd spell casts. + +Most of the game assets ended up under the modules folder [here](https://github.com/Relintai/broken_seals/tree/master/game/modules) for this reason, +however these might get moved, if I find a better arrangement. + +#### Terrain generation + +The terrain generation is now handled by the new [world_generator](https://github.com/Relintai/broken_seals/tree/master/game/addons/world_generator) +addon. + +Right now the terrain is only going to be pseudo-random, as generating proper connected worlds are kind of super difficult, +especially if you also have to mesh them in 3d. \ +I think this solution can be extended later to be able to do a full continent / world randomization / generation. + +For now the idea is that we have a World resource, this contains Continents, those zontain Zones, and those contain SubZones. + +The position and size is predetermined by the designer. And then when a chunk needs to be generated it gets put into this world, and then these generate it's data. + +- World does mostly nothing on it's own for now, except for holding continents. +- Continents handle things like oceans, and big mountains. +- Zones generate proper terrain, and add props. They need to blend into continents. +- SubZones can be used as spawners, prop spawners, or they can even do terrain modifications. + +So when a chunk needs to be generated, first the world gets it, then all continents which intersect with it's position, +then all zones which intersect with it's position, then all subzones which intersect with it's position. + +The editor contains an addon to help with editing the world. + +## Editing the game + +Grab the engine itself, and then open the project inside the `game` folder. + +After the initial import it might need an editor restart, however everything should work after that. + +If you want to use master, you will likely need to build the editor for it if the c++ side had breaking changes since the last release. + +## Compiling + +First make sure, that you have everything installed to be able to compile the engine. +See the [official docs for compiling Godot](https://docs.godotengine.org/en/3.4/development/compiling/index.html) for more info. +My setup/compile script uses the same tools, so you don't need to install anything else. + +Even though the project doesn't use godot anymore, their docs are still sufficient. + +Now let's clone this repository: + +``` git clone https://github.com/Relintai/broken_seals ``` + +cd into the new folder: + +``` cd broken_seals ``` + +Now let's run the project's setup script, by calling scons without arguments. + +``` scons ``` + +This will clone and setup the engine, and all of the required modules into a new `engine` folder inside the project, using http. + +(If you want to use the github's ssh links append `repository_type=ssh` like ``` scons repository_type=ssh ```) + +Once it is done you can compile the engine. + +To build the editor on windows with 4 threads run the following command: + +``` scons bew -j4 ``` + +To build the editor on linux with 4 threads run the following command: + +``` scons bel -j4 ``` + +I call this feature of the setup script build words. [See](#build-words). + +Once the build finishes you can find the editor executable inside the `./engine/bin/` folder. + +For convenience there is a provided `editor.sh`, or `editor.bat` for running it from the project's folder. +These will create a copy, so you can even compile while the editor is running. + +Alternatively if you don't want to use build words, you can also just go into the engine folder: + +``` cd engine ``` + +And compile godot as per the [official docs](https://docs.godotengine.org/en/latest/development/compiling/index.html). + +### Build words + +The project's setup script contains support for "build words". These can be used from the root of this project. + +For example to build the editor for windows with 4 threads you can use: + +``` scons bew -j4 ``` + +The first argument must start with b (build), then it needs to be followed by a few abbreviations (the order does not matters) + +The rest of the arguments will be passed directly to godot's scons script. + +#### Editor + +Append `e` to build with `tools=yes` a.k.a. the editor. + +``` scons bew -j4 ``` + +if you omit `e`, the system will build the export template for you. For example: + +``` scons bw -j4 ``` + +This will be the `release_debug` windows export template. + +#### Platform abbreviations + +`l`: linux \ +`w`: windows \ +`a`: android \ +`j`: Javascript \ +`i`: iphone (Not yet finished, use `build_ios.sh`, and `build_ios_release.sh`) \ +Mac OSX: Not yet finished, use `build_osx.sh` + +#### Target abbreviations + +By default the system builds in release_debug. + +Append `d` for debug, or `r` for release. + +``` scons bewd -j4 ``` + +build editor windows debug + +``` scons bwr -j4 ``` + +build windows release (this will build the windows release export template) + +#### Shared modules + +Note: This only works on linux! + +append `s` to the build string. + +Optionally you can also make the build system only build a target module, by appending one of these: + +`E`: Entity Spell System \ +`T`: Texture Packer \ +`V`: Voxelman \ +`W`: World Generator \ +`P`: Procedural Animations + +Example: + +``` scons belsE -j4 ``` + +build editor linux shared (Entity Spell System) with 4 threads + +Note: to easily run the editor you can use the `editor.sh` or `editor.bat` in the root of the project. + +#### Other + +Append `v` to pass the `vsproj=yes` parameter to the build script. This will generate Visual Studio project files.\ +Append `c` to pass the `compiledb=yes` parameter to the build script. This is a new feature in 3.x to have this disabled by default to lessen compile times. + +#### Postfixes + +There are a few postfixes for the build words. These are more complex options. You have to append them to your build word with an underscore. + +You can use as many as you want. + +For example: + +``` scons bel_slim_latomic -j4 ``` + +##### slim + +With this postfix you can build a slimmed down version of the engine. This disables quite a few unneeded modules. + +``` scons bel_slim -j4 ``` + +##### latomic + +If you get linker errors while building the game/editor about undefined referenced with atomic related functions you can use this postfix. +It will add the ` -latomic ` command line switch to the linker flags. + +I ran into this issue while building on a raspberry pi 4 with the x11 platform. It might be related to the recent reworks to threading. + +``` scons bel_latomic -j4 ``` + +##### strip + +Appends `debug_symbols=no` to the build command, which will strip the resulting binary from debug symbols. + +``` scons bel_strip -j4 ``` + +##### threads + +Appends `threads_enabled=yes` to the build command. Useful for building the editor for html. + +``` scons bej_threads -j4 ``` + +#### Scons cache, and sdk locations + +In order to use scons cache and to tell the build system where some of the required sdks are located you usually +have to use environment variables. Most of the time you might just want to add them globally, +howewer this is sometimes unfeasible (e.g. you don't have administrator access, or you just want to have +multiple sdk versions installed). + +In order to solve this a build config file was added. + +If you want to use the config simply rename the provided `build.config.example` to `build.config`, and customize +the settings inside. + +### Manual Setup + +If you you don't want to use the setup script (or just want to know what it actually does), +this section will explain how to set everything up manually. + +First clone the engine: + +``` git clone https://github.com/Relintai/pandemonium_engine ``` + +Now if you look at the [HEADS file](https://github.com/Relintai/broken_seals/blob/master/HEADS). + +It contains the commit hashes for that particular revision for every module and the engine. +The engine now contains all the modules, so at the moment only worry about the engine's commit hash. + +You need to go and checkout the proper commit for it. + +Now you can go ahead and compile the engine normally. + +## Pulling upstream changes + +First pull the changes by calling + +``` git pull orgin master ``` + +Then just run `scons`, to will update the modules. + +## Upgrading the modules + +Note: this is how to update the HEADS file. Normally you don't need to do this. + +If you want to update the modules, and the engine to the latest, you can use (`action=update`): + +``` scons a=u ``` + +You can also update different targets: `all`, `engine`, `modules`, `all_addons`, `addons`, `third_party_addons` + +For example to update the engine to the latest: ``` scons a=u target=engine ``` diff --git a/addons/world_generator/plugin.cfg b/addons/world_generator/plugin.cfg new file mode 100644 index 0000000..0680fe4 --- /dev/null +++ b/addons/world_generator/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="WorldGenerator" +description="" +author="Relintai" +version="" +script="plugin.gd" diff --git a/addons/world_generator/plugin.gd b/addons/world_generator/plugin.gd new file mode 100644 index 0000000..60b0c25 --- /dev/null +++ b/addons/world_generator/plugin.gd @@ -0,0 +1,77 @@ +tool +extends EditorPlugin + +var SWorldGeneratorSettings = preload("res://addons/world_generator/resources/world_generator_settings.gd") + +var SWorldGenBaseResource = preload("res://addons/world_generator/resources/world_gen_base_resource.gd") +var SWorldGenWorld = preload("res://addons/world_generator/resources/world_gen_world.gd") +var SContinent = preload("res://addons/world_generator/resources/continent.gd") +var SZone = preload("res://addons/world_generator/resources/zone.gd") +var SSubZone = preload("res://addons/world_generator/resources/subzone.gd") + +var editor_packed_scene = preload("res://addons/world_generator/ui/MainScreen.tscn") +var editor_scene = null + +var tool_button : ToolButton = null + +func _enter_tree(): + add_custom_type("WorldGeneratorSettings", "Resource", SWorldGeneratorSettings, null) + + add_custom_type("WorldGenBaseResource", "Resource", SWorldGenBaseResource, null) + #Don't change the base to "WorldGenBaseResource" else it will complain about a non-existant class + #Also it works perfectly like this + add_custom_type("WorldGenWorld", "Resource", SWorldGenWorld, null) + add_custom_type("Continent", "Resource", SContinent, null) + add_custom_type("Zone", "Resource", SZone, null) + add_custom_type("SubZone", "Resource", SSubZone, null) + + editor_scene = editor_packed_scene.instance() + editor_scene.set_plugin(self) + + tool_button = add_control_to_bottom_panel(editor_scene, "World Editor") + tool_button.hide() + +func _exit_tree(): + remove_custom_type("WorldGeneratorSettings") + + remove_custom_type("WorldGenBaseResource") + remove_custom_type("WorldGenWorld") + remove_custom_type("Continent") + remove_custom_type("Zone") + remove_custom_type("SubZone") + + remove_control_from_bottom_panel(editor_scene) + + +func handles(object): + return object is WorldGenWorld + +func edit(object): + #if editor_scene: + # make_bottom_panel_item_visible(editor_scene) + + if object is WorldGenWorld: + var wgw : WorldGenWorld = object as WorldGenWorld + editor_scene.set_wgworld(wgw) + +func make_visible(visible): + if tool_button: + if visible: + tool_button.show() + else: + #if tool_button.pressed: + # tool_button.pressed = false + + if !tool_button.pressed: + tool_button.hide() + +func get_plugin_icon(): + return null + +func get_plugin_name(): + return "WorldGeneratorEditor" + +func has_main_screen(): + return false + + diff --git a/addons/world_generator/raycast/world_gen_raycast.gd b/addons/world_generator/raycast/world_gen_raycast.gd new file mode 100644 index 0000000..2a80373 --- /dev/null +++ b/addons/world_generator/raycast/world_gen_raycast.gd @@ -0,0 +1,32 @@ +tool +extends Reference +class_name WorldGenRaycast + +var current_index : int = -1 +var base_resources : Array = Array() +var local_positions : PoolVector2Array = PoolVector2Array() +var local_uvs : PoolVector2Array = PoolVector2Array() + +func get_local_position() -> Vector2: + return local_positions[current_index] + +func get_local_uv() -> Vector2: + return local_uvs[current_index] + +# WorldGenBaseResource (can't explicitly add -> cyclic dependency) +func get_resource(): + return base_resources[current_index] + +func next() -> bool: + current_index += 1 + + return base_resources.size() > current_index + +func size() -> int: + return base_resources.size() + +# base_resource -> WorldGenBaseResource +func add_data(base_resource, local_pos : Vector2, local_uv : Vector2) -> void: + base_resources.append(base_resource) + local_positions.append(local_pos) + local_uvs.append(local_uv) diff --git a/addons/world_generator/resources/continent.gd b/addons/world_generator/resources/continent.gd new file mode 100644 index 0000000..2a39640 --- /dev/null +++ b/addons/world_generator/resources/continent.gd @@ -0,0 +1,38 @@ +tool +extends "res://addons/world_generator/resources/world_gen_base_resource.gd" +class_name Continent + +export(Array) var zones : Array + +func get_content() -> Array: + return zones + +func set_content(arr : Array) -> void: + zones = arr + +func create_content(item_name : String = "") -> void: + var zone : Zone = Zone.new() + zone.resource_name = item_name + + var r : Rect2 = get_rect() + r.position = Vector2() + r.size.x /= 10.0 + r.size.y /= 10.0 + + zone.set_rect(r) + + add_content(zone) + +func add_content(entry : WorldGenBaseResource) -> void: + zones.append(entry) + emit_changed() + +func remove_content_entry(entry : WorldGenBaseResource) -> void: + for i in range(zones.size()): + if zones[i] == entry: + zones.remove(i) + emit_changed() + return + +func setup_property_inspector(inspector) -> void: + .setup_property_inspector(inspector) diff --git a/addons/world_generator/resources/sub_zone_prop.gd b/addons/world_generator/resources/sub_zone_prop.gd new file mode 100644 index 0000000..8a8fddd --- /dev/null +++ b/addons/world_generator/resources/sub_zone_prop.gd @@ -0,0 +1,7 @@ +tool +extends "res://addons/world_generator/resources/world_gen_base_resource.gd" +class_name SubZoneProp + +func setup_property_inspector(inspector) -> void: + .setup_property_inspector(inspector) + diff --git a/addons/world_generator/resources/subzone.gd b/addons/world_generator/resources/subzone.gd new file mode 100644 index 0000000..111eabc --- /dev/null +++ b/addons/world_generator/resources/subzone.gd @@ -0,0 +1,38 @@ +tool +extends "res://addons/world_generator/resources/world_gen_base_resource.gd" +class_name SubZone + +export(Array) var subzone_props : Array + +func get_content() -> Array: + return subzone_props + +func set_content(arr : Array) -> void: + subzone_props = arr + +func create_content(item_name : String = "") -> void: + var subzone_prop : SubZoneProp = SubZoneProp.new() + subzone_prop.resource_name = item_name + + var r : Rect2 = get_rect() + r.position = Vector2() + r.size.x /= 10.0 + r.size.y /= 10.0 + + subzone_prop.set_rect(r) + + add_content(subzone_prop) + +func add_content(entry : WorldGenBaseResource) -> void: + subzone_props.append(entry) + emit_changed() + +func remove_content_entry(entry : WorldGenBaseResource) -> void: + for i in range(subzone_props.size()): + if subzone_props[i] == entry: + subzone_props.remove(i) + emit_changed() + return + +func setup_property_inspector(inspector) -> void: + .setup_property_inspector(inspector) diff --git a/addons/world_generator/resources/world_gen_base_resource.gd b/addons/world_generator/resources/world_gen_base_resource.gd new file mode 100644 index 0000000..fffb1a7 --- /dev/null +++ b/addons/world_generator/resources/world_gen_base_resource.gd @@ -0,0 +1,219 @@ +tool +extends Resource +class_name WorldGenBaseResource + +export(Rect2) var rect : Rect2 = Rect2(0, 0, 100, 100) +export(Vector2i) var min_size : Vector2i = Vector2i(1, 1) +export(Vector2i) var max_size : Vector2i = Vector2i(1000000, 1000000) +export(bool) var locked : bool = false + +func get_rect() -> Rect2: + return rect + +func set_rect(r : Rect2) -> void: + rect.position = r.position + rect.size.x = max(min_size.x, r.size.x) + rect.size.y = max(min_size.y, r.size.y) + rect.size.x = min(max_size.x, rect.size.x) + rect.size.y = min(max_size.y, rect.size.y) + emit_changed() + +func get_min_size() -> Vector2i: + return min_size + +func set_min_size(r : Vector2i) -> void: + min_size = r + emit_changed() + +func get_max_size() -> Vector2i: + return max_size + +func set_max_size(r : Vector2i) -> void: + max_size = r + emit_changed() + +func get_locked() -> bool: + return locked + +func set_locked(r : bool) -> void: + locked = r + emit_changed() + +func get_content() -> Array: + return Array() + +func set_content(arr : Array) -> void: + pass + +func add_content(entry : WorldGenBaseResource) -> void: + pass + +func create_content(item_name : String = "") -> void: + pass + +func remove_content_entry(entry : WorldGenBaseResource) -> void: + pass + +func is_spawner() -> bool: + return _is_spawner() + +func _is_spawner() -> bool: + return false + +func get_spawn_local_position() -> Vector2: + return _get_spawn_local_position() + +func _get_spawn_local_position() -> Vector2: + return Vector2() + +func get_spawn_positions(var parent_position : Vector2 = Vector2()) -> Array: + if is_spawner(): + return [ [ resource_name, parent_position + rect.position + get_spawn_local_position() ] ] + + var spawners : Array + var p : Vector2 = parent_position + rect.position + + for c in get_content(): + if c: + spawners.append_array(c.get_spawn_positions(p)) + + return spawners + +func get_content_with_name(name : String) -> WorldGenBaseResource: + if resource_name == name: + return self + + for c in get_content(): + if c: + var cc = c.get_content_with_name(name) + if cc: + return cc + + return null + +func get_all_contents_with_name(name : String) -> Array: + var arr : Array = Array() + + if resource_name == name: + arr.append(self) + + for c in get_content(): + if c: + var cc : Array = c.get_all_contents_with_name(name) + arr.append_array(cc) + + return arr + +func duplicate_content_entry(entry : WorldGenBaseResource, add : bool = true) -> WorldGenBaseResource: + var de : WorldGenBaseResource = entry.duplicate(true) + de.resource_name += " (Duplicate)" + + if add: + add_content(de) + + return de + +func setup_terra_library(library : TerrainLibrary, pseed : int) -> void: + _setup_terra_library(library, pseed) + + for c in get_content(): + if c: + c.setup_terra_library(library, pseed) + +func _setup_terra_library(library : TerrainLibrary, pseed : int) -> void: + pass + +func generate_terra_chunk(chunk: TerrainChunk, pseed : int, spawn_mobs: bool) -> void: + var p : Vector2 = Vector2(chunk.get_position_x(), chunk.get_position_z()) + + var raycast : WorldGenRaycast = get_hit_stack(p) + + if raycast.size() == 0: + _generate_terra_chunk_fallback(chunk, pseed, spawn_mobs) + return + + while raycast.next(): + raycast.get_resource()._generate_terra_chunk(chunk, pseed, spawn_mobs, raycast) + +func _generate_terra_chunk(chunk: TerrainChunk, pseed : int, spawn_mobs: bool, raycast : WorldGenRaycast) -> void: + pass + +func _generate_terra_chunk_fallback(chunk: TerrainChunk, pseed : int, spawn_mobs: bool) -> void: + chunk.channel_ensure_allocated(TerrainChunkDefault.DEFAULT_CHANNEL_TYPE, 1) + chunk.channel_ensure_allocated(TerrainChunkDefault.DEFAULT_CHANNEL_ISOLEVEL, 1) + chunk.set_voxel(1, 0, 0, TerrainChunkDefault.DEFAULT_CHANNEL_ISOLEVEL) + +func generate_map(pseed : int) -> Image: + var img : Image = Image.new() + + img.create(get_rect().size.x, get_rect().size.y, false, Image.FORMAT_RGBA8) + + add_to_map(img, pseed) + + return img + +func add_to_map(img : Image, pseed : int) -> void: + _add_to_map(img, pseed) + + for c in get_content(): + if c: + c.add_to_map(img, pseed) + +func _add_to_map(img : Image, pseed : int) -> void: + pass + +func get_hit_stack(pos : Vector2, raycast : WorldGenRaycast = null) -> WorldGenRaycast: + var r : Rect2 = get_rect() + var local_pos : Vector2 = pos - rect.position + r.position = Vector2() + + if !raycast: + raycast = WorldGenRaycast.new() + + if r.has_point(local_pos): + var local_uv : Vector2 = local_pos / rect.size + raycast.add_data(self, local_pos, local_uv) + + for c in get_content(): + if c: + c.get_hit_stack(local_pos, raycast) + + return raycast + +func get_editor_rect_border_color() -> Color: + return Color(1, 1, 1, 1) + +func get_editor_rect_color() -> Color: + return Color(1, 1, 1, 0.9) + +func get_editor_rect_border_size() -> int: + return 2 + +func get_editor_font_color() -> Color: + return Color(0, 0, 0, 1) + +func get_editor_class() -> String: + return "WorldGenBaseResource" + +func get_editor_additional_text() -> String: + return "" + +func eitor_draw_additional(control : Control) -> void: + _eitor_draw_additional(control) + +func _eitor_draw_additional(control : Control) -> void: + pass + +func eitor_draw_additional_background(control : Control) -> void: + _eitor_draw_additional_background(control) + +func _eitor_draw_additional_background(control : Control) -> void: + pass + +func setup_property_inspector(inspector) -> void: + inspector.add_slot_line_edit("get_name", "set_name", "Name") + inspector.add_slot_rect2("get_rect", "set_rect", "Rect", 1) + inspector.add_slot_vector2i("get_min_size", "set_min_size", "Min Size", 1) + inspector.add_slot_vector2i("get_max_size", "set_max_size", "Max Size", 1) + inspector.add_slot_bool("get_locked", "set_locked", "Locked") + diff --git a/addons/world_generator/resources/world_gen_world.gd b/addons/world_generator/resources/world_gen_world.gd new file mode 100644 index 0000000..f5bc15f --- /dev/null +++ b/addons/world_generator/resources/world_gen_world.gd @@ -0,0 +1,62 @@ +tool +extends "res://addons/world_generator/resources/world_gen_base_resource.gd" +class_name WorldGenWorld + +export(Array) var continents : Array + +func get_content() -> Array: + return continents + +func set_content(arr : Array) -> void: + continents = arr + +func create_content(item_name : String = "") -> void: + var continent : Continent = Continent.new() + continent.resource_name = item_name + + add_content(continent) + +func add_content(entry : WorldGenBaseResource) -> void: + var r : Rect2 = get_rect() + r.position = Vector2() + r.size.x /= 10.0 + r.size.y /= 10.0 + + entry.set_rect(r) + + continents.append(entry) + emit_changed() + +func remove_content_entry(entry : WorldGenBaseResource) -> void: + for i in range(continents.size()): + if continents[i] == entry: + continents.remove(i) + emit_changed() + return + +func setup_property_inspector(inspector) -> void: + .setup_property_inspector(inspector) + +func generate_terra_chunk(chunk: TerrainChunk, pseed : int, spawn_mobs: bool) -> void: + var p : Vector2 = Vector2(chunk.get_position_x(), chunk.get_position_z()) + + var raycast : WorldGenRaycast = get_hit_stack(p) + + if raycast.size() == 0: + _generate_terra_chunk_fallback(chunk, pseed, spawn_mobs) + return + + _generate_terra_chunk(chunk, pseed, spawn_mobs, raycast) + + while raycast.next(): + raycast.get_resource()._generate_terra_chunk(chunk, pseed, spawn_mobs, raycast) + + +func _generate_terra_chunk(chunk: TerrainChunk, pseed : int, spawn_mobs: bool, raycast : WorldGenRaycast) -> void: + pass + +func _generate_terra_chunk_fallback(chunk: TerrainChunk, pseed : int, spawn_mobs: bool) -> void: + chunk.channel_ensure_allocated(TerrainChunkDefault.DEFAULT_CHANNEL_TYPE, 1) + chunk.channel_ensure_allocated(TerrainChunkDefault.DEFAULT_CHANNEL_ISOLEVEL, 1) + chunk.set_voxel(1, 0, 0, TerrainChunkDefault.DEFAULT_CHANNEL_ISOLEVEL) + diff --git a/addons/world_generator/resources/world_generator_settings.gd b/addons/world_generator/resources/world_generator_settings.gd new file mode 100644 index 0000000..a7947b4 --- /dev/null +++ b/addons/world_generator/resources/world_generator_settings.gd @@ -0,0 +1,104 @@ +tool +extends Resource +class_name WorldGeneratorSettings + +export(PoolStringArray) var continent_class_folders : PoolStringArray +export(PoolStringArray) var zone_class_folders : PoolStringArray +export(PoolStringArray) var subzone_class_folders : PoolStringArray +export(PoolStringArray) var subzone_prop_class_folders : PoolStringArray + +enum WorldGeneratorScriptType { + CONTINENT = 0, + ZONE = 1, + SUBZONE = 2, + SUBZONE_PROP = 3, +}; + +func evaluate_scripts(script_type : int, tree : Tree) -> void: + if (script_type == WorldGeneratorScriptType.CONTINENT): + evaluate_continent_scripts(tree) + elif (script_type == WorldGeneratorScriptType.ZONE): + evaluate_zone_scripts(tree) + elif (script_type == WorldGeneratorScriptType.SUBZONE): + evaluate_subzone_scripts(tree) + elif (script_type == WorldGeneratorScriptType.SUBZONE_PROP): + evaluate_subzone_prop_scripts(tree) + +func evaluate_continent_scripts(tree : Tree) -> void: + tree.clear() + + var root : TreeItem = tree.create_item() + root.set_text(0, "Continent") + root.set_meta("class_name", "Continent") + + for s in continent_class_folders: + evaluate_folder(s, tree, root) + + root.select(0) + +func evaluate_zone_scripts(tree : Tree) -> void: + tree.clear() + + var root : TreeItem = tree.create_item() + root.set_text(0, "Zone") + root.set_meta("class_name", "Zone") + + for s in zone_class_folders: + evaluate_folder(s, tree, root) + + root.select(0) + +func evaluate_subzone_scripts(tree : Tree) -> void: + tree.clear() + + var root : TreeItem = tree.create_item() + root.set_text(0, "SubZone") + root.set_meta("class_name", "SubZone") + + for s in subzone_class_folders: + evaluate_folder(s, tree, root) + + root.select(0) + +func evaluate_subzone_prop_scripts(tree : Tree) -> void: + tree.clear() + + var root : TreeItem = tree.create_item() + root.set_text(0, "SubZoneProp") + root.set_meta("class_name", "SubZoneProp") + + for s in subzone_prop_class_folders: + evaluate_folder(s, tree, root) + + root.select(0) + +func evaluate_folder(folder : String, tree : Tree, root : TreeItem) -> void: + var ti : TreeItem = null + + var dir = Directory.new() + if dir.open(folder) == OK: + dir.list_dir_begin() + var file_name = dir.get_next() + while file_name != "": + if !dir.current_is_dir(): + #print("Found file: " + file_name) + + if !ti: + var n : String = folder.substr(folder.find_last("/") + 1) + + if n != "": + ti = tree.create_item(root) + ti.set_text(0, n) + else: + ti = root + + var e : TreeItem = tree.create_item(ti) + + e.set_text(0, file_name.get_file()) + e.set_meta("file", folder + "/" + file_name) + + file_name = dir.get_next() + else: + print("An error occurred when trying to access the path.") + + diff --git a/addons/world_generator/resources/zone.gd b/addons/world_generator/resources/zone.gd new file mode 100644 index 0000000..2634a47 --- /dev/null +++ b/addons/world_generator/resources/zone.gd @@ -0,0 +1,38 @@ +tool +extends "res://addons/world_generator/resources/world_gen_base_resource.gd" +class_name Zone + +export(Array) var subzones : Array + +func get_content() -> Array: + return subzones + +func set_content(arr : Array) -> void: + subzones = arr + +func create_content(item_name : String = "") -> void: + var subzone : SubZone = SubZone.new() + subzone.resource_name = item_name + + var r : Rect2 = get_rect() + r.position = Vector2() + r.size.x /= 10.0 + r.size.y /= 10.0 + + subzone.set_rect(r) + + add_content(subzone) + +func add_content(entry : WorldGenBaseResource) -> void: + subzones.append(entry) + emit_changed() + +func remove_content_entry(entry : WorldGenBaseResource) -> void: + for i in range(subzones.size()): + if subzones[i] == entry: + subzones.remove(i) + emit_changed() + return + +func setup_property_inspector(inspector) -> void: + .setup_property_inspector(inspector) diff --git a/addons/world_generator/test/test_world.tres b/addons/world_generator/test/test_world.tres new file mode 100644 index 0000000..387c353 --- /dev/null +++ b/addons/world_generator/test/test_world.tres @@ -0,0 +1,351 @@ +[gd_resource type="Resource" load_steps=40 format=2] + +[ext_resource path="res://addons/world_generator/resources/world_gen_world.gd" type="Script" id=1] +[ext_resource path="res://addons/world_generator/resources/continent.gd" type="Script" id=2] +[ext_resource path="res://addons/world_generator/resources/zone.gd" type="Script" id=3] +[ext_resource path="res://addons/world_generator/resources/subzone.gd" type="Script" id=4] +[ext_resource path="res://scripts/world_generator/continents/test_continent.gd" type="Script" id=5] +[ext_resource path="res://scripts/world_generator/zones/test_zone.gd" type="Script" id=6] +[ext_resource path="res://scripts/world_generator/subzones/test_subzone.gd" type="Script" id=7] + +[sub_resource type="Resource" id=14] +resource_name = "qwe" +script = ExtResource( 4 ) +rect = Rect2( 21, 25, 150, 79 ) +locked = false + +[sub_resource type="Resource" id=15] +resource_name = "we" +script = ExtResource( 4 ) +rect = Rect2( 7, 9, 54, 34 ) +locked = false + +[sub_resource type="Resource" id=34] +resource_name = "yy" +script = ExtResource( 7 ) +rect = Rect2( 66, 11, 100, 91 ) +locked = false + +[sub_resource type="Resource" id=8] +resource_name = "asdasr" +script = ExtResource( 3 ) +rect = Rect2( 35, 21, 191.4, 127.8 ) +locked = false +subzones = [ SubResource( 14 ), SubResource( 15 ), SubResource( 34 ) ] + +[sub_resource type="Resource" id=13] +resource_name = "qqq" +script = ExtResource( 3 ) +rect = Rect2( 17, 59, 200.4, 52 ) +locked = false +subzones = [ ] + +[sub_resource type="Resource" id=24] +resource_name = "trtrtr" +script = ExtResource( 3 ) +rect = Rect2( 0, 0, 57.1, 45.8 ) +locked = false +subzones = [ ] + +[sub_resource type="GDScript" id=25] +script/source = "tool +extends \"res://addons/world_generator/resources/world_gen_base_resource.gd\" +class_name Zone + +export(Array) var subzones : Array + +func get_content() -> Array: + return subzones + +func set_content(arr : Array) -> void: + subzones = arr + +func create_content(item_name : String = \"\") -> void: + var subzone : SubZone = SubZone.new() + subzone.resource_name = item_name + + var r : Rect2 = get_rect() + r.position = Vector2() + r.size.x /= 10.0 + r.size.y /= 10.0 + + subzone.set_rect(r) + + add_content(subzone) + +func add_content(entry : WorldGenBaseResource) -> void: + subzones.append(entry) + emit_changed() + +func remove_content_entry(entry : WorldGenBaseResource) -> void: + for i in range(subzones.size()): + if subzones[i] == entry: + subzones.remove(i) + emit_changed() + return + +func setup_property_inspector(inspector) -> void: + .setup_property_inspector(inspector) +" + +[sub_resource type="Resource" id=26] +resource_name = "trtrtr (Duplicate)" +script = SubResource( 25 ) +rect = Rect2( 64, 6, 158.1, 39.8 ) +locked = false +subzones = [ ] + +[sub_resource type="Resource" id=32] +resource_name = "tttte" +script = ExtResource( 3 ) +rect = Rect2( 103, 28, 100, 100 ) +locked = false +subzones = [ ] + +[sub_resource type="Resource" id=35] +resource_name = "yu" +script = ExtResource( 6 ) +rect = Rect2( 88, 21, 100, 100 ) +locked = false +subzones = [ ] + +[sub_resource type="Resource" id=1] +resource_name = "wwww" +script = ExtResource( 2 ) +rect = Rect2( 163, 35, 241, 158 ) +zones = [ SubResource( 8 ), SubResource( 13 ), SubResource( 24 ), SubResource( 26 ), SubResource( 32 ), SubResource( 35 ) ] + +[sub_resource type="Resource" id=2] +resource_name = "efefef" +script = ExtResource( 2 ) +rect = Rect2( 107, 271, 100, 49 ) + +[sub_resource type="Resource" id=3] +resource_name = "grgrg" +script = ExtResource( 2 ) +rect = Rect2( 498, 185, 100, 100 ) + +[sub_resource type="Resource" id=9] +resource_name = "asd" +script = ExtResource( 3 ) +rect = Rect2( 528, 34, 0, 0 ) +locked = false +subzones = [ ] + +[sub_resource type="Resource" id=10] +resource_name = "qqq" +script = ExtResource( 3 ) +rect = Rect2( 528, 34, 0, 0 ) +locked = false +subzones = [ ] + +[sub_resource type="Resource" id=4] +resource_name = "qwdasd" +script = ExtResource( 2 ) +rect = Rect2( 522, 29, 63, 54 ) +zones = [ SubResource( 9 ), SubResource( 10 ) ] + +[sub_resource type="Resource" id=11] +resource_name = "q" +script = ExtResource( 3 ) +rect = Rect2( 14, 11, 53, 59 ) +locked = false +subzones = [ ] + +[sub_resource type="Resource" id=5] +resource_name = "qwe" +script = ExtResource( 2 ) +rect = Rect2( 473, 331, 100, 100 ) +zones = [ SubResource( 11 ) ] + +[sub_resource type="GDScript" id=16] +script/source = "tool +extends \"res://addons/world_generator/resources/world_gen_base_resource.gd\" +class_name Continent + +export(Array) var zones : Array + +func get_content() -> Array: + return zones + +func set_content(arr : Array) -> void: + zones = arr + +func create_content(item_name : String = \"\") -> void: + var zone : Zone = Zone.new() + zone.resource_name = item_name + + var r : Rect2 = get_rect() + r.position = Vector2() + r.size.x /= 10.0 + r.size.y /= 10.0 + + zone.set_rect(r) + + add_content(zone) + +func add_content(entry : WorldGenBaseResource) -> void: + zones.append(entry) + emit_changed() + +func remove_content_entry(entry : WorldGenBaseResource) -> void: + for i in range(zones.size()): + if zones[i] == entry: + zones.remove(i) + emit_changed() + return + +func setup_property_inspector(inspector) -> void: + .setup_property_inspector(inspector) +" + +[sub_resource type="Resource" id=17] +resource_name = "qwetwwqasd" +script = SubResource( 16 ) +rect = Rect2( 473, 331, 100, 100 ) +locked = false +zones = [ SubResource( 11 ) ] + +[sub_resource type="GDScript" id=18] +script/source = "tool +extends \"res://addons/world_generator/resources/world_gen_base_resource.gd\" +class_name Continent + +export(Array) var zones : Array + +func get_content() -> Array: + return zones + +func set_content(arr : Array) -> void: + zones = arr + +func create_content(item_name : String = \"\") -> void: + var zone : Zone = Zone.new() + zone.resource_name = item_name + + var r : Rect2 = get_rect() + r.position = Vector2() + r.size.x /= 10.0 + r.size.y /= 10.0 + + zone.set_rect(r) + + add_content(zone) + +func add_content(entry : WorldGenBaseResource) -> void: + zones.append(entry) + emit_changed() + +func remove_content_entry(entry : WorldGenBaseResource) -> void: + for i in range(zones.size()): + if zones[i] == entry: + zones.remove(i) + emit_changed() + return + +func setup_property_inspector(inspector) -> void: + .setup_property_inspector(inspector) +" + +[sub_resource type="Resource" id=19] +resource_name = "qwesat" +script = SubResource( 18 ) +rect = Rect2( 613, 346, 100, 100 ) +locked = false +zones = [ SubResource( 11 ) ] + +[sub_resource type="Resource" id=20] +resource_name = "ggg" +script = ExtResource( 2 ) +rect = Rect2( 26, 51, 100, 100 ) + +[sub_resource type="GDScript" id=21] +script/source = "tool +extends \"res://addons/world_generator/resources/world_gen_base_resource.gd\" +class_name Continent + +export(Array) var zones : Array + +func get_content() -> Array: + return zones + +func set_content(arr : Array) -> void: + zones = arr + +func create_content(item_name : String = \"\") -> void: + var zone : Zone = Zone.new() + zone.resource_name = item_name + + var r : Rect2 = get_rect() + r.position = Vector2() + r.size.x /= 10.0 + r.size.y /= 10.0 + + zone.set_rect(r) + + add_content(zone) + +func add_content(entry : WorldGenBaseResource) -> void: + zones.append(entry) + emit_changed() + +func remove_content_entry(entry : WorldGenBaseResource) -> void: + for i in range(zones.size()): + if zones[i] == entry: + zones.remove(i) + emit_changed() + return + +func setup_property_inspector(inspector) -> void: + .setup_property_inspector(inspector) +" + +[sub_resource type="Resource" id=22] +resource_name = "ggg (Duplicate)" +script = SubResource( 21 ) +rect = Rect2( 33, 181, 100, 100 ) +locked = false +zones = [ ] + +[sub_resource type="Resource" id=23] +resource_name = "eeqqq" +script = ExtResource( 2 ) +rect = Rect2( 256, 365, 100, 100 ) + +[sub_resource type="Resource" id=27] +resource_name = "asd" +script = ExtResource( 2 ) +rect = Rect2( 337, 79, 100, 100 ) + +[sub_resource type="Resource" id=28] +resource_name = "asd" +script = ExtResource( 2 ) +rect = Rect2( 244, 234, 100, 100 ) + +[sub_resource type="Resource" id=29] +script = ExtResource( 2 ) +rect = Rect2( 377, 69, 284, 238 ) + +[sub_resource type="Resource" id=30] +resource_name = "tttttt" +script = ExtResource( 2 ) +rect = Rect2( 188, 225, 233, 259 ) + +[sub_resource type="Resource" id=31] +resource_name = "ttttyuqtttt" +script = ExtResource( 2 ) +rect = Rect2( 339, 76, 220, 170 ) + +[sub_resource type="Resource" id=33] +resource_name = "qttt" +script = ExtResource( 5 ) +rect = Rect2( 99, 78, 213, 105 ) +locked = false +zones = [ ] + +[resource] +resource_name = "asdasdsse" +script = ExtResource( 1 ) +rect = Rect2( 0, 0, 1000, 1000 ) +locked = false +continents = [ SubResource( 1 ), SubResource( 2 ), SubResource( 3 ), SubResource( 4 ), SubResource( 5 ), SubResource( 17 ), SubResource( 19 ), SubResource( 20 ), SubResource( 22 ), SubResource( 23 ), SubResource( 27 ), SubResource( 28 ), SubResource( 29 ), SubResource( 30 ), SubResource( 31 ), SubResource( 33 ) ] diff --git a/addons/world_generator/ui/DataList.gd b/addons/world_generator/ui/DataList.gd new file mode 100644 index 0000000..a931560 --- /dev/null +++ b/addons/world_generator/ui/DataList.gd @@ -0,0 +1,201 @@ +tool +extends Tree + +export(int, "Continent,Zone,Sub Zone,Sub Zone Prop") var class_types : int = 0 + +var edited_resource : WorldGenBaseResource = null +var name_edited_resource : WorldGenBaseResource = null + +var _ignore_changed_event : bool = false + +var _plugin : EditorPlugin = null +var _undo_redo : UndoRedo = null + +signal request_item_edit(world_gen_base_resource) + +func _init(): + if !is_connected("item_edited", self, "on_item_edited"): + connect("item_edited", self, "on_item_edited") + + if !is_connected("button_pressed", self, "on_tree_button_pressed"): + connect("button_pressed", self, "on_tree_button_pressed") + +func set_plugin(plugin : EditorPlugin) -> void: + _plugin = plugin + _undo_redo = _plugin.get_undo_redo() + +func _enter_tree(): + var dir : Directory = Directory.new() + + if dir.file_exists("res://world_generator_settings.tres"): + var wgs : WorldGeneratorSettings = load("res://world_generator_settings.tres") as WorldGeneratorSettings + + if !wgs: + return + + wgs.evaluate_scripts(class_types, $NameDialog/VBoxContainer/Tree) + +func add_item(item_name : String = "") -> void: + if !edited_resource: + return + + var ti : TreeItem = $NameDialog/VBoxContainer/Tree.get_selected() + + if !ti: + return + + var e : WorldGenBaseResource = null + + if ti.has_meta("class_name"): + var cn : String = ti.get_meta("class_name") + + if cn == "Continent": + e = Continent.new() + elif cn == "Zone": + e = Zone.new() + elif cn == "SubZone": + e = SubZone.new() + elif cn == "SubZoneProp": + e = SubZoneProp.new() + + elif ti.has_meta("file"): + var cls = load(ti.get_meta("file")) + + if cls: + e = cls.new() + + if !e: + return + + e.resource_name = item_name + + var r : Rect2 = edited_resource.get_rect() + var rs : Vector2 = r.size + r.size.x /= 5.0 + r.size.y /= 5.0 + r.position = rs / Vector2(2, 2) + r.position -= r.size / Vector2(2, 2) + e.set_rect(r) + + #edited_resource.add_content(e) + #remove_content_entry + + _undo_redo.create_action("WE: Created Entry") + _undo_redo.add_do_method(edited_resource, "add_content", e) + _undo_redo.add_undo_method(edited_resource, "remove_content_entry", e) + _undo_redo.commit_action() + +func refresh() -> void: + clear() + + if !edited_resource: + return + + var root : TreeItem = create_item() + + var data : Array = edited_resource.get_content() + + for d in data: + if d: + var n : String = d.resource_name + + if n == "": + n = "" + + var item : TreeItem = create_item(root) + + item.set_text(0, n) + item.set_meta("res", d) + item.add_button(0, get_theme_icon("Edit", "EditorIcons"), -1, false, "Edit") + item.set_editable(0, true) + +func set_edited_resource(res : WorldGenBaseResource)-> void: + if edited_resource: + edited_resource.disconnect("changed", self, "on_resource_changed") + + edited_resource = res + + if edited_resource: + edited_resource.connect("changed", self, "on_resource_changed") + + refresh() + +func add_button_pressed() -> void: + $NameDialog/VBoxContainer/LineEdit.text = "" + $NameDialog.popup_centered() + +func name_dialog_ok_pressed() -> void: + add_item($NameDialog/VBoxContainer/LineEdit.text) + +func delete_button_pressed() -> void: + var item : TreeItem = get_selected() + + if !item: + return + + var item_resource = item.get_meta("res") + + if !item_resource: + return + + #edited_resource.remove_content_entry(item_resource) + + _undo_redo.create_action("WE: Created Entry") + _undo_redo.add_do_method(edited_resource, "remove_content_entry", item_resource) + _undo_redo.add_undo_method(edited_resource, "add_content", item_resource) + _undo_redo.commit_action() + +func duplicate_button_pressed() -> void: + var item : TreeItem = get_selected() + + if !item: + return + + var item_resource = item.get_meta("res") + + if !item_resource: + return + + #edited_resource.duplicate_content_entry(item_resource) + + var de = edited_resource.duplicate_content_entry(item_resource, false) + + _undo_redo.create_action("WE: Created Entry") + _undo_redo.add_do_method(edited_resource, "add_content", de) + _undo_redo.add_undo_method(edited_resource, "remove_content_entry", de) + _undo_redo.commit_action() + +func on_resource_changed() -> void: + if _ignore_changed_event: + return + + call_deferred("refresh") + +func on_tree_button_pressed(item: TreeItem, column: int, id: int) -> void: + var resource : WorldGenBaseResource = item.get_meta("res") + + if !resource: + return + + emit_signal("request_item_edit", resource) + +func on_item_edited() -> void: + var item : TreeItem = get_edited() + + if !item: + return + + name_edited_resource = item.get_meta("res") + + if !name_edited_resource: + return + + _undo_redo.create_action("WE: Renamed Entry") + _undo_redo.add_do_method(name_edited_resource, "set_name", item.get_text(0)) + _undo_redo.add_undo_method(name_edited_resource, "set_name", name_edited_resource.resource_name) + _undo_redo.commit_action() + +# name_edited_resource.resource_name = item.get_text(0) + name_edited_resource.emit_changed() + name_edited_resource = null + on_resource_changed() diff --git a/addons/world_generator/ui/DataList.tscn b/addons/world_generator/ui/DataList.tscn new file mode 100644 index 0000000..5c328af --- /dev/null +++ b/addons/world_generator/ui/DataList.tscn @@ -0,0 +1,57 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/world_generator/ui/DataList.gd" type="Script" id=1] + +[node name="DataList" type="Tree"] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +allow_reselect = true +hide_folding = true +hide_root = true +script = ExtResource( 1 ) + +[node name="NameDialog" type="ConfirmationDialog" parent="."] +margin_right = 329.0 +margin_bottom = 313.0 +window_title = "Name" +resizable = true +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="VBoxContainer" type="VBoxContainer" parent="NameDialog"] +margin_left = 8.0 +margin_top = 8.0 +margin_right = 321.0 +margin_bottom = 277.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="Label2" type="Label" parent="NameDialog/VBoxContainer"] +margin_right = 313.0 +margin_bottom = 14.0 +text = "Class" + +[node name="Tree" type="Tree" parent="NameDialog/VBoxContainer"] +margin_top = 18.0 +margin_right = 313.0 +margin_bottom = 223.0 +rect_min_size = Vector2( 0, 200 ) +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Label" type="Label" parent="NameDialog/VBoxContainer"] +margin_top = 227.0 +margin_right = 313.0 +margin_bottom = 241.0 +text = "Name" + +[node name="LineEdit" type="LineEdit" parent="NameDialog/VBoxContainer"] +margin_top = 245.0 +margin_right = 313.0 +margin_bottom = 269.0 + +[connection signal="confirmed" from="NameDialog" to="." method="name_dialog_ok_pressed"] diff --git a/addons/world_generator/ui/MainScreen.gd b/addons/world_generator/ui/MainScreen.gd new file mode 100644 index 0000000..9809efb --- /dev/null +++ b/addons/world_generator/ui/MainScreen.gd @@ -0,0 +1,73 @@ +tool +extends PanelContainer + +var edited_world + +func _ready(): + var world : Control = get_node("TabContainer/World") + if !world.is_connected("request_item_edit", self, "on_world_request_item_edit"): + world.connect("request_item_edit", self, "on_world_request_item_edit") + + var continent : Control = get_node("TabContainer/Continent") + if !continent.is_connected("request_item_edit", self, "on_continent_request_item_edit"): + continent.connect("request_item_edit", self, "on_continent_request_item_edit") + + var zone : Control = get_node("TabContainer/Zone") + if !zone.is_connected("request_item_edit", self, "on_zone_request_item_edit"): + zone.connect("request_item_edit", self, "on_zone_request_item_edit") + + var subzone : Control = get_node("TabContainer/SubZone") + if !subzone.is_connected("request_item_edit", self, "on_subzone_request_item_edit"): + subzone.connect("request_item_edit", self, "on_subzone_request_item_edit") + + +func set_plugin(plugin : EditorPlugin) -> void: + $TabContainer/World.set_plugin(plugin) + $TabContainer/Continent.set_plugin(plugin) + $TabContainer/Zone.set_plugin(plugin) + $TabContainer/SubZone.set_plugin(plugin) + $TabContainer/SubZoneProp.set_plugin(plugin) + +func refresh() -> void: + $TabContainer/World.set_wgworld(edited_world) + $TabContainer/Continent.set_wgworld(edited_world) + $TabContainer/Zone.set_wgworld(edited_world) + $TabContainer/SubZone.set_wgworld(edited_world) + $TabContainer/SubZoneProp.set_wgworld(edited_world) + +func set_wgworld(wgw : WorldGenWorld) -> void: + edited_world = wgw + + refresh() + +func on_world_request_item_edit(resource : WorldGenBaseResource) -> void: + var cont : Control = get_node("TabContainer/Continent") + + var tc : TabContainer = get_node("TabContainer") + tc.current_tab = cont.get_position_in_parent() + + cont.switch_to(resource) + +func on_continent_request_item_edit(continent : WorldGenBaseResource, resource : WorldGenBaseResource) -> void: + var zone : Control = get_node("TabContainer/Zone") + + var tc : TabContainer = get_node("TabContainer") + tc.current_tab = zone.get_position_in_parent() + + zone.switch_to(continent, resource) + +func on_zone_request_item_edit(continent : WorldGenBaseResource, zone : WorldGenBaseResource, subzone : WorldGenBaseResource) -> void: + var sz : Control = get_node("TabContainer/SubZone") + + var tc : TabContainer = get_node("TabContainer") + tc.current_tab = sz.get_position_in_parent() + + sz.switch_to(continent, zone, subzone) + +func on_subzone_request_item_edit(continent : WorldGenBaseResource, zone : WorldGenBaseResource, subzone : WorldGenBaseResource, subzone_prop : WorldGenBaseResource) -> void: + var sz : Control = get_node("TabContainer/SubZoneProp") + + var tc : TabContainer = get_node("TabContainer") + tc.current_tab = sz.get_position_in_parent() + + sz.switch_to(continent, zone, subzone, subzone_prop) diff --git a/addons/world_generator/ui/MainScreen.tscn b/addons/world_generator/ui/MainScreen.tscn new file mode 100644 index 0000000..0f86dc8 --- /dev/null +++ b/addons/world_generator/ui/MainScreen.tscn @@ -0,0 +1,44 @@ +[gd_scene load_steps=7 format=2] + +[ext_resource path="res://addons/world_generator/ui/MainScreen.gd" type="Script" id=1] +[ext_resource path="res://addons/world_generator/ui/tabs/World.tscn" type="PackedScene" id=2] +[ext_resource path="res://addons/world_generator/ui/tabs/Continent.tscn" type="PackedScene" id=3] +[ext_resource path="res://addons/world_generator/ui/tabs/Zone.tscn" type="PackedScene" id=4] +[ext_resource path="res://addons/world_generator/ui/tabs/SubZone.tscn" type="PackedScene" id=5] +[ext_resource path="res://addons/world_generator/ui/tabs/SubZoneProp.tscn" type="PackedScene" id=6] + +[node name="WorldGenerator" type="PanelContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +rect_min_size = Vector2( 0, 200 ) +size_flags_vertical = 3 +script = ExtResource( 1 ) + +[node name="TabContainer" type="TabContainer" parent="."] +margin_left = 7.0 +margin_top = 7.0 +margin_right = 1017.0 +margin_bottom = 593.0 +tab_align = 0 + +[node name="World" parent="TabContainer" instance=ExtResource( 2 )] + +[node name="Continent" parent="TabContainer" instance=ExtResource( 3 )] +visible = false +margin_left = 4.0 +margin_top = 32.0 +margin_right = -4.0 +margin_bottom = -4.0 + +[node name="Zone" parent="TabContainer" instance=ExtResource( 4 )] +visible = false +margin_left = 4.0 +margin_top = 32.0 +margin_right = -4.0 +margin_bottom = -4.0 + +[node name="SubZone" parent="TabContainer" instance=ExtResource( 5 )] +visible = false + +[node name="SubZoneProp" parent="TabContainer" instance=ExtResource( 6 )] +visible = false diff --git a/addons/world_generator/ui/RectEditor.gd b/addons/world_generator/ui/RectEditor.gd new file mode 100644 index 0000000..83c2605 --- /dev/null +++ b/addons/world_generator/ui/RectEditor.gd @@ -0,0 +1,34 @@ +tool +extends PanelContainer + +var last_edited_res : WorldGenBaseResource = null + +func set_plugin(plugin : EditorPlugin) -> void: + get_node("ScrollContainer/MarginContainer/RectView").set_plugin(plugin) + +func set_edited_resource(res : WorldGenBaseResource): + get_node("ScrollContainer/MarginContainer/RectView").set_edited_resource(res) + + if res && res != last_edited_res: + var r : Rect2 = res.get_rect() + last_edited_res = res + + var axis : int = 0 + + if r.size.x > r.size.y: + axis = Vector2.AXIS_X + else: + axis = Vector2.AXIS_Y + + if r.size[axis] > 0: + var rsx : float = get_node("ScrollContainer").rect_size[axis] + var scale : float = rsx / r.size[axis] * 0.5 + + get_node("Control/EditorZoomWidget").zoom = scale + get_node("ScrollContainer/MarginContainer/RectView").apply_zoom() + + var sb : ScrollBar = get_node("ScrollContainer").get_h_scrollbar() + sb.ratio = 1 + + sb = get_node("ScrollContainer").get_v_scrollbar() + sb.ratio = 1 diff --git a/addons/world_generator/ui/RectEditor.tscn b/addons/world_generator/ui/RectEditor.tscn new file mode 100644 index 0000000..9ff7bcc --- /dev/null +++ b/addons/world_generator/ui/RectEditor.tscn @@ -0,0 +1,48 @@ +[gd_scene load_steps=4 format=2] + +[ext_resource path="res://addons/world_generator/ui/RectEditor.gd" type="Script" id=1] +[ext_resource path="res://addons/world_generator/widgets/EditorZoomWidget.tscn" type="PackedScene" id=2] +[ext_resource path="res://addons/world_generator/ui/RectView.gd" type="Script" id=3] + +[node name="RectEditor" type="PanelContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 1 ) + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +margin_left = 7.0 +margin_top = 7.0 +margin_right = 1017.0 +margin_bottom = 593.0 + +[node name="MarginContainer" type="MarginContainer" parent="ScrollContainer"] +margin_right = 400.0 +margin_bottom = 400.0 +custom_constants/margin_right = 200 +custom_constants/margin_top = 200 +custom_constants/margin_left = 200 +custom_constants/margin_bottom = 200 + +[node name="RectView" type="Control" parent="ScrollContainer/MarginContainer"] +margin_left = 200.0 +margin_top = 200.0 +margin_right = 200.0 +margin_bottom = 200.0 +script = ExtResource( 3 ) +zoom_widget_path = NodePath("../../../Control/EditorZoomWidget") + +[node name="Control" type="Control" parent="."] +margin_left = 7.0 +margin_top = 7.0 +margin_right = 1017.0 +margin_bottom = 593.0 +mouse_filter = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="EditorZoomWidget" parent="Control" instance=ExtResource( 2 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 115.0 +margin_bottom = 22.0 +custom_constants/separation = -8 diff --git a/addons/world_generator/ui/RectView.gd b/addons/world_generator/ui/RectView.gd new file mode 100644 index 0000000..1dcc56e --- /dev/null +++ b/addons/world_generator/ui/RectView.gd @@ -0,0 +1,139 @@ +tool +extends Control + +var rect_editor_node_scene : PackedScene = preload("res://addons/world_generator/ui/RectViewNode.tscn") + +export(NodePath) var zoom_widget_path : NodePath = "" + +var _rect_scale : float = 1 + +var edited_resource : WorldGenBaseResource = null +var edited_resource_current_size : Vector2 = Vector2() + +var _plugin : EditorPlugin = null +var _undo_redo : UndoRedo = null + +func _enter_tree(): + var zoom_widget : Node = get_node_or_null(zoom_widget_path) + + if !zoom_widget: + return + + if !zoom_widget.is_connected("zoom_changed", self, "on_zoom_changed"): + zoom_widget.connect("zoom_changed", self, "on_zoom_changed") + + if !is_connected("visibility_changed", self, "on_visibility_changed"): + connect("visibility_changed", self, "on_visibility_changed") + +func set_plugin(plugin : EditorPlugin) -> void: + _plugin = plugin + _undo_redo = _plugin.get_undo_redo() + +func on_visibility_changed() -> void: + call_deferred("apply_zoom") + +func apply_zoom() -> void: + if !edited_resource: + return + + var rect : Rect2 = edited_resource.rect + edited_resource_current_size = rect.size + rect.position = rect.position * _rect_scale + rect.size = rect.size * _rect_scale + set_custom_minimum_size(rect.size) + + var p : MarginContainer = get_parent() as MarginContainer + + p.add_theme_constant_override("margin_left", min(rect.size.x / 4.0, 50 * _rect_scale)) + p.add_theme_constant_override("margin_right", min(rect.size.x / 4.0, 50 * _rect_scale)) + p.add_theme_constant_override("margin_top", min(rect.size.y / 4.0, 50 * _rect_scale)) + p.add_theme_constant_override("margin_bottom", min(rect.size.y / 4.0, 50 * _rect_scale)) + + for c in get_children(): + c.set_editor_rect_scale(_rect_scale) + +func on_zoom_changed(zoom : float) -> void: + _rect_scale = zoom + apply_zoom() + +func _draw(): + draw_rect(Rect2(Vector2(), get_size()), Color(0.2, 0.2, 0.2, 1)) + + var rsh : float = clamp(_rect_scale / 2.0, 1, 5) + var c : Color = Color(0.4, 0.4, 0.4, 1) + + # Indicators that show the size of a unit (1 chunk) + + # Top left + draw_line(Vector2(_rect_scale, 0), Vector2(_rect_scale, rsh), c) + draw_line(Vector2(0, _rect_scale), Vector2(rsh, _rect_scale), c) + + # Top right + draw_line(Vector2(get_size().x - _rect_scale, 0), Vector2(get_size().x - _rect_scale, rsh), c) + draw_line(Vector2(get_size().x - rsh, _rect_scale), Vector2(get_size().x, _rect_scale), c) + + # Bottom left + draw_line(Vector2(_rect_scale, get_size().y - rsh), Vector2(_rect_scale, get_size().y), c) + draw_line(Vector2(0, get_size().y - _rect_scale), Vector2(rsh, get_size().y - _rect_scale), c) + + # Bottom right + draw_line(Vector2(get_size().x - _rect_scale, get_size().y - rsh), Vector2(get_size().x - _rect_scale, get_size().y), c) + draw_line(Vector2(get_size().x - rsh, get_size().y - _rect_scale), Vector2(get_size().x, get_size().y - _rect_scale), c) + + edited_resource.eitor_draw_additional_background(self) + +func refresh() -> void: + clear() + + if !edited_resource: + return + + var rect : Rect2 = edited_resource.rect + edited_resource_current_size = rect.size + rect.position = rect.position * _rect_scale + rect.size = rect.size * _rect_scale + set_custom_minimum_size(rect.size) + + apply_zoom() + + refresh_rects() + +func clear() -> void: + pass + +func refresh_rects() -> void: + clear_rects() + + if !edited_resource: + return + + var cont : Array = edited_resource.get_content() + + for c in cont: + if c: + var s : Node = rect_editor_node_scene.instance() + + add_child(s) + s.set_plugin(_plugin) + s.set_editor_rect_scale(_rect_scale) + s.edited_resource_parent_size = edited_resource_current_size + s.set_edited_resource(c) + +func clear_rects(): + for c in get_children(): + c.queue_free() + remove_child(c) + +func set_edited_resource(res : WorldGenBaseResource): + if edited_resource: + edited_resource.disconnect("changed", self, "on_edited_resource_changed") + + edited_resource = res + + refresh() + + if edited_resource: + edited_resource.connect("changed", self, "on_edited_resource_changed") + +func on_edited_resource_changed() -> void: + call_deferred("refresh") diff --git a/addons/world_generator/ui/RectViewNode.gd b/addons/world_generator/ui/RectViewNode.gd new file mode 100644 index 0000000..bbfbe82 --- /dev/null +++ b/addons/world_generator/ui/RectViewNode.gd @@ -0,0 +1,232 @@ +tool +extends MarginContainer + +enum DragType { + DRAG_NONE = 0, + DRAG_MOVE = 1, + DRAG_RESIZE_TOP = 1 << 1, + DRAG_RESIZE_RIGHT = 1 << 2, + DRAG_RESIZE_BOTTOM = 1 << 3, + DRAG_RESIZE_LEFT = 1 << 4 +}; + +var edited_resource : WorldGenBaseResource = null +var edited_resource_parent_size : Vector2 = Vector2() + +var _edited_resource_rect_border_color : Color = Color(1, 1, 1, 1) +var _edited_resource_rect_color : Color = Color(0.8, 0.8, 0.8, 0.9) +var _editor_rect_border_size : int = 2 +var _edited_resource_font_color : Color = Color(0, 0, 0, 1) +var _editor_additional_text : String = "" + +var drag_type : int +var drag_offset : Vector2 +var drag_offset_far : Vector2 + +var _rect_scale : float = 1 + +var _edited_resource_event_ignore : bool = false + +var _plugin : EditorPlugin = null +var _undo_redo : UndoRedo = null + +func set_plugin(plugin : EditorPlugin) -> void: + _plugin = plugin + _undo_redo = _plugin.get_undo_redo() + +func _draw(): + draw_rect(Rect2(Vector2(), get_size()), _edited_resource_rect_color) + draw_rect(Rect2(Vector2(), get_size()), _edited_resource_rect_border_color, false, _editor_rect_border_size) + + var font : Font = get_theme_font("font") + + var res_name : String = "NULL" + + if edited_resource: + res_name = edited_resource.resource_name + + var res_cls : String = "" + + if edited_resource: + res_cls = edited_resource.get_editor_class() + + draw_string(font, Vector2(_editor_rect_border_size, font.get_height()), res_name, _edited_resource_font_color) + + if res_cls != "": + draw_string(font, Vector2(_editor_rect_border_size, font.get_height() * 2), res_cls, _edited_resource_font_color, get_rect().size.x) + + if _editor_additional_text != "": + draw_string(font, Vector2(_editor_rect_border_size, font.get_height() * 3), _editor_additional_text, _edited_resource_font_color, get_rect().size.x) + + if edited_resource: + edited_resource.eitor_draw_additional(self) + +func refresh() -> void: + if !edited_resource: + return + + #anchor is bottom left here + var rect : Rect2 = edited_resource.get_rect() + rect.position *= _rect_scale + rect.size *= _rect_scale + + #anchor needs to be on top left here + var rp : Vector2 = rect.position + rp.y = edited_resource_parent_size.y * _rect_scale - rect.size.y - rect.position.y + rect_position = rp + rect_size = rect.size + + update() + +func set_editor_rect_scale(rect_scale) -> void: + _rect_scale = rect_scale + + refresh() + +func set_edited_resource(res : WorldGenBaseResource): + edited_resource = res + + if edited_resource: + _edited_resource_rect_border_color = edited_resource.get_editor_rect_border_color() + _edited_resource_rect_color = edited_resource.get_editor_rect_color() + _editor_rect_border_size = edited_resource.get_editor_rect_border_size() + _edited_resource_font_color = edited_resource.get_editor_font_color() + _editor_additional_text = edited_resource.get_editor_additional_text() + + edited_resource.connect("changed", self, "on_edited_resource_changed") + + refresh() + +func on_edited_resource_changed() -> void: + if _edited_resource_event_ignore: + return + + refresh() + +#based on / ported from engine/scene/gui/dialogs.h and .cpp +func _notification(p_what : int) -> void: + if (p_what == NOTIFICATION_MOUSE_EXIT): + # Reset the mouse cursor when leaving the resizable window border. + if (edited_resource && !edited_resource.locked && !drag_type): + if (get_default_cursor_shape() != CURSOR_ARROW): + set_default_cursor_shape(CURSOR_ARROW) + +#based on / ported from engine/scene/gui/dialogs.h and .cpp +func _gui_input(p_event : InputEvent) -> void: + if (p_event is InputEventMouseButton) && (p_event.get_button_index() == BUTTON_LEFT): + var mb : InputEventMouseButton = p_event as InputEventMouseButton + + if (mb.is_pressed()): + # Begin a possible dragging operation. + drag_type = _drag_hit_test(Vector2(mb.get_position().x, mb.get_position().y)) + + if (drag_type != DragType.DRAG_NONE): + drag_offset = get_global_mouse_position() - get_position() + + drag_offset_far = get_position() + get_size() - get_global_mouse_position() + + elif (drag_type != DragType.DRAG_NONE && !mb.is_pressed()): + # End a dragging operation. + drag_type = DragType.DRAG_NONE + + var rect : Rect2 = get_rect() + #rect needs to be converted back + rect.position.y = edited_resource_parent_size.y * _rect_scale - rect.size.y - rect.position.y + rect.position /= _rect_scale + rect.size /= _rect_scale + + #edited_resource.set_rect(rect) + _edited_resource_event_ignore = true + _undo_redo.create_action("WE: Drag End") + _undo_redo.add_do_method(edited_resource, "set_rect", rect) + _undo_redo.add_undo_method(edited_resource, "set_rect", edited_resource.get_rect()) + _undo_redo.commit_action() + _edited_resource_event_ignore = false + + if p_event is InputEventMouseMotion: + var mm : InputEventMouseMotion = p_event as InputEventMouseMotion + + if (drag_type == DragType.DRAG_NONE): + # Update the cursor while moving along the borders. + var cursor = CURSOR_ARROW + if (!edited_resource.locked): + var preview_drag_type : int = _drag_hit_test(Vector2(mm.get_position().x, mm.get_position().y)) + + var top_left : int = DragType.DRAG_RESIZE_TOP + DragType.DRAG_RESIZE_LEFT + var bottom_right : int = DragType.DRAG_RESIZE_BOTTOM + DragType.DRAG_RESIZE_RIGHT + var top_right : int = DragType.DRAG_RESIZE_TOP + DragType.DRAG_RESIZE_RIGHT + var bottom_left : int = DragType.DRAG_RESIZE_BOTTOM + DragType.DRAG_RESIZE_LEFT + + match (preview_drag_type): + DragType.DRAG_RESIZE_TOP: + cursor = CURSOR_VSIZE + DragType.DRAG_RESIZE_BOTTOM: + cursor = CURSOR_VSIZE + DragType.DRAG_RESIZE_LEFT: + cursor = CURSOR_HSIZE + DragType.DRAG_RESIZE_RIGHT: + cursor = CURSOR_HSIZE + top_left: + cursor = CURSOR_FDIAGSIZE + bottom_right: + cursor = CURSOR_FDIAGSIZE + top_right: + cursor = CURSOR_BDIAGSIZE + bottom_left: + cursor = CURSOR_BDIAGSIZE + + if (get_cursor_shape() != cursor): + set_default_cursor_shape(cursor); + + else: + # Update while in a dragging operation. + var global_pos : Vector2 = get_global_mouse_position() + + var rect : Rect2 = get_rect() + var min_size : Vector2 = get_combined_minimum_size() + + if (drag_type == DragType.DRAG_MOVE): + rect.position = global_pos - drag_offset + else: + if (drag_type & DragType.DRAG_RESIZE_TOP): + var bottom : int = rect.position.y + rect.size.y + var max_y : int = bottom - min_size.y + rect.position.y = min(global_pos.y - drag_offset.y, max_y) + rect.size.y = bottom - rect.position.y + elif (drag_type & DragType.DRAG_RESIZE_BOTTOM): + rect.size.y = global_pos.y - rect.position.y + drag_offset_far.y + + if (drag_type & DragType.DRAG_RESIZE_LEFT): + var right : int = rect.position.x + rect.size.x + var max_x : int = right - min_size.x + rect.position.x = min(global_pos.x - drag_offset.x, max_x) + rect.size.x = right - rect.position.x + elif (drag_type & DragType.DRAG_RESIZE_RIGHT): + rect.size.x = global_pos.x - rect.position.x + drag_offset_far.x + + set_size(rect.size) + set_position(rect.position) + +#based on / ported from engine/scene/gui/dialogs.h and .cpp +func _drag_hit_test(pos : Vector2) -> int: + var drag_type : int = DragType.DRAG_NONE + + if (!edited_resource.locked): + var scaleborder_size : int = 5 #get_constant("scaleborder_size", "WindowDialog") + + var rect : Rect2 = get_rect() + + if (pos.y < (scaleborder_size)): + drag_type = DragType.DRAG_RESIZE_TOP + elif (pos.y >= (rect.size.y - scaleborder_size)): + drag_type = DragType.DRAG_RESIZE_BOTTOM + + if (pos.x < scaleborder_size): + drag_type |= DragType.DRAG_RESIZE_LEFT + elif (pos.x >= (rect.size.x - scaleborder_size)): + drag_type |= DragType.DRAG_RESIZE_RIGHT + + if (drag_type == DragType.DRAG_NONE): + drag_type = DragType.DRAG_MOVE + + return drag_type diff --git a/addons/world_generator/ui/RectViewNode.tscn b/addons/world_generator/ui/RectViewNode.tscn new file mode 100644 index 0000000..a1f7886 --- /dev/null +++ b/addons/world_generator/ui/RectViewNode.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/world_generator/ui/RectViewNode.gd" type="Script" id=1] + +[node name="RectViewNode" type="MarginContainer"] +margin_right = 1024.0 +margin_bottom = 600.0 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} diff --git a/addons/world_generator/ui/ResourcePropertyList.gd b/addons/world_generator/ui/ResourcePropertyList.gd new file mode 100644 index 0000000..5a4783d --- /dev/null +++ b/addons/world_generator/ui/ResourcePropertyList.gd @@ -0,0 +1,540 @@ +tool +extends ScrollContainer + +var EditorResourceWidget : PackedScene = preload("res://addons/world_generator/widgets/EditorResourceWidget.tscn") + +var _edited_resource : WorldGenBaseResource = null +var properties : Array = Array() + +var _ignore_changed_evend : bool = false +var _refresh_queued : bool = false + +var _plugin : EditorPlugin = null +var _undo_redo : UndoRedo = null + +func set_plugin(plugin : EditorPlugin) -> void: + _plugin = plugin + _undo_redo = _plugin.get_undo_redo() + +func add_h_separator() -> int: + var hsep : HSeparator = HSeparator.new() + + var content_node = $MainContainer/Content + + content_node.add_child(hsep) + var slot_idx : int = content_node.get_child_count() - 1 + + return slot_idx + +func add_slot_color(getter : String, setter : String) -> int: + var cp : ColorPickerButton = ColorPickerButton.new() + + var slot_idx : int = add_slot(getter, setter, cp) + + cp.color = _edited_resource.call(getter) + + cp.connect("color_changed", _edited_resource, setter) + + return slot_idx + +func add_slot_label(getter : String, setter : String, slot_name : String) -> int: + var l : Label = Label.new() + + l.text = slot_name + + return add_slot(getter, setter, l) + +func add_slot_resource(getter : String, setter : String, slot_name : String, resource_type : String = "Resource") -> int: + var bc : HBoxContainer = HBoxContainer.new() + bc.set_h_size_flags(SIZE_EXPAND_FILL) + + var l : Label = Label.new() + l.text = slot_name + bc.add_child(l) + + var r : Control = EditorResourceWidget.instance() + r.set_plugin(_plugin) + r.set_resource_type(resource_type) + r.set_resource(_edited_resource.call(getter)) + r.set_h_size_flags(SIZE_EXPAND_FILL) + + bc.add_child(r) + + var slot_idx : int = add_slot(getter, setter, bc) + + r.connect("on_resource_changed", self, "on_widget_resource_changed", [ slot_idx ]) + + return slot_idx + +func add_slot_line_edit(getter : String, setter : String, slot_name : String, placeholder : String = "") -> int: + var bc : HBoxContainer = HBoxContainer.new() + bc.set_h_size_flags(SIZE_EXPAND_FILL) + + var l : Label = Label.new() + l.text = slot_name + bc.add_child(l) + + var le : LineEdit = LineEdit.new() + le.placeholder_text = placeholder + le.set_h_size_flags(SIZE_EXPAND_FILL) + bc.add_child(le) + + var slot_idx : int = add_slot(getter, setter, bc) + + le.text = _edited_resource.call(getter) + + le.connect("text_entered", self, "on_slot_line_edit_text_entered", [ slot_idx ]) + + return slot_idx + +func add_slot_enum(getter : String, setter : String, slot_name : String, values : Array) -> int: + var bc : VBoxContainer = VBoxContainer.new() + + if slot_name: + var l : Label = Label.new() + l.text = slot_name + bc.add_child(l) + + var mb : OptionButton = OptionButton.new() + + for v in values: + mb.add_item(v) + + bc.add_child(mb) + + var slot_idx : int = add_slot(getter, setter, bc) + + mb.selected = _edited_resource.call(getter) + + mb.connect("item_selected", self, "on_slot_enum_item_selected", [ slot_idx ]) + + return slot_idx + +func add_slot_int(getter : String, setter : String, slot_name : String, prange : Vector2 = Vector2(-1000, 1000)) -> int: + var bc : HBoxContainer = HBoxContainer.new() + + var l : Label = Label.new() + l.text = slot_name +# l.size_flags_horizontal = SIZE_EXPAND_FILL + bc.add_child(l) + + var sb : SpinBox = SpinBox.new() + sb.rounded = true + sb.min_value = prange.x + sb.max_value = prange.y + sb.set_h_size_flags(SIZE_EXPAND_FILL) + bc.add_child(sb) + + var slot_idx : int = add_slot(getter, setter, bc) + + sb.value = _edited_resource.call(getter) + + sb.connect("value_changed", self, "on_int_spinbox_value_changed", [ slot_idx ]) + + return slot_idx + +func add_slot_bool(getter : String, setter : String, slot_name : String) -> int: + var cb : CheckBox = CheckBox.new() + cb.text = slot_name + + var slot_idx : int = add_slot(getter, setter, cb) + + cb.pressed = _edited_resource.call(getter) + + cb.connect("toggled", self, "on_checkbox_value_changed", [ slot_idx ]) + + return slot_idx + +func add_slot_float(getter : String, setter : String, slot_name : String, step : float = 0.1, prange : Vector2 = Vector2(-1000, 1000)) -> int: + var bc : HBoxContainer = HBoxContainer.new() + + var l : Label = Label.new() + l.text = slot_name +# l.size_flags_horizontal = SIZE_EXPAND_FILL + bc.add_child(l) + + var sb : SpinBox = SpinBox.new() + bc.add_child(sb) + + var slot_idx : int = add_slot(getter, setter, bc) + sb.rounded = false + sb.step = step + sb.min_value = prange.x + sb.max_value = prange.y + sb.value = _edited_resource.call(getter) + sb.set_h_size_flags(SIZE_EXPAND_FILL) + + sb.connect("value_changed", self, "on_float_spinbox_value_changed", [ slot_idx ]) + + return slot_idx + +func add_slot_vector2(getter : String, setter : String, slot_name : String, step : float = 0.1, prange : Vector2 = Vector2(-1000, 1000)) -> int: + var bc : VBoxContainer = VBoxContainer.new() + + var l : Label = Label.new() + l.text = slot_name + bc.add_child(l) + + var sbx : SpinBox = SpinBox.new() + bc.add_child(sbx) + + var sby : SpinBox = SpinBox.new() + bc.add_child(sby) + + var slot_idx : int = add_slot(getter, setter, bc) + sbx.rounded = false + sby.rounded = false + sbx.step = step + sby.step = step + sbx.min_value = prange.x + sbx.max_value = prange.y + sby.min_value = prange.x + sby.max_value = prange.y + + var val : Vector2 = _edited_resource.call(getter) + + sbx.value = val.x + sby.value = val.y + + sbx.connect("value_changed", self, "on_vector2_spinbox_value_changed", [ slot_idx, sbx, sby ]) + sby.connect("value_changed", self, "on_vector2_spinbox_value_changed", [ slot_idx, sbx, sby ]) + + return slot_idx + +func add_slot_vector3(getter : String, setter : String, slot_name : String, step : float = 0.1, prange : Vector2 = Vector2(-1000, 1000)) -> int: + var bc : VBoxContainer = VBoxContainer.new() + + var l : Label = Label.new() + l.text = slot_name + bc.add_child(l) + + var sbx : SpinBox = SpinBox.new() + bc.add_child(sbx) + + var sby : SpinBox = SpinBox.new() + bc.add_child(sby) + + var sbz : SpinBox = SpinBox.new() + bc.add_child(sbz) + + var slot_idx : int = add_slot(getter, setter, bc) + sbx.rounded = false + sby.rounded = false + sbz.rounded = false + sbx.step = step + sby.step = step + sbz.step = step + sbx.min_value = prange.x + sbx.max_value = prange.y + sby.min_value = prange.x + sby.max_value = prange.y + sbz.min_value = prange.x + sbz.max_value = prange.y + + var val : Vector3 = _edited_resource.call(getter) + + sbx.value = val.x + sby.value = val.y + sbz.value = val.z + + sbx.connect("value_changed", self, "on_vector3_spinbox_value_changed", [ slot_idx, sbx, sby, sbz ]) + sby.connect("value_changed", self, "on_vector3_spinbox_value_changed", [ slot_idx, sbx, sby, sbz ]) + sbz.connect("value_changed", self, "on_vector3_spinbox_value_changed", [ slot_idx, sbx, sby, sbz ]) + + return slot_idx + + +func add_slot_rect2(getter : String, setter : String, slot_name : String, step : float = 0.1, prange : Vector2 = Vector2(-10000, 10000)) -> int: + var bc : VBoxContainer = VBoxContainer.new() + bc.size_flags_horizontal = SIZE_EXPAND_FILL + + var l : Label = Label.new() + l.text = slot_name + bc.add_child(l) + + var hc1 : HBoxContainer = HBoxContainer.new() + hc1.size_flags_horizontal = SIZE_EXPAND_FILL + bc.add_child(hc1) + + var sbx : SpinBox = SpinBox.new() + sbx.size_flags_horizontal = SIZE_EXPAND_FILL + hc1.add_child(sbx) + + var sby : SpinBox = SpinBox.new() + sby.size_flags_horizontal = SIZE_EXPAND_FILL + hc1.add_child(sby) + + var hc2 : HBoxContainer = HBoxContainer.new() + hc2.size_flags_horizontal = SIZE_EXPAND_FILL + bc.add_child(hc2) + + var sbw : SpinBox = SpinBox.new() + sbw.size_flags_horizontal = SIZE_EXPAND_FILL + hc2.add_child(sbw) + + var sbh : SpinBox = SpinBox.new() + sbh.size_flags_horizontal = SIZE_EXPAND_FILL + hc2.add_child(sbh) + + var slot_idx : int = add_slot(getter, setter, bc) + sbx.rounded = false + sby.rounded = false + sbw.rounded = false + sbh.rounded = false + sbx.step = step + sby.step = step + sbw.step = step + sbh.step = step + sbx.min_value = prange.x + sbx.max_value = prange.y + sby.min_value = prange.x + sby.max_value = prange.y + sbw.min_value = prange.x + sbw.max_value = prange.y + sbh.min_value = prange.x + sbh.max_value = prange.y + + var val : Rect2 = _edited_resource.call(getter) + + sbx.value = val.position.x + sby.value = val.position.y + sbw.value = val.size.x + sbh.value = val.size.y + + sbx.connect("value_changed", self, "on_rect2_spinbox_value_changed", [ slot_idx, [ sbx, sby, sbw, sbh ] ]) + sby.connect("value_changed", self, "on_rect2_spinbox_value_changed", [ slot_idx, [ sbx, sby, sbw, sbh ] ]) + sbw.connect("value_changed", self, "on_rect2_spinbox_value_changed", [ slot_idx, [ sbx, sby, sbw, sbh ] ]) + sbh.connect("value_changed", self, "on_rect2_spinbox_value_changed", [ slot_idx, [ sbx, sby, sbw, sbh ] ]) + + return slot_idx + +func add_slot_vector2i(getter : String, setter : String, slot_name : String, step : int = 1, prange : Vector2i = Vector2i(-1000000, 1000000)) -> int: + var bc : VBoxContainer = VBoxContainer.new() + + var l : Label = Label.new() + l.text = slot_name + bc.add_child(l) + + var sbx : SpinBox = SpinBox.new() + bc.add_child(sbx) + + var sby : SpinBox = SpinBox.new() + bc.add_child(sby) + + var slot_idx : int = add_slot(getter, setter, bc) + sbx.rounded = true + sby.rounded = true + sbx.step = step + sby.step = step + sbx.min_value = prange.x + sbx.max_value = prange.y + sby.min_value = prange.x + sby.max_value = prange.y + + var val : Vector2 = _edited_resource.call(getter) + + sbx.value = val.x + sby.value = val.y + + sbx.connect("value_changed", self, "on_vector2i_spinbox_value_changed", [ slot_idx, sbx, sby ]) + sby.connect("value_changed", self, "on_vector2i_spinbox_value_changed", [ slot_idx, sbx, sby ]) + + return slot_idx + +func add_slot(getter : String, setter : String, control : Control) -> int: + var content_node = $MainContainer/Content + + content_node.add_child(control) + var child_idx : int = content_node.get_child_count() - 1 + + var arr : Array = Array() + + arr.append(child_idx) + arr.append(getter) + arr.append(setter) + arr.append(control) + + properties.append(arr) + + var slot_idx : int = properties.size() - 1 + + return slot_idx + +func get_property_control(slot_idx : int) -> Node: + return properties[slot_idx][3] + +func on_int_spinbox_value_changed(val : float, slot_idx) -> void: + _ignore_changed_evend = true + + #_edited_resource.call(properties[slot_idx][2], int(val)) + + _undo_redo.create_action("WE: Set Value") + _undo_redo.add_do_method(_edited_resource, properties[slot_idx][2], int(val)) + _undo_redo.add_undo_method(_edited_resource, properties[slot_idx][2], _edited_resource.call(properties[slot_idx][1])) + _undo_redo.commit_action() + + _ignore_changed_evend = false + +func on_checkbox_value_changed(val : bool, slot_idx) -> void: + _ignore_changed_evend = true + + #_edited_resource.call(properties[slot_idx][2], val) + + _undo_redo.create_action("WE: Set Value") + _undo_redo.add_do_method(_edited_resource, properties[slot_idx][2], val) + _undo_redo.add_undo_method(_edited_resource, properties[slot_idx][2], _edited_resource.call(properties[slot_idx][1])) + _undo_redo.commit_action() + + _ignore_changed_evend = false + +func on_float_spinbox_value_changed(val : float, slot_idx) -> void: + _ignore_changed_evend = true + + #_edited_resource.call(properties[slot_idx][2], val) + + _undo_redo.create_action("WE: Set Value") + _undo_redo.add_do_method(_edited_resource, properties[slot_idx][2], val) + _undo_redo.add_undo_method(_edited_resource, properties[slot_idx][2], _edited_resource.call(properties[slot_idx][1])) + _undo_redo.commit_action() + + _ignore_changed_evend = false + +func on_vector2_spinbox_value_changed(val : float, slot_idx, spinbox_x, spinbox_y) -> void: + _ignore_changed_evend = true + var vv : Vector2 = Vector2(spinbox_x.value, spinbox_y.value) + + #_edited_resource.call(properties[slot_idx][2], vv) + + _undo_redo.create_action("WE: Set Value") + _undo_redo.add_do_method(_edited_resource, properties[slot_idx][2], vv) + _undo_redo.add_undo_method(_edited_resource, properties[slot_idx][2], _edited_resource.call(properties[slot_idx][1])) + _undo_redo.commit_action() + + _ignore_changed_evend = false + +func on_vector3_spinbox_value_changed(val : float, slot_idx, spinbox_x, spinbox_y, spinbox_z) -> void: + _ignore_changed_evend = true + var vv : Vector3 = Vector3(spinbox_x.value, spinbox_y.value, spinbox_z.value) + + #_edited_resource.call(properties[slot_idx][2], vv) + + _undo_redo.create_action("WE: Set Value") + _undo_redo.add_do_method(_edited_resource, properties[slot_idx][2], vv) + _undo_redo.add_undo_method(_edited_resource, properties[slot_idx][2], _edited_resource.call(properties[slot_idx][1])) + _undo_redo.commit_action() + + _ignore_changed_evend = false + +func on_rect2_spinbox_value_changed(val : float, slot_idx, spinboxes) -> void: + _ignore_changed_evend = true + var vv : Rect2 = Rect2(spinboxes[0].value, spinboxes[1].value, spinboxes[2].value, spinboxes[3].value) + + #_edited_resource.call(properties[slot_idx][2], vv) + + _undo_redo.create_action("WE: Set Value") + _undo_redo.add_do_method(_edited_resource, properties[slot_idx][2], vv) + _undo_redo.add_undo_method(_edited_resource, properties[slot_idx][2], _edited_resource.call(properties[slot_idx][1])) + _undo_redo.commit_action() + + _ignore_changed_evend = false + +func on_vector2i_spinbox_value_changed(val : float, slot_idx, spinbox_x, spinbox_y) -> void: + _ignore_changed_evend = true + var vv : Vector2i = Vector2i(spinbox_x.value, spinbox_y.value) + + #_edited_resource.call(properties[slot_idx][2], vv) + + _undo_redo.create_action("WE: Set Value") + _undo_redo.add_do_method(_edited_resource, properties[slot_idx][2], vv) + _undo_redo.add_undo_method(_edited_resource, properties[slot_idx][2], _edited_resource.call(properties[slot_idx][1])) + _undo_redo.commit_action() + + _ignore_changed_evend = false + +func on_slot_enum_item_selected(val : int, slot_idx : int) -> void: + _ignore_changed_evend = true + #_edited_resource.call(properties[slot_idx][2], val) + + _undo_redo.create_action("WE: Set Value") + _undo_redo.add_do_method(_edited_resource, properties[slot_idx][2], val) + _undo_redo.add_undo_method(_edited_resource, properties[slot_idx][2], _edited_resource.call(properties[slot_idx][1])) + _undo_redo.commit_action() + + _ignore_changed_evend = false + +func on_slot_line_edit_text_entered(text : String, slot_idx : int) -> void: + _ignore_changed_evend = true + #_edited_resource.call(properties[slot_idx][2], text) + + _undo_redo.create_action("WE: Set Value") + _undo_redo.add_do_method(_edited_resource, properties[slot_idx][2], text) + _undo_redo.add_undo_method(_edited_resource, properties[slot_idx][2], _edited_resource.call(properties[slot_idx][1])) + _undo_redo.commit_action() + + _ignore_changed_evend = false + +func on_widget_resource_changed(res : Resource, slot_idx : int) -> void: + _ignore_changed_evend = true + #_edited_resource.call(properties[slot_idx][2], res) + + _undo_redo.create_action("WE: Set Value") + _undo_redo.add_do_method(_edited_resource, properties[slot_idx][2], res) + _undo_redo.add_undo_method(_edited_resource, properties[slot_idx][2], _edited_resource.call(properties[slot_idx][1])) + _undo_redo.commit_action() + + _ignore_changed_evend = false + +func clear() -> void: + properties.clear() + + var content_node = $MainContainer/Content + + if !content_node: + return + + for c in content_node.get_children(): + c.queue_free() + content_node.remove_child(c) + +func refresh() -> void: + clear() + + var cls_str : String = "[none]" + var script_str : String = "[none]" + + if _edited_resource: + cls_str = _edited_resource.get_class() + + var scr = _edited_resource.get_script() + + if scr: + script_str = scr.resource_path + + _edited_resource.setup_property_inspector(self) + + $MainContainer/HBoxContainer/ClassLE.text = cls_str + $MainContainer/HBoxContainer2/ScriptLE.text = script_str + + _refresh_queued = false + +func edit_resource(wgw) -> void: + if _edited_resource: + _edited_resource.disconnect("changed", self, "on_edited_resource_changed") + + _edited_resource = wgw + + #if !_edited_resource.is_connected("changed", self, "on_edited_resource_changed"): + if _edited_resource: + _edited_resource.connect("changed", self, "on_edited_resource_changed") + + refresh() + +func on_edited_resource_changed() -> void: + if _ignore_changed_evend: + return + + if _refresh_queued: + return + + _refresh_queued = true + call_deferred("refresh") diff --git a/addons/world_generator/ui/ResourcePropertyList.tscn b/addons/world_generator/ui/ResourcePropertyList.tscn new file mode 100644 index 0000000..b97e3b5 --- /dev/null +++ b/addons/world_generator/ui/ResourcePropertyList.tscn @@ -0,0 +1,81 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/world_generator/ui/ResourcePropertyList.gd" type="Script" id=1] + +[node name="ResourcePropertyList" type="ScrollContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +rect_min_size = Vector2( 100, 0 ) +scroll_horizontal_enabled = false +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="MainContainer" type="VBoxContainer" parent="."] +margin_right = 1024.0 +margin_bottom = 90.0 +size_flags_horizontal = 3 + +[node name="Label" type="Label" parent="MainContainer"] +margin_right = 1024.0 +margin_bottom = 14.0 +text = "Properties" +align = 1 +valign = 1 + +[node name="HSeparator" type="HSeparator" parent="MainContainer"] +margin_top = 18.0 +margin_right = 1024.0 +margin_bottom = 22.0 + +[node name="Content" type="VBoxContainer" parent="MainContainer"] +margin_top = 26.0 +margin_right = 1024.0 +margin_bottom = 26.0 +size_flags_horizontal = 3 + +[node name="HSeparator2" type="HSeparator" parent="MainContainer"] +margin_top = 30.0 +margin_right = 1024.0 +margin_bottom = 34.0 + +[node name="HBoxContainer" type="HBoxContainer" parent="MainContainer"] +margin_top = 38.0 +margin_right = 1024.0 +margin_bottom = 62.0 +size_flags_horizontal = 3 + +[node name="Label" type="Label" parent="MainContainer/HBoxContainer"] +margin_top = 5.0 +margin_right = 37.0 +margin_bottom = 19.0 +text = "Class " +valign = 1 + +[node name="ClassLE" type="LineEdit" parent="MainContainer/HBoxContainer"] +margin_left = 41.0 +margin_right = 1024.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +editable = false + +[node name="HBoxContainer2" type="HBoxContainer" parent="MainContainer"] +margin_top = 66.0 +margin_right = 1024.0 +margin_bottom = 90.0 +size_flags_horizontal = 3 + +[node name="Label" type="Label" parent="MainContainer/HBoxContainer2"] +margin_top = 5.0 +margin_right = 36.0 +margin_bottom = 19.0 +text = "Script" +valign = 1 + +[node name="ScriptLE" type="LineEdit" parent="MainContainer/HBoxContainer2"] +margin_left = 40.0 +margin_right = 1024.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +editable = false diff --git a/addons/world_generator/ui/WorldTab.gd b/addons/world_generator/ui/WorldTab.gd new file mode 100644 index 0000000..1dea5b6 --- /dev/null +++ b/addons/world_generator/ui/WorldTab.gd @@ -0,0 +1,29 @@ +tool +extends HBoxContainer + +var edited_world + +signal request_item_edit(world_gen_base_resource) + +func _ready(): + var dl : Control = get_node("VBoxContainer/DataList") + if !dl.is_connected("request_item_edit", self, "on_request_item_edit"): + dl.connect("request_item_edit", self, "on_request_item_edit") + +func set_plugin(plugin : EditorPlugin) -> void: + $HSplitContainer/ResourcePropertyList.set_plugin(plugin) + $HSplitContainer/RectEditor.set_plugin(plugin) + $VBoxContainer/DataList.set_plugin(plugin) + +func refresh() -> void: + $HSplitContainer/ResourcePropertyList.edit_resource(edited_world) + $VBoxContainer/DataList.set_edited_resource(edited_world) + $HSplitContainer/RectEditor.set_edited_resource(edited_world) + +func set_wgworld(wgw : WorldGenWorld) -> void: + edited_world = wgw + + refresh() + +func on_request_item_edit(resource : WorldGenBaseResource) -> void: + emit_signal("request_item_edit", resource) diff --git a/addons/world_generator/ui/tabs/Continent.gd b/addons/world_generator/ui/tabs/Continent.gd new file mode 100644 index 0000000..5cf15fc --- /dev/null +++ b/addons/world_generator/ui/tabs/Continent.gd @@ -0,0 +1,78 @@ +tool +extends HBoxContainer + +var edited_world : WorldGenWorld = null +var edited_continent : Continent = null + +signal request_item_edit(continent, world_gen_base_resource) + +func _ready(): + var option_button : OptionButton = $HSplitContainer/VBoxContainer/OptionButton + option_button.connect("item_selected", self, "on_item_selected") + + var dl : Control = get_node("HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList") + if !dl.is_connected("request_item_edit", self, "on_request_item_edit"): + dl.connect("request_item_edit", self, "on_request_item_edit") + + +func set_plugin(plugin : EditorPlugin) -> void: + $HSplitContainer/VBoxContainer/HBoxContainer2/ResourcePropertyList.set_plugin(plugin) + $HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList.set_plugin(plugin) + $HSplitContainer/RectEditor.set_plugin(plugin) + +func refresh_continent() -> void: + $HSplitContainer/VBoxContainer/HBoxContainer2/ResourcePropertyList.edit_resource(edited_continent) + $HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList.set_edited_resource(edited_continent) + $HSplitContainer/RectEditor.set_edited_resource(edited_continent) + +# if !edited_continent: +# return + +func refresh() -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/OptionButton + option_button.clear() + + if !edited_world: + return + + var content : Array = edited_world.get_content() + + for c in content: + if c: + option_button.add_item(c.resource_name) + option_button.set_item_metadata(option_button.get_item_count() - 1, c) + + if !edited_continent: + edited_continent = c + + refresh_continent() + +func set_wgworld(wgw : WorldGenWorld) -> void: + edited_world = wgw + + refresh() + +func switch_to(resource : WorldGenBaseResource) -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/OptionButton + + for i in range(option_button.get_item_count()): + var continent : Continent = option_button.get_item_metadata(i) + + if (continent == resource): + option_button.select(i) + set_continent(continent) + return + +func set_continent(continent : Continent) -> void: + edited_continent = continent + + refresh_continent() + +func on_item_selected(idx : int) -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/OptionButton + + set_continent(option_button.get_item_metadata(idx)) + +func on_request_item_edit(resource : WorldGenBaseResource) -> void: + emit_signal("request_item_edit", edited_continent, resource) + diff --git a/addons/world_generator/ui/tabs/Continent.tscn b/addons/world_generator/ui/tabs/Continent.tscn new file mode 100644 index 0000000..8102c2f --- /dev/null +++ b/addons/world_generator/ui/tabs/Continent.tscn @@ -0,0 +1,100 @@ +[gd_scene load_steps=5 format=2] + +[ext_resource path="res://addons/world_generator/ui/tabs/Continent.gd" type="Script" id=1] +[ext_resource path="res://addons/world_generator/ui/ResourcePropertyList.tscn" type="PackedScene" id=2] +[ext_resource path="res://addons/world_generator/ui/DataList.tscn" type="PackedScene" id=3] +[ext_resource path="res://addons/world_generator/ui/RectEditor.tscn" type="PackedScene" id=4] + +[node name="Continent" type="HBoxContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="HSplitContainer" type="HSplitContainer" parent="."] +margin_right = 1024.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 + +[node name="RectEditor" parent="HSplitContainer" instance=ExtResource( 4 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 735.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer"] +margin_left = 747.0 +margin_right = 1024.0 +margin_bottom = 600.0 + +[node name="OptionButton" type="OptionButton" parent="HSplitContainer/VBoxContainer"] +margin_right = 277.0 +margin_bottom = 20.0 + +[node name="HBoxContainer2" type="HBoxContainer" parent="HSplitContainer/VBoxContainer"] +margin_top = 24.0 +margin_right = 277.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ResourcePropertyList" parent="HSplitContainer/VBoxContainer/HBoxContainer2" instance=ExtResource( 2 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 100.0 +margin_bottom = 576.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer/VBoxContainer/HBoxContainer2"] +margin_left = 104.0 +margin_right = 277.0 +margin_bottom = 576.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Label" type="Label" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer"] +margin_right = 173.0 +margin_bottom = 14.0 +text = "Zones" +align = 1 +valign = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer"] +margin_top = 18.0 +margin_right = 173.0 +margin_bottom = 38.0 + +[node name="AddButton" type="Button" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer"] +margin_right = 37.0 +margin_bottom = 20.0 +text = "Add" + +[node name="DeleteButton" type="Button" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer"] +margin_left = 41.0 +margin_right = 96.0 +margin_bottom = 20.0 +text = "Delete" + +[node name="Duplicate" type="Button" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer"] +margin_left = 100.0 +margin_right = 173.0 +margin_bottom = 20.0 +text = "Duplicate" + +[node name="DataList" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer" instance=ExtResource( 3 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 42.0 +margin_right = 173.0 +margin_bottom = 576.0 +class_types = 1 + +[connection signal="pressed" from="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer/AddButton" to="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList" method="add_button_pressed"] +[connection signal="pressed" from="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer/DeleteButton" to="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList" method="delete_button_pressed"] +[connection signal="pressed" from="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer/Duplicate" to="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList" method="duplicate_button_pressed"] diff --git a/addons/world_generator/ui/tabs/SubZone.gd b/addons/world_generator/ui/tabs/SubZone.gd new file mode 100644 index 0000000..9474bf7 --- /dev/null +++ b/addons/world_generator/ui/tabs/SubZone.gd @@ -0,0 +1,168 @@ +tool +extends HBoxContainer + +var edited_world : WorldGenWorld = null +var edited_continent : Continent = null +var edited_zone : Zone = null +var edited_sub_zone : SubZone = null + +signal request_item_edit(continent, zone, subzone, subzone_prop) + +func _ready(): + var coption_button : OptionButton = $HSplitContainer/VBoxContainer/ContinentOptionButton + coption_button.connect("item_selected", self, "on_continent_item_selected") + + var zoption_button : OptionButton = $HSplitContainer/VBoxContainer/ZoneOptionButton + zoption_button.connect("item_selected", self, "on_zone_item_selected") + + var szoption_button : OptionButton = $HSplitContainer/VBoxContainer/SubZoneOptionButton + szoption_button.connect("item_selected", self, "on_sub_zone_item_selected") + + var dl : Control = get_node("HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList") + if !dl.is_connected("request_item_edit", self, "on_request_item_edit"): + dl.connect("request_item_edit", self, "on_request_item_edit") + +func set_plugin(plugin : EditorPlugin) -> void: + $HSplitContainer/VBoxContainer/HBoxContainer2/ResourcePropertyList.set_plugin(plugin) + $HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList.set_plugin(plugin) + $HSplitContainer/RectEditor.set_plugin(plugin) + +func refresh() -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/ContinentOptionButton + option_button.clear() + edited_continent = null + edited_zone = null + + if !edited_world: + return + + var content : Array = edited_world.get_content() + + for c in content: + if c: + option_button.add_item(c.resource_name) + option_button.set_item_metadata(option_button.get_item_count() - 1, c) + + if !edited_continent: + edited_continent = c + + continent_changed() + +func continent_changed() -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/ZoneOptionButton + option_button.clear() + edited_zone = null + + if !edited_continent: + return + + var content : Array = edited_continent.get_content() + + for c in content: + if c: + option_button.add_item(c.resource_name) + option_button.set_item_metadata(option_button.get_item_count() - 1, c) + + if !edited_zone: + edited_zone = c + + zone_changed() + +func zone_changed() -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/SubZoneOptionButton + option_button.clear() + edited_sub_zone = null + + if !edited_zone: + return + + var content : Array = edited_zone.get_content() + + for c in content: + if c: + option_button.add_item(c.resource_name) + option_button.set_item_metadata(option_button.get_item_count() - 1, c) + + if !edited_sub_zone: + edited_sub_zone = c + + sub_zone_changed() + + +func sub_zone_changed() -> void: + $HSplitContainer/VBoxContainer/HBoxContainer2/ResourcePropertyList.edit_resource(edited_sub_zone) + $HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList.set_edited_resource(edited_sub_zone) + $HSplitContainer/RectEditor.set_edited_resource(edited_sub_zone) + +func set_continent(continent : Continent) -> void: + edited_continent = continent + edited_zone = null + + continent_changed() + +func set_zone(zone : Zone) -> void: + edited_zone = zone + + zone_changed() + +func set_sub_zone(sub_zone : SubZone) -> void: + edited_sub_zone = sub_zone + + sub_zone_changed() + +func set_wgworld(wgw : WorldGenWorld) -> void: + edited_world = wgw + edited_continent = null + edited_zone = null + + refresh() + +func switch_to(continent : WorldGenBaseResource, zone : WorldGenBaseResource, subzone : WorldGenBaseResource) -> void: + var contob : OptionButton = $HSplitContainer/VBoxContainer/ContinentOptionButton + + for i in range(contob.get_item_count()): + var ccont : Continent = contob.get_item_metadata(i) + + if (ccont == continent): + contob.select(i) + set_continent(continent) + break + + var zoneob : OptionButton = $HSplitContainer/VBoxContainer/ZoneOptionButton + + for i in range(zoneob.get_item_count()): + var czone : Zone = zoneob.get_item_metadata(i) + + if (czone == zone): + zoneob.select(i) + set_zone(zone) + break + + var subzoneob : OptionButton = $HSplitContainer/VBoxContainer/SubZoneOptionButton + + for i in range(subzoneob.get_item_count()): + var cszone : SubZone = subzoneob.get_item_metadata(i) + + if (cszone == subzone): + subzoneob.select(i) + set_sub_zone(subzone) + return + +func on_continent_item_selected(idx : int) -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/ContinentOptionButton + + set_continent(option_button.get_item_metadata(idx)) + +func on_zone_item_selected(idx : int) -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/ZoneOptionButton + + set_zone(option_button.get_item_metadata(idx)) + +func on_sub_zone_item_selected(idx : int) -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/SubZoneOptionButton + + set_sub_zone(option_button.get_item_metadata(idx)) + +func on_request_item_edit(resource : WorldGenBaseResource) -> void: + emit_signal("request_item_edit", edited_continent, edited_zone, edited_sub_zone, resource) + diff --git a/addons/world_generator/ui/tabs/SubZone.tscn b/addons/world_generator/ui/tabs/SubZone.tscn new file mode 100644 index 0000000..95c3980 --- /dev/null +++ b/addons/world_generator/ui/tabs/SubZone.tscn @@ -0,0 +1,107 @@ +[gd_scene load_steps=5 format=2] + +[ext_resource path="res://addons/world_generator/ui/tabs/SubZone.gd" type="Script" id=1] +[ext_resource path="res://addons/world_generator/ui/ResourcePropertyList.tscn" type="PackedScene" id=2] +[ext_resource path="res://addons/world_generator/ui/DataList.tscn" type="PackedScene" id=3] +[ext_resource path="res://addons/world_generator/ui/RectEditor.tscn" type="PackedScene" id=4] + +[node name="SubZone" type="HBoxContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource( 1 ) + +[node name="HSplitContainer" type="HSplitContainer" parent="."] +margin_right = 1024.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 + +[node name="RectEditor" parent="HSplitContainer" instance=ExtResource( 4 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 735.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer"] +margin_left = 747.0 +margin_right = 1024.0 +margin_bottom = 600.0 + +[node name="ContinentOptionButton" type="OptionButton" parent="HSplitContainer/VBoxContainer"] +margin_right = 277.0 +margin_bottom = 20.0 + +[node name="ZoneOptionButton" type="OptionButton" parent="HSplitContainer/VBoxContainer"] +margin_top = 24.0 +margin_right = 277.0 +margin_bottom = 44.0 + +[node name="SubZoneOptionButton" type="OptionButton" parent="HSplitContainer/VBoxContainer"] +margin_top = 48.0 +margin_right = 277.0 +margin_bottom = 68.0 + +[node name="HBoxContainer2" type="HBoxContainer" parent="HSplitContainer/VBoxContainer"] +margin_top = 72.0 +margin_right = 277.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ResourcePropertyList" parent="HSplitContainer/VBoxContainer/HBoxContainer2" instance=ExtResource( 2 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 100.0 +margin_bottom = 528.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer/VBoxContainer/HBoxContainer2"] +margin_left = 104.0 +margin_right = 277.0 +margin_bottom = 528.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Label" type="Label" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer"] +margin_right = 173.0 +margin_bottom = 14.0 +text = "Sub Zones" +align = 1 +valign = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer"] +margin_top = 18.0 +margin_right = 173.0 +margin_bottom = 38.0 + +[node name="AddButton" type="Button" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer"] +margin_right = 37.0 +margin_bottom = 20.0 +text = "Add" + +[node name="DeleteButton" type="Button" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer"] +margin_left = 41.0 +margin_right = 96.0 +margin_bottom = 20.0 +text = "Delete" + +[node name="Duplicate" type="Button" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer"] +margin_left = 100.0 +margin_right = 173.0 +margin_bottom = 20.0 +text = "Duplicate" + +[node name="DataList" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer" instance=ExtResource( 3 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 42.0 +margin_right = 173.0 +margin_bottom = 528.0 +class_types = 3 + +[connection signal="pressed" from="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer/AddButton" to="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList" method="add_button_pressed"] +[connection signal="pressed" from="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer/DeleteButton" to="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList" method="delete_button_pressed"] +[connection signal="pressed" from="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer/Duplicate" to="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList" method="duplicate_button_pressed"] diff --git a/addons/world_generator/ui/tabs/SubZoneProp.gd b/addons/world_generator/ui/tabs/SubZoneProp.gd new file mode 100644 index 0000000..cabb97d --- /dev/null +++ b/addons/world_generator/ui/tabs/SubZoneProp.gd @@ -0,0 +1,204 @@ +tool +extends HBoxContainer + +var edited_world : WorldGenWorld = null +var edited_continent : Continent = null +var edited_zone : Zone = null +var edited_sub_zone : SubZone = null +var edited_sub_zone_prop : SubZoneProp = null + +func _ready(): + var coption_button : OptionButton = $VBoxContainer/ContinentOptionButton + coption_button.connect("item_selected", self, "on_continent_item_selected") + + var zoption_button : OptionButton = $VBoxContainer/ZoneOptionButton + zoption_button.connect("item_selected", self, "on_zone_item_selected") + + var szoption_button : OptionButton = $VBoxContainer/SubZoneOptionButton + szoption_button.connect("item_selected", self, "on_sub_zone_item_selected") + + var szpoption_button : OptionButton = $VBoxContainer/SubZonePropOptionButton + szpoption_button.connect("item_selected", self, "on_sub_zone_prop_item_selected") + +func set_plugin(plugin : EditorPlugin) -> void: + $VBoxContainer/HBoxContainer2/ResourcePropertyList.set_plugin(plugin) + +func continent_changed() -> void: + var option_button : OptionButton = $VBoxContainer/ZoneOptionButton + option_button.clear() + edited_zone = null + edited_sub_zone = null + + if !edited_continent: + return + + var content : Array = edited_continent.get_content() + + for c in content: + if c: + option_button.add_item(c.resource_name) + option_button.set_item_metadata(option_button.get_item_count() - 1, c) + + if !edited_zone: + edited_zone = c + + zone_changed() + +func zone_changed() -> void: + var option_button : OptionButton = $VBoxContainer/SubZoneOptionButton + option_button.clear() + edited_sub_zone = null + + if !edited_zone: + return + + var content : Array = edited_zone.get_content() + + for c in content: + if c: + option_button.add_item(c.resource_name) + option_button.set_item_metadata(option_button.get_item_count() - 1, c) + + if !edited_sub_zone: + edited_sub_zone = c + + sub_zone_changed() + +func sub_zone_changed() -> void: + var option_button : OptionButton = $VBoxContainer/SubZonePropOptionButton + option_button.clear() + edited_sub_zone_prop = null + + if !edited_sub_zone: + return + + var content : Array = edited_sub_zone.get_content() + + for c in content: + if c: + option_button.add_item(c.resource_name) + option_button.set_item_metadata(option_button.get_item_count() - 1, c) + + if !edited_sub_zone_prop: + edited_sub_zone_prop = c + + sub_zone_prop_changed() + +func sub_zone_prop_changed() -> void: + $VBoxContainer/HBoxContainer2/ResourcePropertyList.edit_resource(edited_sub_zone_prop) + +func refresh() -> void: + var option_button : OptionButton = $VBoxContainer/ContinentOptionButton + option_button.clear() + + if !edited_world: + return + + var content : Array = edited_world.get_content() + + for c in content: + if c: + option_button.add_item(c.resource_name) + option_button.set_item_metadata(option_button.get_item_count() - 1, c) + + if !edited_continent: + edited_continent = c + + continent_changed() + +func set_wgworld(wgw : WorldGenWorld) -> void: + edited_world = wgw + edited_continent = null + edited_zone = null + edited_sub_zone = null + edited_sub_zone_prop = null + + refresh() + +func set_continent(continent : Continent) -> void: + edited_continent = continent + edited_zone = null + edited_sub_zone = null + edited_sub_zone_prop = null + + continent_changed() + +func set_zone(zone : Zone) -> void: + edited_zone = zone + edited_sub_zone = null + edited_sub_zone_prop = null + + zone_changed() + +func set_sub_zone(sub_zone : SubZone) -> void: + edited_sub_zone = sub_zone + edited_sub_zone_prop = null + + sub_zone_changed() + +func set_sub_zone_prop(sub_zone_prop : SubZoneProp) -> void: + edited_sub_zone_prop = sub_zone_prop + + sub_zone_prop_changed() + +func switch_to(continent : WorldGenBaseResource, zone : WorldGenBaseResource, subzone : WorldGenBaseResource, subzone_prop : WorldGenBaseResource) -> void: + var contob : OptionButton = $VBoxContainer/ContinentOptionButton + + for i in range(contob.get_item_count()): + var ccont : Continent = contob.get_item_metadata(i) + + if (ccont == continent): + contob.select(i) + set_continent(continent) + break + + var zoneob : OptionButton = $VBoxContainer/ZoneOptionButton + + for i in range(zoneob.get_item_count()): + var czone : Zone = zoneob.get_item_metadata(i) + + if (czone == zone): + zoneob.select(i) + set_zone(zone) + break + + var subzoneob : OptionButton = $VBoxContainer/SubZoneOptionButton + + for i in range(subzoneob.get_item_count()): + var cszone : SubZone = subzoneob.get_item_metadata(i) + + if (cszone == subzone): + subzoneob.select(i) + set_sub_zone(subzone) + break + + var subzonepropob : OptionButton = $VBoxContainer/SubZonePropOptionButton + + for i in range(subzonepropob.get_item_count()): + var cszoneprop : SubZoneProp = subzonepropob.get_item_metadata(i) + + if (cszoneprop == subzone_prop): + subzonepropob.select(i) + set_sub_zone_prop(subzone_prop) + return + + +func on_continent_item_selected(idx : int) -> void: + var option_button : OptionButton = $VBoxContainer/ContinentOptionButton + + set_continent(option_button.get_item_metadata(idx)) + +func on_zone_item_selected(idx : int) -> void: + var option_button : OptionButton = $VBoxContainer/ZoneOptionButton + + set_zone(option_button.get_item_metadata(idx)) + +func on_sub_zone_item_selected(idx : int) -> void: + var option_button : OptionButton = $VBoxContainer/SubZoneOptionButton + + set_sub_zone(option_button.get_item_metadata(idx)) + +func on_sub_zone_prop_item_selected(idx : int) -> void: + var option_button : OptionButton = $VBoxContainer/SubZonePropOptionButton + + set_sub_zone_prop(option_button.get_item_metadata(idx)) diff --git a/addons/world_generator/ui/tabs/SubZoneProp.tscn b/addons/world_generator/ui/tabs/SubZoneProp.tscn new file mode 100644 index 0000000..1745900 --- /dev/null +++ b/addons/world_generator/ui/tabs/SubZoneProp.tscn @@ -0,0 +1,51 @@ +[gd_scene load_steps=3 format=2] + +[ext_resource path="res://addons/world_generator/ui/tabs/SubZoneProp.gd" type="Script" id=1] +[ext_resource path="res://addons/world_generator/ui/ResourcePropertyList.tscn" type="PackedScene" id=2] + +[node name="SubZoneProp" type="HBoxContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource( 1 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +margin_right = 1024.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ContinentOptionButton" type="OptionButton" parent="VBoxContainer"] +margin_right = 1024.0 +margin_bottom = 20.0 + +[node name="ZoneOptionButton" type="OptionButton" parent="VBoxContainer"] +margin_top = 24.0 +margin_right = 1024.0 +margin_bottom = 44.0 + +[node name="SubZoneOptionButton" type="OptionButton" parent="VBoxContainer"] +margin_top = 48.0 +margin_right = 1024.0 +margin_bottom = 68.0 + +[node name="SubZonePropOptionButton" type="OptionButton" parent="VBoxContainer"] +margin_top = 72.0 +margin_right = 1024.0 +margin_bottom = 92.0 + +[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer"] +margin_top = 96.0 +margin_right = 1024.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ResourcePropertyList" parent="VBoxContainer/HBoxContainer2" instance=ExtResource( 2 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 1024.0 +margin_bottom = 504.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 diff --git a/addons/world_generator/ui/tabs/World.tscn b/addons/world_generator/ui/tabs/World.tscn new file mode 100644 index 0000000..42f0a74 --- /dev/null +++ b/addons/world_generator/ui/tabs/World.tscn @@ -0,0 +1,79 @@ +[gd_scene load_steps=5 format=2] + +[ext_resource path="res://addons/world_generator/ui/ResourcePropertyList.tscn" type="PackedScene" id=2] +[ext_resource path="res://addons/world_generator/ui/WorldTab.gd" type="Script" id=5] +[ext_resource path="res://addons/world_generator/ui/DataList.tscn" type="PackedScene" id=6] +[ext_resource path="res://addons/world_generator/ui/RectEditor.tscn" type="PackedScene" id=7] + +[node name="World" type="HBoxContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 4.0 +margin_top = 32.0 +margin_right = -4.0 +margin_bottom = -4.0 +script = ExtResource( 5 ) + +[node name="HSplitContainer" type="HSplitContainer" parent="."] +margin_right = 839.0 +margin_bottom = 564.0 +size_flags_horizontal = 3 + +[node name="RectEditor" parent="HSplitContainer" instance=ExtResource( 7 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 727.0 +margin_bottom = 564.0 +size_flags_horizontal = 3 + +[node name="ResourcePropertyList" parent="HSplitContainer" instance=ExtResource( 2 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 739.0 +margin_right = 839.0 +margin_bottom = 564.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +margin_left = 843.0 +margin_right = 1016.0 +margin_bottom = 564.0 + +[node name="Label" type="Label" parent="VBoxContainer"] +margin_right = 173.0 +margin_bottom = 14.0 +text = "Continents" +align = 1 +valign = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +margin_top = 18.0 +margin_right = 173.0 +margin_bottom = 38.0 + +[node name="AddButton" type="Button" parent="VBoxContainer/HBoxContainer"] +margin_right = 37.0 +margin_bottom = 20.0 +text = "Add" + +[node name="DeleteButton" type="Button" parent="VBoxContainer/HBoxContainer"] +margin_left = 41.0 +margin_right = 96.0 +margin_bottom = 20.0 +text = "Delete" + +[node name="Duplicate" type="Button" parent="VBoxContainer/HBoxContainer"] +margin_left = 100.0 +margin_right = 173.0 +margin_bottom = 20.0 +text = "Duplicate" + +[node name="DataList" parent="VBoxContainer" instance=ExtResource( 6 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 42.0 +margin_right = 173.0 +margin_bottom = 564.0 + +[connection signal="pressed" from="VBoxContainer/HBoxContainer/AddButton" to="VBoxContainer/DataList" method="add_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/DeleteButton" to="VBoxContainer/DataList" method="delete_button_pressed"] +[connection signal="pressed" from="VBoxContainer/HBoxContainer/Duplicate" to="VBoxContainer/DataList" method="duplicate_button_pressed"] diff --git a/addons/world_generator/ui/tabs/Zone.gd b/addons/world_generator/ui/tabs/Zone.gd new file mode 100644 index 0000000..dbe284f --- /dev/null +++ b/addons/world_generator/ui/tabs/Zone.gd @@ -0,0 +1,122 @@ +tool +extends HBoxContainer + +var edited_world : WorldGenWorld = null +var edited_continent : Continent = null +var edited_zone : Zone = null + +signal request_item_edit(continent, zone, subzone) + +func _ready(): + var coption_button : OptionButton = $HSplitContainer/VBoxContainer/ContinentOptionButton + coption_button.connect("item_selected", self, "on_continent_item_selected") + + var zoption_button : OptionButton = $HSplitContainer/VBoxContainer/ZoneOptionButton + zoption_button.connect("item_selected", self, "on_zone_item_selected") + + var dl : Control = get_node("HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList") + if !dl.is_connected("request_item_edit", self, "on_request_item_edit"): + dl.connect("request_item_edit", self, "on_request_item_edit") + +func set_plugin(plugin : EditorPlugin) -> void: + $HSplitContainer/VBoxContainer/HBoxContainer2/ResourcePropertyList.set_plugin(plugin) + $HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList.set_plugin(plugin) + $HSplitContainer/RectEditor.set_plugin(plugin) + +func refresh() -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/ContinentOptionButton + option_button.clear() + edited_continent = null + edited_zone = null + + if !edited_world: + return + + var content : Array = edited_world.get_content() + + for c in content: + if c: + option_button.add_item(c.resource_name) + option_button.set_item_metadata(option_button.get_item_count() - 1, c) + + if !edited_continent: + edited_continent = c + + continent_changed() + +func continent_changed() -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/ZoneOptionButton + option_button.clear() + edited_zone = null + + if !edited_continent: + return + + var content : Array = edited_continent.get_content() + + for c in content: + if c: + option_button.add_item(c.resource_name) + option_button.set_item_metadata(option_button.get_item_count() - 1, c) + + if !edited_zone: + edited_zone = c + + zone_changed() + +func zone_changed() -> void: + $HSplitContainer/VBoxContainer/HBoxContainer2/ResourcePropertyList.edit_resource(edited_zone) + $HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList.set_edited_resource(edited_zone) + $HSplitContainer/RectEditor.set_edited_resource(edited_zone) + +func set_continent(continent : Continent) -> void: + edited_continent = continent + edited_zone = null + + continent_changed() + +func set_zone(zone : Zone) -> void: + edited_zone = zone + + zone_changed() + +func set_wgworld(wgw : WorldGenWorld) -> void: + edited_world = wgw + edited_continent = null + edited_zone = null + + refresh() + +func switch_to(continent : WorldGenBaseResource, resource : WorldGenBaseResource) -> void: + var contob : OptionButton = $HSplitContainer/VBoxContainer/ContinentOptionButton + + for i in range(contob.get_item_count()): + var ccont : Continent = contob.get_item_metadata(i) + + if (ccont == continent): + contob.select(i) + set_continent(continent) + break + + var zoneob : OptionButton = $HSplitContainer/VBoxContainer/ZoneOptionButton + + for i in range(zoneob.get_item_count()): + var czone : Zone = zoneob.get_item_metadata(i) + + if (czone == resource): + zoneob.select(i) + set_zone(czone) + return + +func on_continent_item_selected(idx : int) -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/ContinentOptionButton + + set_continent(option_button.get_item_metadata(idx)) + +func on_zone_item_selected(idx : int) -> void: + var option_button : OptionButton = $HSplitContainer/VBoxContainer/ZoneOptionButton + + set_zone(option_button.get_item_metadata(idx)) + +func on_request_item_edit(resource : WorldGenBaseResource) -> void: + emit_signal("request_item_edit", edited_continent, edited_zone, resource) diff --git a/addons/world_generator/ui/tabs/Zone.tscn b/addons/world_generator/ui/tabs/Zone.tscn new file mode 100644 index 0000000..a113ff0 --- /dev/null +++ b/addons/world_generator/ui/tabs/Zone.tscn @@ -0,0 +1,105 @@ +[gd_scene load_steps=5 format=2] + +[ext_resource path="res://addons/world_generator/ui/tabs/Zone.gd" type="Script" id=1] +[ext_resource path="res://addons/world_generator/ui/ResourcePropertyList.tscn" type="PackedScene" id=2] +[ext_resource path="res://addons/world_generator/ui/DataList.tscn" type="PackedScene" id=3] +[ext_resource path="res://addons/world_generator/ui/RectEditor.tscn" type="PackedScene" id=4] + +[node name="Zone" type="HBoxContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="HSplitContainer" type="HSplitContainer" parent="."] +margin_right = 1024.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 + +[node name="RectEditor" parent="HSplitContainer" instance=ExtResource( 4 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 735.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer"] +margin_left = 747.0 +margin_right = 1024.0 +margin_bottom = 600.0 + +[node name="ContinentOptionButton" type="OptionButton" parent="HSplitContainer/VBoxContainer"] +margin_right = 277.0 +margin_bottom = 20.0 + +[node name="ZoneOptionButton" type="OptionButton" parent="HSplitContainer/VBoxContainer"] +margin_top = 24.0 +margin_right = 277.0 +margin_bottom = 44.0 + +[node name="HBoxContainer2" type="HBoxContainer" parent="HSplitContainer/VBoxContainer"] +margin_top = 48.0 +margin_right = 277.0 +margin_bottom = 600.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ResourcePropertyList" parent="HSplitContainer/VBoxContainer/HBoxContainer2" instance=ExtResource( 2 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 100.0 +margin_bottom = 552.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer/VBoxContainer/HBoxContainer2"] +margin_left = 104.0 +margin_right = 277.0 +margin_bottom = 552.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Label" type="Label" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer"] +margin_right = 173.0 +margin_bottom = 14.0 +text = "Sub Zones" +align = 1 +valign = 1 + +[node name="HBoxContainer" type="HBoxContainer" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer"] +margin_top = 18.0 +margin_right = 173.0 +margin_bottom = 38.0 + +[node name="AddButton" type="Button" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer"] +margin_right = 37.0 +margin_bottom = 20.0 +text = "Add" + +[node name="DeleteButton" type="Button" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer"] +margin_left = 41.0 +margin_right = 96.0 +margin_bottom = 20.0 +text = "Delete" + +[node name="Duplicate" type="Button" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer"] +margin_left = 100.0 +margin_right = 173.0 +margin_bottom = 20.0 +text = "Duplicate" + +[node name="DataList" parent="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer" instance=ExtResource( 3 )] +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 42.0 +margin_right = 173.0 +margin_bottom = 552.0 +class_types = 2 + +[connection signal="pressed" from="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer/AddButton" to="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList" method="add_button_pressed"] +[connection signal="pressed" from="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer/DeleteButton" to="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList" method="delete_button_pressed"] +[connection signal="pressed" from="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/HBoxContainer/Duplicate" to="HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer/DataList" method="duplicate_button_pressed"] diff --git a/addons/world_generator/widgets/EditorResourceWidget.gd b/addons/world_generator/widgets/EditorResourceWidget.gd new file mode 100644 index 0000000..a73924e --- /dev/null +++ b/addons/world_generator/widgets/EditorResourceWidget.gd @@ -0,0 +1,80 @@ +tool +extends HBoxContainer + +var _resource_type : String = "Resource" +var _resource : Resource = null +var _plugin : EditorPlugin = null + +signal on_resource_changed(new_res) + +func _enter_tree(): + $ResourceButton.set_drag_forwarding(self) + +func set_resource_type(type : String) -> void: + _resource_type = type + +func set_resource(res : Resource) -> void: + if res && !res.is_class(_resource_type): + return + + var emit : bool = res != _resource + + _resource = res + + refresh_ui() + + if emit: + emit_signal("on_resource_changed", _resource) + +func refresh_ui() -> void: + if _resource: + var text : String = _resource.resource_name + + if text == "": + text = _resource.get_class() + + $ResourceButton.text = text + else: + $ResourceButton.text = "[null]" + +func on_clear_button_pressed() -> void: + if _resource: + set_resource(null) + +func on_resource_button_pressed() -> void: + if _resource && _plugin: + _plugin.get_editor_interface().inspect_object(_resource) + +#examples +#{files:[res://modules/planets/test_planet/test_world.tres], from:@@4176:[Tree:9070], type:files} +#{from:Button:[Button:917001], resource:[Resource:26180], type:resource} + +func can_drop_data_fw(position, data, from_control): + return true + +func drop_data_fw(position, data, from_control): + if data["type"] == "resource": + var res : Resource = data["resource"] + set_resource(res) + elif data["type"] == "files": + var files : Array = data["files"] + + for f in files: + var res : Resource = load(f) + set_resource(res) + return + +func get_drag_data_fw(position, from_control): + if !_resource: + return + + var d : Dictionary = Dictionary() + + d["from"] = self + d["resource"] = _resource + d["type"] = "resource" + + return d + +func set_plugin(plugin : EditorPlugin) -> void: + _plugin = plugin diff --git a/addons/world_generator/widgets/EditorResourceWidget.tscn b/addons/world_generator/widgets/EditorResourceWidget.tscn new file mode 100644 index 0000000..f5f3bfe --- /dev/null +++ b/addons/world_generator/widgets/EditorResourceWidget.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/world_generator/widgets/EditorResourceWidget.gd" type="Script" id=1] + +[node name="EditorResourceWidget" type="HBoxContainer"] +margin_right = 376.0 +margin_bottom = 40.0 +script = ExtResource( 1 ) + +[node name="ResourceButton" type="Button" parent="."] +margin_right = 342.0 +margin_bottom = 40.0 +size_flags_horizontal = 3 + +[node name="Clear" type="Button" parent="."] +margin_left = 346.0 +margin_right = 376.0 +margin_bottom = 40.0 +rect_min_size = Vector2( 30, 0 ) +text = "X" + +[connection signal="pressed" from="ResourceButton" to="." method="on_resource_button_pressed"] +[connection signal="pressed" from="Clear" to="." method="on_clear_button_pressed"] diff --git a/addons/world_generator/widgets/EditorZoomWidget.gd b/addons/world_generator/widgets/EditorZoomWidget.gd new file mode 100644 index 0000000..645bd14 --- /dev/null +++ b/addons/world_generator/widgets/EditorZoomWidget.gd @@ -0,0 +1,220 @@ +tool +extends HBoxContainer + +#This is a port of godot 4.0's EditorZoomWidget + +#/*************************************************************************/ +#/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +#/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +#/* */ +#/* Permission is hereby granted, free of charge, to any person obtaining */ +#/* a copy of this software and associated documentation files (the */ +#/* "Software"), to deal in the Software without restriction, including */ +#/* without limitation the rights 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 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. */ +#/*************************************************************************/ + +var zoom_minus : Button +var zoom_reset : Button +var zoom_plus : Button + +var EDSCALE : float = 1 + +export(float) var zoom : float = 1.0 setget set_zoom, get_zoom + +signal zoom_changed(zoom) + +func _init() -> void: + # Zoom buttons + zoom_minus = Button.new() + zoom_minus.set_flat(true) + add_child(zoom_minus) + zoom_minus.connect("pressed", self, "_button_zoom_minus") + zoom_minus.set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_minus", tr("Zoom Out"), KEY_MASK_CMD | KEY_MINUS)) + zoom_minus.set_focus_mode(FOCUS_NONE) + + zoom_reset = Button.new() + zoom_reset.set_flat(true) + add_child(zoom_reset) + zoom_reset.add_theme_constant_override("outline_size", 1) + zoom_reset.add_theme_color_override("font_outline_color", Color(0, 0, 0)) + zoom_reset.add_theme_color_override("font_color", Color(1, 1, 1)) + zoom_reset.connect("pressed", self, "_button_zoom_reset") + zoom_reset.set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", tr("Zoom Reset"), KEY_MASK_CMD | KEY_0)) + zoom_reset.set_focus_mode(FOCUS_NONE) + #Prevent the button's size from changing when the text size changes + zoom_reset.set_custom_minimum_size(Vector2(75, 0)) + + zoom_plus = Button.new() + zoom_plus.set_flat(true) + add_child(zoom_plus) + zoom_plus.connect("pressed", self, "_button_zoom_plus") + zoom_plus.set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", tr("Zoom In"), KEY_MASK_CMD | KEY_EQUAL)) # Usually direct access key for PLUS + zoom_plus.set_focus_mode(FOCUS_NONE) + + _update_zoom_label() + + add_theme_constant_override("separation", round(-8)) + +func get_zoom() -> float: + return zoom + +func set_zoom(p_zoom : float) -> void: + if (p_zoom > 0 && p_zoom != zoom): + zoom = p_zoom; + _update_zoom_label(); + emit_signal("zoom_changed", zoom); + + +func set_zoom_by_increments(p_increment_count : int, p_integer_only : bool) -> void: + # Remove editor scale from the index computation. + var zoom_noscale : float = zoom / max(1, EDSCALE) + var CMP_EPSILON : float = 0.00001 + + if (p_integer_only): + # Only visit integer scaling factors above 100%, and fractions with an integer denominator below 100% + # (1/2 = 50%, 1/3 = 33.33%, 1/4 = 25%, …). + # This is useful when working on pixel art projects to avoid distortion. + # This algorithm is designed to handle fractional start zoom values correctly + # (e.g. 190% will zoom up to 200% and down to 100%). + if (zoom_noscale + p_increment_count * 0.001 >= 1.0 - CMP_EPSILON): + # New zoom is certain to be above 100%. + if (p_increment_count >= 1): + # Zooming. + set_zoom(floor(zoom_noscale + p_increment_count) * max(1, EDSCALE)) + else: + # Dezooming. + set_zoom(ceil(zoom_noscale + p_increment_count) * max(1, EDSCALE)) + else: + if (p_increment_count >= 1): + # Zooming. Convert the current zoom into a denominator. + var new_zoom : float = 1.0 / ceil(1.0 / zoom_noscale - p_increment_count) + if (is_equal_approx(zoom_noscale, new_zoom)): + # New zoom is identical to the old zoom, so try again. + # This can happen due to floating-point precision issues. + new_zoom = 1.0 / ceil(1.0 / zoom_noscale - p_increment_count - 1) + + set_zoom(new_zoom * max(1, EDSCALE)); + else: + # Dezooming. Convert the current zoom into a denominator. + var new_zoom : float = 1.0 / floor(1.0 / zoom_noscale - p_increment_count) + if (is_equal_approx(zoom_noscale, new_zoom)): + # New zoom is identical to the old zoom, so try again. + # This can happen due to floating-point precision issues. + new_zoom = 1.0 / floor(1.0 / zoom_noscale - p_increment_count + 1) + + set_zoom(new_zoom * max(1, EDSCALE)) + else: + # Base increment factor defined as the twelveth root of two. + # This allow a smooth geometric evolution of the zoom, with the advantage of + # visiting all integer power of two scale factors. + # note: this is analogous to the 'semitones' interval in the music world + # In order to avoid numerical imprecisions, we compute and edit a zoom index + # with the following relation: zoom = 2 ^ (index / 12) + + if (zoom < CMP_EPSILON || p_increment_count == 0): + return + + # zoom = 2**(index/12) => log2(zoom) = index/12 + var closest_zoom_index : float = round(log(zoom_noscale) * 12.0 / log(2.0)) + + var new_zoom_index : float = closest_zoom_index + p_increment_count + var new_zoom : float = pow(2.0, new_zoom_index / 12.0) + + # Restore Editor scale transformation + new_zoom *= max(1, EDSCALE) + + set_zoom(new_zoom) + + +func _update_zoom_label() -> void: + var zoom_text : String = "" + + # The zoom level displayed is relative to the editor scale + # (like in most image editors). Its lower bound is clamped to 1 as some people + # lower the editor scale to increase the available real estate, + # even if their display doesn't have a particularly low DPI. + + if (zoom >= 10): + # Don't show a decimal when the zoom level is higher than 1000 %. + #zoom_text = (rtos(round((zoom / max(1, EDSCALE)) * 100))) + " %" + zoom_text = (String(round((zoom / max(1, EDSCALE)) * 100))) + " %" + else: + var v : float = (zoom / max(1, EDSCALE)) * 100 + var val : float = floor(v / 0.1 + 0.5) * 0.1 + +# zoom_text = (rtos(val)) + " %" + zoom_text = (String(val)) + " %" + + zoom_reset.set_text(zoom_text) + +func _button_zoom_minus() -> void: + set_zoom_by_increments(-6, Input.is_key_pressed(KEY_ALT)); + emit_signal("zoom_changed", zoom); + +func _button_zoom_reset() -> void: + set_zoom(1.0 * max(1, EDSCALE)); + emit_signal("zoom_changed", zoom); + +func _button_zoom_plus() -> void: + set_zoom_by_increments(6, Input.is_key_pressed(KEY_ALT)); + emit_signal("zoom_changed", zoom); + +func _notification(p_what : int) -> void: + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED): + zoom_minus.icon = get_theme_icon("ZoomLess", "EditorIcons") + zoom_plus.icon = get_theme_icon("ZoomMore", "EditorIcons") + +#from godot editor/editor_Settings.cpp +func ED_SHORTCUT(p_path : String, p_name : String, p_keycode : int, editor_settings : EditorSettings = null) -> ShortCut: + if OS.get_name() == "OSX": + # Use Cmd+Backspace as a general replacement for Delete shortcuts on macOS + if (p_keycode == KEY_DELETE): + p_keycode = KEY_MASK_CMD | KEY_BACKSPACE + + var ie : InputEventKey = null + if (p_keycode): + ie = InputEventKey.new() + + ie.set_unicode(p_keycode & KEY_CODE_MASK) + ie.set_scancode(p_keycode & KEY_CODE_MASK) + ie.set_shift(bool(p_keycode & KEY_MASK_SHIFT)) + ie.set_alt(bool(p_keycode & KEY_MASK_ALT)) + ie.set_control(bool(p_keycode & KEY_MASK_CTRL)) + ie.set_metakey(bool(p_keycode & KEY_MASK_META)) + + if (!editor_settings): + var sc : ShortCut + sc = ShortCut.new() + sc.set_name(p_name) + sc.set_shortcut(ie) + sc.set_meta("original", ie) + return sc + + var sc : ShortCut = editor_settings.get_shortcut(p_path) + if (sc.is_valid()): + sc.set_name(p_name); #keep name (the ones that come from disk have no name) + sc.set_meta("original", ie); #to compare against changes + return sc; + + sc = ShortCut.new() + sc.set_name(p_name) + sc.set_shortcut(ie) + sc.set_meta("original", ie) #to compare against changes + editor_settings.add_shortcut(p_path, sc) + + return sc + diff --git a/addons/world_generator/widgets/EditorZoomWidget.tscn b/addons/world_generator/widgets/EditorZoomWidget.tscn new file mode 100644 index 0000000..5de87a9 --- /dev/null +++ b/addons/world_generator/widgets/EditorZoomWidget.tscn @@ -0,0 +1,8 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/world_generator/widgets/EditorZoomWidget.gd" type="Script" id=1] + +[node name="EditorZoomWidget" type="HBoxContainer"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 1 ) diff --git a/default_env.tres b/default_env.tres new file mode 100644 index 0000000..4f08e8f --- /dev/null +++ b/default_env.tres @@ -0,0 +1,7 @@ +[gd_resource type="Environment3D" load_steps=2 format=2] + +[sub_resource type="ProceduralSky" id=1] + +[resource] +background_mode = 2 +background_sky = SubResource( 1 ) diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..87f1f7549e0489d0758939152cd1f879f1e653b5 GIT binary patch literal 3218 zcmV;D3~lp?P)Px>Oi4sRRCt`-n|p9v)qTf5=iI$}?`o~pW3{W5wUUr5+ZZ>+Ha2kvrbE(n3@r>e z6xz@>Qyxj3Ng!!SGDBuM`Ku++LLq@nT^K`)n@5w-mPttloU|q-HU{G-mStPWl5IU# zZ>{!q?>+rvB_U}a+Lc#+^!wM&{r&Fm{Lb&5^EM0?_ip!zwaziOKi4c#X)IUXr zhJa}U`2Bta{8R|>nair-C4y4wCkpNv8X7`WRaGIS{Gt@%dkYzLUB_dZNKLyOdR{af z+a?x|lS-vBX-e&I9A~GfsHkYxU9O{ZmwkD8Id!2BE|=?)D!g2>F5+YG_y^U)9vBc;47$}R__lvMb9BoYY{$s_2=)Z9m#TtcZ$i1cA#8b(Q$y=7Sp4G&}6HZ^`fZnt|$as~zmxzN)? zGMQv`Yb(`NRTrg~%jLo}jM-Iy5Sv^G5tw0Es-knDl%oB_2{tJW7lQu2e#RRc(#LEO z=sbIt=8*|jYi^XG^KM5cm76LqdTYAf?vD%rb@QYNAu``660ul}H8yyphAt&xmr3wk zFK4^D3VPph9NJHvV$E2TN|%lh0x2YGHJxZQdeQnVoMXv7T`HBrBjz8QKF!72(Misn zIkV{d7kYa5SS*EC&X?iUG-C01Np*$gFo0!|Z6SH2M(ae3;W=UGLfE!V)xa=iGBfVb z5X54!QmP6pg8`IMIfYheE=D@Luq-Ppy|<@_m71QFCWXL`UIhk_QsU&G2_aao>2!5> zXV5ImqAD_il)3$oYZ*e5l81VlreP@u$R0kMQo}bq&XMS`3`IVXg%IE<*>nRqMP0fULgcE!bN|cZIf5y zDP2b?mGg@lQnJzQNjs=z`0BYJh2T2F!?g-DBmcM)%Au?*_m<4-1dW<%l8ZRBbh2{k zCO>yvE#I+8+JSdr<6Wu&{MFU;*qQaTs|87=Fe)nY`lP%DFin$wj~lzhN6)1~cQQdR z5Xj53XdKYo(!vQVv9OeM#Utw2Ow?3n9G45Rr~wSaz-SB;u?q`?t^_!a;*gc3sj+cU z-hyJwU?9L5LnCz+Yz3thZOItn)|Q;X&s-E7T(_)R#fijJLA<^q>wkry%vX*}&z&vK zAf_B9E6b^@s3=Ho!3Ho4gJd4M>Gkp#fByu#J5TX1XWICFR|k(g{~f#qDv}E~Z@-i0 z-`&UeyE^#Ezy+Ro<8{_;{di7(ODT*Zin}FAuiU1|lW+c<+aA0h%`iB8_%LVAc5?F{ zeuk&_?xDx}n`MM+k`mfO;uwP*g}nc2w$ba!|2 z-S0fd4Zrv4jCU%7K_36b&-mJ|7uf#Dmow-afA>?gojf@^c}SPcUwq+3PMt&jF4d zJ3h;Eu_%cF&b7BAr6iCg>Z+{`(sQN*$F_@-*?qD-tGu+#bpNue?PT%FELlY^epX6&xVRXfcn-JPV9T9WHQHXiw~>Pb--*mfBrn< z(>FzwQmLZ0fS|O6kFmi)#s&vVDzjJ=wFSIY6-bv0;kxoJO)1iild9TaQT>CWR)84@ z@aoZzJg?(8?EUdi0XVYvE&4jUXxq1MS*pS_aF>;_^$%{Ne$8rv4GpyX)^#+kUq4IV zxpOCnzWwcjU?nPtcQ#h6 zV8@e>L!K{m?04*YTtqeDXsbapW+6sMI% zLymbY*#`(Ah>eb8c^pQ^Cm223&58fs%NsAg3_$hDI(B_<1h-+3$YFp_{qeorboX}N z{`ddPN^e}Zj!*v4J^XU_4|95(U}FPsz4|IoKe!_+J!M-cf8iNuQCnatPeCL$os{UN zK_W+jvS#xQwEgBcJiFsdS?RSc&Fnn*4sO#R6;EX4@rT14eQR${JuFpR53wjGnAEkL zwZR#Mx7@l7e?u65Ll~*+>5i_J9_C{=-NcFg@8*<0($^0_Wgs})4VEq!-pWe+4Pk;! zP1Lnq!$|)?P8l&}NgPz+;*?cJ|JHDv@M-c$lv|_iSDn9fxy=ugU2vFfSZE zm{-Qh1G$aZWhZIQ(+60Hq72~k`RE>u(5z=#SfKc`mbJL1Gd#CxvZ6YDudlYDk?Oj1 z0&{TBp4T}fQEyqDQmu71?N)fS=^jk?13t|%?1Pw|Se5tZ1CpbAahIfw5 z9HkhuZBC`)#61Sli3!%YJ=DA0xP@G_eO(M}$032D-%iqQr3ls5qG=kP(I{o(lU!?< zxTmk^*12>#$HoeZ2MRWTQi|l{B*HAmavaWENiLW=HK8CaRaNM^PAZimGBm{dBO^>s zPU5m0f-Vjz#ODNBNu?NbY=$ihTi00O_2T!}v&HY9*Y=9X<9yKH zj~N+emCL{-BxB>_ghHX>46xV&o^TxYB%=g_L283R)*Hq=x~}Wgg+kO#4;)VmA$ z-=)^;lA*u9e_pXhDD{r;`FyQzT|X>@n7wCK;qws+g$nAgUk$Kro8G=Y#>dBJpDCrH z$y92CsH&_)lkyQEWkwojxZRi@&$5=dmJ`Zxh{xk||06UDN2xE{w*CCf%!J?Xe@zN; z`=wR!@<2JxYmwpMy8ty4)hLv@Lpjb@l~RRW1Gx&Jl$uhG^A+GurceEOYrCeR0vu;M1& literal 0 HcmV?d00001 diff --git a/icon.png.import b/icon.png.import new file mode 100644 index 0000000..a4c02e6 --- /dev/null +++ b/icon.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.png" +dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/project.pandemonium b/project.pandemonium new file mode 100644 index 0000000..56d2fe4 --- /dev/null +++ b/project.pandemonium @@ -0,0 +1,76 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +_global_script_classes=[ { +"base": "Resource", +"class": @"Continent", +"language": @"GDScript", +"path": "res://addons/world_generator/resources/continent.gd" +}, { +"base": "Resource", +"class": @"SubZone", +"language": @"GDScript", +"path": "res://addons/world_generator/resources/subzone.gd" +}, { +"base": "Resource", +"class": @"SubZoneProp", +"language": @"GDScript", +"path": "res://addons/world_generator/resources/sub_zone_prop.gd" +}, { +"base": "Resource", +"class": @"WorldGenBaseResource", +"language": @"GDScript", +"path": "res://addons/world_generator/resources/world_gen_base_resource.gd" +}, { +"base": "Reference", +"class": @"WorldGenRaycast", +"language": @"GDScript", +"path": "res://addons/world_generator/raycast/world_gen_raycast.gd" +}, { +"base": "Resource", +"class": @"WorldGenWorld", +"language": @"GDScript", +"path": "res://addons/world_generator/resources/world_gen_world.gd" +}, { +"base": "Resource", +"class": @"WorldGeneratorSettings", +"language": @"GDScript", +"path": "res://addons/world_generator/resources/world_generator_settings.gd" +}, { +"base": "Resource", +"class": @"Zone", +"language": @"GDScript", +"path": "res://addons/world_generator/resources/zone.gd" +} ] +_global_script_class_icons={ +@"WorldGenRaycast": "", +@"Continent": "", +@"SubZone": "", +@"SubZoneProp": "", +@"WorldGeneratorSettings": "", +@"WorldGenBaseResource": "", +@"WorldGenWorld": "", +@"Zone": "" +} + +[application] + +config/name="World Generator" +config/icon="res://icon.png" + +[physics] + +common/enable_pause_aware_picking=true + +[rendering] + +vram_compression/import_etc=true +vram_compression/import_etc2=false +environment/default_environment="res://default_env.tres"