From 6b9360d4dc7706108e5f245996e6a0e3b3aa190c Mon Sep 17 00:00:00 2001 From: adtpdn Date: Tue, 22 Oct 2024 16:15:56 +0800 Subject: [PATCH] first commit --- .gitattributes | 2 + .gitignore | 3 + README.md | 20 + addons/.gitignore | 27 ++ addons/README.md | 350 ++++++++++++++++++ addons/enhanced_gridmap/enhanced_gridmap.gd | 298 +++++++++++++++ .../enhanced_gridmap/enhanced_gridmap_dock.gd | 349 +++++++++++++++++ .../enhanced_gridmap_dock.tscn | 237 ++++++++++++ addons/enhanced_gridmap/icon.png | Bin 0 -> 413 bytes addons/enhanced_gridmap/icon.png.import | 34 ++ .../enhanced_gridmap/meshlibrary/default.tres | 49 +++ addons/enhanced_gridmap/plugin.cfg | 7 + addons/enhanced_gridmap/plugin.gd | 28 ++ assets/main-environment.tres | 31 ++ assets/models/Textures/colormap.png | Bin 0 -> 9385 bytes assets/models/Textures/colormap.png.import | 35 ++ assets/models/meshes/end.tres | 6 + assets/models/meshes/hover.res | Bin 0 -> 3195 bytes assets/models/meshes/non-walkable.res | Bin 0 -> 7628 bytes assets/models/meshes/normal.res | Bin 0 -> 3255 bytes assets/models/meshes/start.res | Bin 0 -> 3257 bytes assets/models/meshes/tiles_armagedon.glb | Bin 0 -> 70288 bytes .../models/meshes/tiles_armagedon.glb.import | 111 ++++++ assets/models/meshes/tiles_armagedon_a2.res | Bin 0 -> 13003 bytes assets/models/meshes/tiles_armagedon_a3.res | Bin 0 -> 3257 bytes assets/models/meshes/tiles_armagedon_a4.res | Bin 0 -> 7612 bytes assets/models/meshes/tiles_armagedon_b1.res | Bin 0 -> 11257 bytes assets/models/meshes/tiles_armagedon_b2.res | Bin 0 -> 12977 bytes assets/models/meshes/tiles_armagedon_b3.res | Bin 0 -> 7605 bytes assets/models/meshes/tiles_armagedon_b4.res | Bin 0 -> 14581 bytes assets/models/non-walkable.tres | 10 + assets/models/normal.tres | 7 + assets/models/start.tres | 7 + assets/textures/bub.png | Bin 0 -> 1243 bytes assets/textures/bub.png.import | 35 ++ icon.svg | 1 + icon.svg.import | 37 ++ project.godot | 25 ++ scenes/maiCE47.tmp | 45 +++ scenes/main.gd | 133 +++++++ scenes/main.tscn | 103 ++++++ scenes/player.gd | 187 ++++++++++ scenes/player.tscn | 58 +++ tiles_armagedon_a1.res | Bin 0 -> 11257 bytes 44 files changed, 2235 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README.md create mode 100644 addons/.gitignore create mode 100644 addons/README.md create mode 100644 addons/enhanced_gridmap/enhanced_gridmap.gd create mode 100644 addons/enhanced_gridmap/enhanced_gridmap_dock.gd create mode 100644 addons/enhanced_gridmap/enhanced_gridmap_dock.tscn create mode 100644 addons/enhanced_gridmap/icon.png create mode 100644 addons/enhanced_gridmap/icon.png.import create mode 100644 addons/enhanced_gridmap/meshlibrary/default.tres create mode 100644 addons/enhanced_gridmap/plugin.cfg create mode 100644 addons/enhanced_gridmap/plugin.gd create mode 100644 assets/main-environment.tres create mode 100644 assets/models/Textures/colormap.png create mode 100644 assets/models/Textures/colormap.png.import create mode 100644 assets/models/meshes/end.tres create mode 100644 assets/models/meshes/hover.res create mode 100644 assets/models/meshes/non-walkable.res create mode 100644 assets/models/meshes/normal.res create mode 100644 assets/models/meshes/start.res create mode 100644 assets/models/meshes/tiles_armagedon.glb create mode 100644 assets/models/meshes/tiles_armagedon.glb.import create mode 100644 assets/models/meshes/tiles_armagedon_a2.res create mode 100644 assets/models/meshes/tiles_armagedon_a3.res create mode 100644 assets/models/meshes/tiles_armagedon_a4.res create mode 100644 assets/models/meshes/tiles_armagedon_b1.res create mode 100644 assets/models/meshes/tiles_armagedon_b2.res create mode 100644 assets/models/meshes/tiles_armagedon_b3.res create mode 100644 assets/models/meshes/tiles_armagedon_b4.res create mode 100644 assets/models/non-walkable.tres create mode 100644 assets/models/normal.tres create mode 100644 assets/models/start.tres create mode 100644 assets/textures/bub.png create mode 100644 assets/textures/bub.png.import create mode 100644 icon.svg create mode 100644 icon.svg.import create mode 100644 project.godot create mode 100644 scenes/maiCE47.tmp create mode 100644 scenes/main.gd create mode 100644 scenes/main.tscn create mode 100644 scenes/player.gd create mode 100644 scenes/player.tscn create mode 100644 tiles_armagedon_a1.res diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8ad74f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0af181c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Godot 4+ specific ignores +.godot/ +/android/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..56226ce --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +``` +- project.godot +- scenes/ + - Main.tscn + - Lobby.tscn + - Game.tscn + - Player.tscn + - Token.tscn + - Board.tscn +- scripts/ + - Main.gd + - Lobby.gd + - Game.gd + - Player.gd + - Token.gd + - Board.gd + - NetworkManager.gd +- assets/ + - (3D models, textures, etc.) +``` \ No newline at end of file diff --git a/addons/.gitignore b/addons/.gitignore new file mode 100644 index 0000000..7e07042 --- /dev/null +++ b/addons/.gitignore @@ -0,0 +1,27 @@ +# Godot 4+ specific ignores +.godot/ + +# Godot-specific ignores +.import/ +export.cfg +export_presets.cfg +# Dummy HTML5 export presets file for continuous integration +!.github/dist/export_presets.cfg + +# Imported translations (automatically generated from CSV files) +*.translation + +# Mono-specific ignores +.mono/ +data_*/ +mono_crash.*.json + +# System/tool-specific ignores +.directory +.DS_Store +*~ +*.blend1 +*.zip + +# Jetbrains IDE files +.idea/ \ No newline at end of file diff --git a/addons/README.md b/addons/README.md new file mode 100644 index 0000000..408cdf7 --- /dev/null +++ b/addons/README.md @@ -0,0 +1,350 @@ +# EnhancedGridMap Plugin for Godot 4.3 + +## Overview + +EnhancedGridMap is a powerful plugin for Godot 4.3 that extends the functionality of the built-in GridMap node. It provides additional features for grid-based game development, including custom cell states, A* pathfinding, and an intuitive editor interface. + +## Features + +- Custom grid size (columns and rows) +- Auto-generation of grid +- Custom cell states with visual representation +- A* pathfinding with diagonal movement option +- Randomization of grid cells +- Fill grid with specific cell types +- Editor dock for easy manipulation of grid properties + +## Installation + +1. Copy the `enhanced_gridmap` folder into your Godot project's `addons` directory. +2. Enable the plugin in Project Settings > Plugins. + +## EnhancedGridMap Dock + +The EnhancedGridMap Dock provides a user-friendly interface to control and manipulate the EnhancedGridMap node. Here's a detailed explanation of all features available in the dock: + +### Grid Properties + +1. **Columns**: Set the number of columns in the grid. +2. **Rows**: Set the number of rows in the grid. +3. **Auto Generate**: Toggle automatic grid generation when properties change. + +### Grid Operations + +1. **Generate**: Manually generate the grid based on current properties. +2. **Clear**: Remove all cells from the grid. +3. **Randomize**: Randomly assign cell types based on defined states. +4. **Fill**: Fill the entire grid with a specific cell type. + +### Cell States + +The dock allows you to define and manage custom cell states: + +1. **Default States**: + - Normal + - Hover + - Start + - End + - Non-Walkable + +2. **Custom States**: + - Add new states with the "Add Item State" button. + - For each state, you can set: + - Name + - ID (used in scripts to reference the state) + - Include in Randomize (toggle) + - Randomize Percentage (when included in randomization) + +3. **State Management**: + - Edit existing states + - Remove custom states + +### A* Pathfinding + +1. **Start Position**: Set the X and Z coordinates for the pathfinding start point. +2. **End Position**: Set the X and Z coordinates for the pathfinding end point. +3. **Find Path**: Calculate and visualize the path between start and end points. +4. **Diagonal Movement**: Toggle to allow diagonal movement in pathfinding. + +### Visual Grid Editor + +The dock includes a visual representation of the grid: + +1. Each cell displays its coordinates. +2. Drop-down menus in each cell allow you to change the cell's state directly. + +## In-Depth Feature Explanation + +### Custom Grid Generation + +The EnhancedGridMap allows for flexible grid generation: + +1. **Auto-generation**: The grid can automatically update when properties change. +2. **Manual generation**: Use the `generate_grid()` method for custom generation logic. +3. **Clear and regenerate**: Useful for level resets or procedural generation. + +### Cell States + +Cell states are central to the EnhancedGridMap's functionality: + +1. **Default states**: Predefined states for common use cases (normal, hover, start, end, non-walkable). +2. **Custom states**: Create states for specific game mechanics (e.g., water, lava, ice). +3. **State properties**: + - ID: Used in scripts to set or check cell states. + - Randomize inclusion: Determine if the state should be included in randomization. + - Randomize percentage: Control the frequency of the state when randomizing. + +### A* Pathfinding + +The integrated A* pathfinding system offers: + +1. **Efficient pathfinding**: Quickly find optimal paths between two points. +2. **Diagonal movement**: Option to allow or disallow diagonal movement. +3. **Custom cost functions**: Override `get_cell_cost()` for complex movement rules. +4. **Path visualization**: Easily visualize calculated paths for debugging. + +### Grid Manipulation + +Several methods for manipulating the grid: + +1. **`set_cell_from_data(x, z, item_index)`**: Set a specific cell's state. +2. **`fill_grid(item_index)`**: Fill the entire grid with a specific state. +3. **`randomize_grid()`**: Randomly assign states based on defined probabilities. +4. **`randomize_grid_custom(randomize_states)`**: Use custom randomization rules. + +### Extending Functionality + +The EnhancedGridMap is designed to be easily extended: + +1. **Custom grid generation**: Override `generate_grid()` for specialized layouts. +2. **Custom pathfinding costs**: Implement `get_cell_cost()` for complex movement rules. +3. **Integration with game mechanics**: Use signals like `grid_updated` to trigger game events. + +### Editor Integration + +The plugin seamlessly integrates with the Godot editor: + +1. **Visual editing**: Manipulate the grid directly in the editor viewport. +2. **Real-time updates**: Changes in the dock immediately reflect in the scene. +3. **Custom inspector**: The EnhancedGridMap node has a custom inspector for easy property editing. + +## Plugin Usage + +### Basic Setup + +1. Add an `EnhancedGridMap` node to your scene. +2. Assign a `MeshLibrary` to the `EnhancedGridMap`. +3. Use the `EnhancedGridMap` dock in the editor to configure grid properties and cell states. + +### Scripting + +You can also interact with the `EnhancedGridMap` through GDScript. Here's a basic example: + +```gdscript +var enhanced_gridmap = $EnhancedGridMap + +# Generate a 10x10 grid +enhanced_gridmap.columns = 10 +enhanced_gridmap.rows = 10 +enhanced_gridmap.generate_grid() + +# Find a path from (0,0) to (5,5) +var path = enhanced_gridmap.find_path(Vector2(0, 0), Vector2(5, 5)) +``` + +## Sample Scene and Player Movement + +The plugin includes a sample scene that demonstrates how to implement player movement using the EnhancedGridMap. This scene showcases pathfinding and grid-based movement. + +### Scene Setup + +The sample scene (`main.tscn`) includes: + +1. An `EnhancedGridMap` node with a pre-configured grid. +2. A player character `CharacterBody3D` with a custom script for grid-based movement. +3. A top-down camera for easy visualization. + +### Player Movement Script + +The `player.gd` script provides a flexible implementation of grid-based movement using the EnhancedGridMap. Key features include: + +- Click-to-move functionality +- Pathfinding using the EnhancedGridMap's A* algorithm +- Smooth movement along the calculated path +- Customizable cell size and offset +- Option for diagonal movement + +### Usage Example + +To use the player movement script in your own scene: + +1. Add an `EnhancedGridMap` to your scene. +2. Add a `CharacterBody3D` (or other suitable node) for your player. +3. Attach the `player.gd` script to your player node. +4. Set the `enhanced_gridmap_path` and `player_path` in the inspector. +5. Customize other properties like `cell_size` and `use_diagonal_movement` as needed. + +Here's a minimal setup example: + +```gdscript +extends Node3D + +@onready var enhanced_gridmap = $EnhancedGridMap +@onready var player = $Player + +func _ready(): + player.enhanced_gridmap_path = enhanced_gridmap.get_path() + player.player_path = player.get_path() + player.cell_size = Vector3(2, 2, 2) + player.use_diagonal_movement = true +``` + +This setup allows for click-to-move functionality on your EnhancedGridMap, with the player finding and following optimal paths while avoiding non-walkable cells. + +## API Reference + +### Properties + +- `columns: int` - Number of columns in the grid +- `rows: int` - Number of rows in the grid +- `auto_generate: bool` - Whether to automatically generate the grid when properties change +- `normal_item: int` - Item index for normal cells +- `hover_item: int` - Item index for hover state cells +- `start_item: int` - Item index for pathfinding start cells +- `end_item: int` - Item index for pathfinding end cells +- `non_walkable_item: int` - Item index for non-walkable cells +- `diagonal_movement: bool` - Whether to allow diagonal movement in pathfinding + +### Methods + +#### Grid Management + +- `generate_grid()` - Generate the grid based on current properties +- `clear_grid()` - Clear all cells in the grid +- `randomize_grid()` - Randomize cell types in the grid +- `randomize_grid_custom(randomize_states: Array)` - Randomize cell types based on custom states +- `fill_grid(item_index: int)` - Fill the entire grid with a specific item type + +#### Cell Manipulation + +- `set_cell_from_data(x: int, z: int, item_index: int)` - Set a specific cell's item type +- `get_cell_cost(x: int, z: int) -> float` - Get the cost of moving through a specific cell + +#### Pathfinding + +- `find_path(start: Vector2, end: Vector2) -> Array` - Find a path between two points +- `set_diagonal_movement(enable: bool)` - Enable or disable diagonal movement in pathfinding +- `set_point_solid(x: int, z: int, is_solid: bool)` - Set whether a point is solid (non-walkable) for pathfinding + +### Signals + +- `mesh_library_changed` - Emitted when the MeshLibrary is changed +- `grid_updated` - Emitted when the grid is updated + +### Example Scene : Player Movement API + +Here are the main properties and methods of the player movement script: + +#### Properties + +- `enhanced_gridmap_path: NodePath` - Path to the EnhancedGridMap node +- `player_path: NodePath` - Path to the player node +- `cell_size: Vector3` - Size of each grid cell +- `cell_offset: Vector3` - Offset applied to the player's position +- `center_x: bool` - Center the player horizontally within the cell +- `center_y: bool` - Center the player vertically within the cell +- `center_z: bool` - Center the player depth-wise within the cell +- `use_diagonal_movement: bool` - Allow diagonal movement in pathfinding + +#### Methods + +- `find_valid_starting_position() -> Vector2i` - Find a valid starting position on the grid +- `move_player_to_clicked_position(grid_position: Vector2i)` - Move the player to a clicked grid position +- `move_player_along_path(path: Array)` - Move the player along a calculated path +- `update_player_position(grid_position: Vector2i)` - Update the player's position based on a grid position +- `grid_to_world(grid_position: Vector2i) -> Vector3` - Convert a grid position to a world position + +## Extending the Plugin + +### Custom Item States + +You can add custom item states to the EnhancedGridMap: + +1. In the `EnhancedGridMap` dock, click the "Add Item State" button. +2. Set a name, ID, and randomization properties for the new state. +3. Use the new state's ID when setting cell types in your scripts. + +### Custom Pathfinding Costs + +Override the `get_cell_cost` method in a script extending EnhancedGridMap to implement custom pathfinding costs: + +```gdscript +extends EnhancedGridMap + +func get_cell_cost(x: int, z: int) -> float: + var cell_item = get_cell_item(Vector3i(x, 0, z)) + match cell_item: + 0: return 1.0 # Normal cell + 1: return 2.0 # Slow terrain + 2: return 0.5 # Fast terrain + _: return INF # Non-walkable +``` + +### Custom Grid Generation + +You can implement custom grid generation by overriding the `generate_grid` method: + +```gdscript +extends EnhancedGridMap + +func generate_grid(): + clear() + for x in range(columns): + for z in range(rows): + var item_index = (x + z) % 2 # Checkerboard pattern + set_cell_item(Vector3i(x, 0, z), item_index) + update_grid_data() + initialize_astar() + update_astar_costs() +``` + +## Extending the Player Movement + +You can extend the player movement functionality by overriding or adding methods to the `player.gd` script. For example: + +### Custom Movement Rules + +```gdscript +extends "res://addons/enhanced_gridmap/examples/player.gd" + +func is_valid_move(from: Vector2i, to: Vector2i) -> bool: + # Add custom logic for valid moves + var distance = from.distance_to(to) + return distance <= 1 and super.is_valid_move(from, to) +``` + +### Additional Interactions + +```gdscript +extends "res://addons/enhanced_gridmap/examples/player.gd" + +func _unhandled_input(event): + super._unhandled_input(event) + + if event.is_action_pressed("interact"): + interact_with_current_cell() + +func interact_with_current_cell(): + var cell_item = enhanced_gridmap.get_cell_item(Vector3i(current_position.x, 0, current_position.y)) + # Add custom interaction logic based on cell_item +``` + +These examples demonstrate how you can build upon the provided player movement script to create more complex game mechanics that integrate seamlessly with the EnhancedGridMap plugin. + +## Contributing + +Contributions to the EnhancedGridMap plugin are welcome! Please submit pull requests or issues on the project's GitHub repository. + +## License + +This plugin is released under the MIT License. See the LICENSE file for details. \ No newline at end of file diff --git a/addons/enhanced_gridmap/enhanced_gridmap.gd b/addons/enhanced_gridmap/enhanced_gridmap.gd new file mode 100644 index 0000000..2d7ffbf --- /dev/null +++ b/addons/enhanced_gridmap/enhanced_gridmap.gd @@ -0,0 +1,298 @@ +@tool +class_name EnhancedGridMap +extends GridMap + +signal mesh_library_changed +signal grid_updated + +@export var columns: int = 10 : set = set_columns +@export var rows: int = 10 : set = set_rows +@export var auto_generate: bool = false : set = set_auto_generate + +@export var normal_items: Array[int] = [0] +@export var non_walkable_items: Array[int] = [4] +@export var hover_item: int = 1 +@export var start_item: int = 2 +@export var end_item: int = 3 + +var current_mesh_library: MeshLibrary +var grid_data: Array = [] + +# A* Pathfinding variables +var astar = AStar2D.new() +var path = [] + +# Item states +enum ItemState {NORMAL, HOVER, START, END, NON_WALKABLE} + +# Add this to the class variables +var diagonal_movement: bool = false + + +func _ready(): + mesh_library_changed.connect(_on_mesh_library_changed) + if not Engine.is_editor_hint() and auto_generate: + generate_grid() + + # Validate item indices + validate_item_indices() + +func validate_item_indices(): + if not mesh_library: + print("Warning: No MeshLibrary assigned to GridMap") + return + + var item_list = mesh_library.get_item_list() + var max_index = item_list.size() - 1 + + normal_items = normal_items.filter(func(item): return item >= 0 and item <= max_index) + hover_item = clamp(hover_item, 0, max_index) + start_item = clamp(start_item, 0, max_index) + end_item = clamp(end_item, 0, max_index) + non_walkable_items = non_walkable_items.filter(func(item): return item >= 0 and item <= max_index) + + if normal_items.is_empty(): + normal_items = [0] + if non_walkable_items.is_empty(): + non_walkable_items = [max_index] + +func set_columns(value: int): + columns = value + if auto_generate: + generate_grid() + else: + update_grid_data() + +func set_rows(value: int): + rows = value + if auto_generate: + generate_grid() + else: + update_grid_data() + +func set_auto_generate(value: bool): + auto_generate = value + if auto_generate: + generate_grid() + +# Override the existing functions to update A* data +func generate_grid(): + clear() + if not mesh_library: + print("Error: No MeshLibrary assigned to GridMap") + return + + validate_item_indices() + + current_mesh_library = mesh_library + var item_list = mesh_library.get_item_list() + if item_list.size() < 5: + print("Warning: MeshLibrary should have at least 5 items") + + for x in range(columns): + for z in range(rows): + set_cell_item(Vector3i(x, 0, z), normal_items[0]) + + update_grid_data() + initialize_astar() + update_astar_costs() + +func clear_grid(): + clear() + update_grid_data() + +func randomize_grid(): + if not mesh_library: + print("Error: No MeshLibrary assigned to GridMap") + return + + validate_item_indices() + + var rng = RandomNumberGenerator.new() + rng.randomize() + + for x in range(columns): + for z in range(rows): + var random_value = rng.randi() % 100 # Generate a random number between 0 and 99 + var item_index + if random_value < 80: + item_index = normal_items[rng.randi() % normal_items.size()] + else: + item_index = non_walkable_items[rng.randi() % non_walkable_items.size()] + set_cell_from_data(x, z, item_index) + + update_grid_data() + initialize_astar() + update_astar_costs() + emit_signal("grid_updated") + +func randomize_grid_custom(randomize_states: Array): + if not mesh_library: + print("Error: No MeshLibrary assigned to GridMap") + return + + var rng = RandomNumberGenerator.new() + rng.randomize() + + for x in range(columns): + for z in range(rows): + var random_value = rng.randf() * 100 # Generate a random number between 0 and 100 + var accumulated_percentage = 0 + var selected_state = null + + for state in randomize_states: + accumulated_percentage += state.randomize_percentage + if random_value <= accumulated_percentage: + selected_state = state + break + + if selected_state: + set_cell_from_data(x, z, selected_state.id) + else: + set_cell_from_data(x, z, randomize_states[0].id) # Default to the first state if no match + + update_grid_data() + initialize_astar() + update_astar_costs() + emit_signal("grid_updated") + +func fill_grid(item_index: int): + if not mesh_library: + print("No MeshLibrary assigned to GridMap") + return + + if item_index < 0 or item_index >= mesh_library.get_item_list().size(): + print("Invalid item index") + return + + for x in range(columns): + for z in range(rows): + set_cell_item(Vector3i(x, 0, z), item_index) + + update_grid_data() + +func _on_mesh_library_changed(): + validate_item_indices() + if auto_generate: + generate_grid() + +func _set(property, value): + if property == "mesh_library": + mesh_library = value + _on_mesh_library_changed() + return true + return false + +func update_grid_data(): + grid_data.clear() + for z in range(rows): + var row = [] + for x in range(columns): + row.append(get_cell_item(Vector3i(x, 0, z))) + grid_data.append(row) + emit_signal("grid_updated") + +func set_cell_from_data(x: int, z: int, item_index: int): + if x >= 0 and x < columns and z >= 0 and z < rows: + if not mesh_library: + print("Error: No MeshLibrary assigned to GridMap") + return + + var item_list = mesh_library.get_item_list() + var max_index = item_list.size() - 1 + var valid_index = clamp(item_index, 0, max_index) + + set_cell_item(Vector3i(x, 0, z), valid_index) + grid_data[z][x] = valid_index + var point_id = z * columns + x + var cost = get_cell_cost(x, z) + if cost == INF: + astar.set_point_disabled(point_id, true) + else: + astar.set_point_disabled(point_id, false) + astar.set_point_weight_scale(point_id, cost) + +# New A* Pathfinding functions +# Modify the initialize_astar function +func initialize_astar(): + astar.clear() + for x in range(columns): + for z in range(rows): + var point_id = z * columns + x + astar.add_point(point_id, Vector2(x, z)) + + # Connect to neighboring points + if x > 0: + astar.connect_points(point_id, point_id - 1) + if z > 0: + astar.connect_points(point_id, point_id - columns) + + # Add diagonal connections if diagonal movement is enabled + if diagonal_movement: + if x > 0 and z > 0: + astar.connect_points(point_id, point_id - columns - 1) # Top-left + if x < columns - 1 and z > 0: + astar.connect_points(point_id, point_id - columns + 1) # Top-right + + update_astar_costs() + +# Add this function to toggle diagonal movement +func set_diagonal_movement(enable: bool): + diagonal_movement = enable + initialize_astar() # Reinitialize the A* graph with new connections + +func set_point_solid(x: int, z: int, is_solid: bool): + var point_id = z * columns + x + astar.set_point_disabled(point_id, is_solid) + +func find_path(start: Vector2, end: Vector2) -> Array: + var start_point = start.y * columns + start.x + var end_point = end.y * columns + end.x + path = astar.get_point_path(start_point, end_point) + + # Visualize the path + clear_path_visualization() + set_cell_item(Vector3i(start.x, 0, start.y), start_item) + set_cell_item(Vector3i(end.x, 0, end.y), end_item) + for point in path: + if point != start and point != end: + set_cell_item(Vector3i(point.x, 0, point.y), hover_item) + + return path + +func clear_path_visualization(): + for x in range(columns): + for z in range(rows): + var cell_item = get_cell_item(Vector3i(x, 0, z)) + if cell_item == hover_item or cell_item == start_item or cell_item == end_item: + set_cell_item(Vector3i(x, 0, z), normal_items[0]) + +func get_cell_cost(x: int, z: int) -> float: + var cell_item = get_cell_item(Vector3i(x, 0, z)) + if cell_item in non_walkable_items: + return INF + elif cell_item == hover_item: + return 0.5 + elif cell_item == start_item or cell_item == end_item: + return 0.0 + return 1.0 + +func update_astar_costs(): + for x in range(columns): + for z in range(rows): + var point_id = z * columns + x + var cost = get_cell_cost(x, z) + if cost == INF: + astar.set_point_disabled(point_id, true) + else: + astar.set_point_disabled(point_id, false) + astar.set_point_weight_scale(point_id, cost) + +func get_cell_rotation(position: Vector3i) -> int: + return get_cell_item_orientation(position) + +func set_cell_rotation(position: Vector3i, mode: int): + var item = get_cell_item(position) + if item != INVALID_CELL_ITEM: + var orientation = int(mode) + set_cell_item(position, item, orientation) diff --git a/addons/enhanced_gridmap/enhanced_gridmap_dock.gd b/addons/enhanced_gridmap/enhanced_gridmap_dock.gd new file mode 100644 index 0000000..2bafa31 --- /dev/null +++ b/addons/enhanced_gridmap/enhanced_gridmap_dock.gd @@ -0,0 +1,349 @@ +@tool +extends Control + +var enhanced_gridmap: EnhancedGridMap + +@onready var columns_spin = $VBoxContainer/Columns/SpinBox +@onready var rows_spin = $VBoxContainer/Rows/SpinBox +@onready var auto_generate_check = $VBoxContainer/AutoGenerate +@onready var generate_button = $VBoxContainer/GenerateButton +@onready var clear_button = $VBoxContainer/ClearButton +@onready var randomize_button = $VBoxContainer/RandomizeButton +@onready var fill_options = $VBoxContainer/FillOptions +@onready var fill_button = $VBoxContainer/FillButton +@onready var grid_container = $VBoxContainer/GridScrollContainer/GridContainer + +# A* Pathfinding UI elements +@onready var start_x_spin = $VBoxContainer/AStarContainer/StartX/SpinBox +@onready var start_z_spin = $VBoxContainer/AStarContainer/StartZ/SpinBox +@onready var end_x_spin = $VBoxContainer/AStarContainer/EndX/SpinBox +@onready var end_z_spin = $VBoxContainer/AStarContainer/EndZ/SpinBox +@onready var find_path_button = $VBoxContainer/AStarContainer/FindPathButton +@onready var path_result_label = $VBoxContainer/AStarContainer/PathResultLabel +@onready var diagonal_movement_check = $VBoxContainer/AStarContainer/DiagonalMovement + +# Item state UI elements +@onready var normal_item_spin = $VBoxContainer/ItemStates/NormalItem/SpinBox +@onready var hover_item_spin = $VBoxContainer/ItemStates/HoverItem/SpinBox +@onready var start_item_spin = $VBoxContainer/ItemStates/StartItem/SpinBox +@onready var end_item_spin = $VBoxContainer/ItemStates/EndItem/SpinBox +@onready var non_walkable_item_spin = $VBoxContainer/ItemStates/NonWalkableItem/SpinBox +# Custom item states UI elements +@onready var item_states_container = $VBoxContainer/ItemStates/ItemStatesContainer +@onready var add_item_state_button = $VBoxContainer/ItemStates/AddItemStateButton + +var row_containers: Array = [] +var cell_options: Array = [] +var custom_item_states: Dictionary = {} + +func _ready(): + connect_signals() + initialize_custom_item_states() + print("EnhancedGridMapDock ready") + +func connect_signals(): + columns_spin.value_changed.connect(_on_columns_changed) + rows_spin.value_changed.connect(_on_rows_changed) + auto_generate_check.toggled.connect(_on_auto_generate_toggled) + generate_button.pressed.connect(_on_generate_pressed) + clear_button.pressed.connect(_on_clear_pressed) + randomize_button.pressed.connect(_on_randomize_pressed) + fill_button.pressed.connect(_on_fill_pressed) + find_path_button.pressed.connect(_on_find_path_pressed) + diagonal_movement_check.toggled.connect(_on_diagonal_movement_toggled) + add_item_state_button.pressed.connect(_on_add_item_state_pressed) + normal_item_spin.value_changed.connect(_on_normal_item_changed) + hover_item_spin.value_changed.connect(_on_hover_item_changed) + start_item_spin.value_changed.connect(_on_start_item_changed) + end_item_spin.value_changed.connect(_on_end_item_changed) + non_walkable_item_spin.value_changed.connect(_on_non_walkable_item_changed) + +func initialize_custom_item_states(): + # Add default item states + add_custom_item_state("Normal", 0) + #add_custom_item_state("Hover", 1) + #add_custom_item_state("Start", 2) + #add_custom_item_state("End", 3) + add_custom_item_state("Non-Walkable", 4) + +func add_custom_item_state(name: String, id: int): + var new_state = CustomItemState.new(name, id) + custom_item_states[id] = new_state + add_item_state_ui(new_state) + +func add_item_state_ui(item_state: CustomItemState): + var container = HBoxContainer.new() + var name_edit = LineEdit.new() + var id_spin = SpinBox.new() + var randomize_check = CheckBox.new() + var percentage_spin = SpinBox.new() + var remove_button = Button.new() + + name_edit.text = item_state.name + name_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL + name_edit.text_changed.connect(_on_item_state_name_changed.bind(item_state)) + + id_spin.value = item_state.id + id_spin.min_value = 0 + id_spin.max_value = 9999 + id_spin.value_changed.connect(_on_item_state_id_changed.bind(item_state)) + + randomize_check.text = "🎲" + randomize_check.button_pressed = item_state.include_in_randomize # Changed from 'pressed' to 'button_pressed' + randomize_check.toggled.connect(_on_item_state_randomize_toggled.bind(item_state)) + + percentage_spin.min_value = 0 + percentage_spin.max_value = 100 + percentage_spin.value = item_state.randomize_percentage + percentage_spin.suffix = "%" + percentage_spin.value_changed.connect(_on_item_state_percentage_changed.bind(item_state)) + + remove_button.text = "Del" + remove_button.pressed.connect(_on_remove_item_state_pressed.bind(item_state, container)) + + container.add_child(name_edit) + container.add_child(id_spin) + container.add_child(randomize_check) + container.add_child(percentage_spin) + container.add_child(remove_button) + + item_states_container.add_child(container) + +func _on_add_item_state_pressed(): + var new_id = custom_item_states.size() + add_custom_item_state("New State {0}".format([new_id]), new_id) + +func _on_item_state_name_changed(new_name: String, item_state: CustomItemState): + item_state.name = new_name + +func _on_item_state_id_changed(new_id: int, item_state: CustomItemState): + custom_item_states.erase(item_state.id) + item_state.id = new_id + custom_item_states[new_id] = item_state + +func _on_item_state_randomize_toggled(toggled: bool, item_state: CustomItemState): + item_state.include_in_randomize = toggled + +func _on_item_state_percentage_changed(new_percentage: float, item_state: CustomItemState): + item_state.randomize_percentage = new_percentage + +func _on_remove_item_state_pressed(item_state: CustomItemState, container: Container): + custom_item_states.erase(item_state.id) + container.queue_free() + +class CustomItemState: + var name: String + var id: int + var include_in_randomize: bool = false + var randomize_percentage: float = 0 + + func _init(_name: String, _id: int): + name = _name + id = _id + +func set_enhanced_gridmap(gridmap: EnhancedGridMap): + enhanced_gridmap = gridmap + enhanced_gridmap.grid_updated.connect(_on_grid_updated) + update_ui() + diagonal_movement_check.button_pressed = enhanced_gridmap.diagonal_movement + print("EnhancedGridMap set: ", enhanced_gridmap) + +func update_ui(): + if enhanced_gridmap: + columns_spin.value = enhanced_gridmap.columns + rows_spin.value = enhanced_gridmap.rows + auto_generate_check.button_pressed = enhanced_gridmap.auto_generate + _update_fill_options() + _update_grid_ui() + _update_astar_ui() + _update_item_state_ui() + print("UI updated. Columns: ", enhanced_gridmap.columns, " Rows: ", enhanced_gridmap.rows) + +func _update_fill_options(): + fill_options.clear() + if enhanced_gridmap and enhanced_gridmap.mesh_library: + var item_list = enhanced_gridmap.mesh_library.get_item_list() + for i in range(item_list.size()): + fill_options.add_item(enhanced_gridmap.mesh_library.get_item_name(item_list[i]), i) + +func _update_grid_ui(): + for child in grid_container.get_children(): + child.queue_free() + row_containers.clear() + cell_options.clear() + + if not enhanced_gridmap or not enhanced_gridmap.mesh_library: + print("No EnhancedGridMap or MeshLibrary") + return + + var item_list = enhanced_gridmap.mesh_library.get_item_list() + + print("Updating grid UI. Columns: ", enhanced_gridmap.columns, " Rows: ", enhanced_gridmap.rows) + + for z in range(enhanced_gridmap.rows): + var row_container = HBoxContainer.new() + row_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + grid_container.add_child(row_container) + row_containers.append(row_container) + + for x in range(enhanced_gridmap.columns): + var cell_container = VBoxContainer.new() + cell_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL + cell_container.size_flags_vertical = Control.SIZE_EXPAND_FILL + + var coord_label = Label.new() + coord_label.text = "(%d,%d)" % [x, z] + coord_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + coord_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + coord_label.size_flags_vertical = Control.SIZE_SHRINK_CENTER + cell_container.add_child(coord_label) + + var option = OptionButton.new() + option.set_meta("grid_position", Vector2i(x, z)) + for i in range(item_list.size()): + option.add_item(enhanced_gridmap.mesh_library.get_item_name(item_list[i]), i) + var cell_item = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) + option.select(cell_item) + option.item_selected.connect(_on_cell_item_selected.bind(option)) + option.size_flags_horizontal = Control.SIZE_EXPAND_FILL + option.size_flags_vertical = Control.SIZE_EXPAND_FILL + cell_container.add_child(option) + + # Add rotation Option for each cell + var rotation_option = OptionButton.new() + rotation_option.add_item("0°", 0) + rotation_option.add_item("90°", 10) + rotation_option.add_item("180°", 16) + rotation_option.add_item("270°", 22) + rotation_option.size_flags_horizontal = Control.SIZE_EXPAND_FILL + rotation_option.size_flags_vertical = Control.SIZE_SHRINK_CENTER + rotation_option.set_meta("grid_position", Vector2i(x, z)) + # Set the current item based on the cell's rotation + var current_rotation = enhanced_gridmap.get_cell_rotation(Vector3i(x, 0, z)) + match current_rotation: + 0: rotation_option.select(0) + 10: rotation_option.select(1) + 16: rotation_option.select(2) + 22: rotation_option.select(3) + rotation_option.item_selected.connect(_on_cell_rotation_changed.bind(rotation_option)) + + cell_container.add_child(rotation_option) + + row_container.add_child(cell_container) + cell_options.append(option) + + print("Grid UI updated. Total cells: ", cell_options.size()) + +func _update_astar_ui(): + start_x_spin.max_value = enhanced_gridmap.columns - 1 + start_z_spin.max_value = enhanced_gridmap.rows - 1 + end_x_spin.max_value = enhanced_gridmap.columns - 1 + end_z_spin.max_value = enhanced_gridmap.rows - 1 + +func _update_item_state_ui(): + normal_item_spin.value = enhanced_gridmap.normal_item + hover_item_spin.value = enhanced_gridmap.hover_item + start_item_spin.value = enhanced_gridmap.start_item + end_item_spin.value = enhanced_gridmap.end_item + non_walkable_item_spin.value = enhanced_gridmap.non_walkable_item + +func _on_columns_changed(value): + if enhanced_gridmap: + enhanced_gridmap.columns = value + print("Columns changed to: ", value) + +func _on_rows_changed(value): + if enhanced_gridmap: + enhanced_gridmap.rows = value + print("Rows changed to: ", value) + +func _on_auto_generate_toggled(button_pressed): + if enhanced_gridmap: + enhanced_gridmap.auto_generate = button_pressed + +func _on_generate_pressed(): + if enhanced_gridmap: + enhanced_gridmap.generate_grid() + print("Generate grid pressed") + +func _on_clear_pressed(): + if enhanced_gridmap: + enhanced_gridmap.clear_grid() + print("Clear grid pressed") + +func _on_randomize_pressed(): + if enhanced_gridmap: + var randomize_states = [] + var total_percentage = 0 + for state in custom_item_states.values(): + if state.include_in_randomize: + randomize_states.append(state) + total_percentage += state.randomize_percentage + + if total_percentage != 100: + print("Warning: Total randomize percentage is not 100%") + + enhanced_gridmap.randomize_grid_custom(randomize_states) + +func _on_fill_pressed(): + if enhanced_gridmap: + var selected_index = fill_options.get_selected_id() + enhanced_gridmap.fill_grid(selected_index) + print("Fill grid pressed with item index: ", selected_index) + +func _on_cell_item_selected(index: int, option: OptionButton): + var position = option.get_meta("grid_position") + if enhanced_gridmap: + enhanced_gridmap.set_cell_from_data(position.x, position.y, index) + print("Cell item selected: ", index, " at position: ", position) + +func _on_find_path_pressed(): + if enhanced_gridmap: + var start = Vector2(start_x_spin.value, start_z_spin.value) + var end = Vector2(end_x_spin.value, end_z_spin.value) + var path = enhanced_gridmap.find_path(start, end) + if path.is_empty(): + path_result_label.text = "No path found" + else: + path_result_label.text = "Path found: " + str(path) + print("Find path pressed. Start: ", start, " End: ", end) + +func _on_diagonal_movement_toggled(button_pressed): + if enhanced_gridmap: + enhanced_gridmap.set_diagonal_movement(button_pressed) + print("Diagonal movement toggled: ", button_pressed) + +func _on_normal_item_changed(value): + if enhanced_gridmap: + enhanced_gridmap.normal_item = value + print("Normal item changed to: ", value) + +func _on_hover_item_changed(value): + if enhanced_gridmap: + enhanced_gridmap.hover_item = value + print("Hover item changed to: ", value) + +func _on_start_item_changed(value): + if enhanced_gridmap: + enhanced_gridmap.start_item = value + print("Start item changed to: ", value) + +func _on_end_item_changed(value): + if enhanced_gridmap: + enhanced_gridmap.end_item = value + print("End item changed to: ", value) + +func _on_non_walkable_item_changed(value): + if enhanced_gridmap: + enhanced_gridmap.non_walkable_item = value + print("Non-walkable item changed to: ", value) + +func _on_grid_updated(): + update_ui() + +func _on_cell_rotation_changed(index: int, option: OptionButton): + var position = option.get_meta("grid_position") + var rotation_value = option.get_item_id(index) + if enhanced_gridmap: + enhanced_gridmap.set_cell_rotation(Vector3i(position.x, 0, position.y), rotation_value) + print("Cell rotation changed: ", rotation_value, " at position: ", position) diff --git a/addons/enhanced_gridmap/enhanced_gridmap_dock.tscn b/addons/enhanced_gridmap/enhanced_gridmap_dock.tscn new file mode 100644 index 0000000..30eeeed --- /dev/null +++ b/addons/enhanced_gridmap/enhanced_gridmap_dock.tscn @@ -0,0 +1,237 @@ +[gd_scene load_steps=2 format=3 uid="uid://bnkcae3aoavgh"] + +[ext_resource type="Script" path="res://addons/enhanced_gridmap/enhanced_gridmap_dock.gd" id="1_abcde"] + +[node name="Enhanced GridMap" type="ScrollContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_abcde") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="TitleLabel" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "☕︎ Enhanced Grid Map" + +[node name="HSeparator" type="HSeparator" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Columns" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/Columns"] +layout_mode = 2 +text = "Columns:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/Columns"] +layout_mode = 2 +min_value = 1.0 +value = 10.0 + +[node name="Rows" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/Rows"] +layout_mode = 2 +text = "Rows:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/Rows"] +layout_mode = 2 +min_value = 1.0 +value = 10.0 + +[node name="AutoGenerate" type="CheckBox" parent="VBoxContainer"] +layout_mode = 2 +text = "Auto Generate" + +[node name="ClearButton" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Clear Grid" + +[node name="GenerateButton" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Generate Grid" + +[node name="QuickFillLabel" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Quick Fill" + +[node name="HSeparator2" type="HSeparator" parent="VBoxContainer"] +layout_mode = 2 + +[node name="FillOptions" type="OptionButton" parent="VBoxContainer"] +layout_mode = 2 + +[node name="FillButton" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Fill Grid" + +[node name="CellMapLabel" type="Label" parent="VBoxContainer"] +layout_mode = 2 +text = "Cell Map" + +[node name="HSeparator3" type="HSeparator" parent="VBoxContainer"] +layout_mode = 2 + +[node name="GridScrollContainer" type="ScrollContainer" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 300) +layout_mode = 2 +size_flags_vertical = 3 + +[node name="GridContainer" type="VBoxContainer" parent="VBoxContainer/GridScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="ItemStates" type="VBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="MeshLibraryLabel" type="Label" parent="VBoxContainer/ItemStates"] +layout_mode = 2 +text = "Mesh Library" + +[node name="HSeparator4" type="HSeparator" parent="VBoxContainer/ItemStates"] +layout_mode = 2 + +[node name="ItemStatesContainer" type="VBoxContainer" parent="VBoxContainer/ItemStates"] +layout_mode = 2 + +[node name="AddItemStateButton" type="Button" parent="VBoxContainer/ItemStates"] +layout_mode = 2 +text = "Add New Item State" + +[node name="NormalItem" type="HBoxContainer" parent="VBoxContainer/ItemStates"] +visible = false +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/ItemStates/NormalItem"] +layout_mode = 2 +text = "Normal Item:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/ItemStates/NormalItem"] +layout_mode = 2 + +[node name="HoverItem" type="HBoxContainer" parent="VBoxContainer/ItemStates"] +visible = false +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/ItemStates/HoverItem"] +layout_mode = 2 +text = "Hover Item:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/ItemStates/HoverItem"] +layout_mode = 2 +value = 1.0 + +[node name="StartItem" type="HBoxContainer" parent="VBoxContainer/ItemStates"] +visible = false +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/ItemStates/StartItem"] +layout_mode = 2 +text = "Start Item:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/ItemStates/StartItem"] +layout_mode = 2 +value = 2.0 + +[node name="EndItem" type="HBoxContainer" parent="VBoxContainer/ItemStates"] +visible = false +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/ItemStates/EndItem"] +layout_mode = 2 +text = "End Item:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/ItemStates/EndItem"] +layout_mode = 2 +value = 3.0 + +[node name="NonWalkableItem" type="HBoxContainer" parent="VBoxContainer/ItemStates"] +visible = false +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/ItemStates/NonWalkableItem"] +layout_mode = 2 +text = "Non-Walkable Item:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/ItemStates/NonWalkableItem"] +layout_mode = 2 +value = 4.0 + +[node name="RandomizeButton" type="Button" parent="VBoxContainer"] +layout_mode = 2 +text = "Randomize Grid" + +[node name="AStarContainer" type="VBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="AStarLabel" type="Label" parent="VBoxContainer/AStarContainer"] +layout_mode = 2 +text = "A* Pathfinding" + +[node name="HSeparator5" type="HSeparator" parent="VBoxContainer/AStarContainer"] +layout_mode = 2 + +[node name="DiagonalMovement" type="CheckBox" parent="VBoxContainer/AStarContainer"] +layout_mode = 2 +text = "Diagonal Movement" + +[node name="StartX" type="HBoxContainer" parent="VBoxContainer/AStarContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/AStarContainer/StartX"] +layout_mode = 2 +text = "Start X:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/AStarContainer/StartX"] +layout_mode = 2 +max_value = 9.0 + +[node name="StartZ" type="HBoxContainer" parent="VBoxContainer/AStarContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/AStarContainer/StartZ"] +layout_mode = 2 +text = "Start Z:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/AStarContainer/StartZ"] +layout_mode = 2 +max_value = 9.0 + +[node name="EndX" type="HBoxContainer" parent="VBoxContainer/AStarContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/AStarContainer/EndX"] +layout_mode = 2 +text = "End X:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/AStarContainer/EndX"] +layout_mode = 2 +max_value = 9.0 + +[node name="EndZ" type="HBoxContainer" parent="VBoxContainer/AStarContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="VBoxContainer/AStarContainer/EndZ"] +layout_mode = 2 +text = "End Z:" + +[node name="SpinBox" type="SpinBox" parent="VBoxContainer/AStarContainer/EndZ"] +layout_mode = 2 +max_value = 9.0 + +[node name="FindPathButton" type="Button" parent="VBoxContainer/AStarContainer"] +layout_mode = 2 +text = "Find Path" + +[node name="PathResultLabel" type="Label" parent="VBoxContainer/AStarContainer"] +layout_mode = 2 +text = "Path Result:" +autowrap_mode = 2 diff --git a/addons/enhanced_gridmap/icon.png b/addons/enhanced_gridmap/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7378e7bdfe41bd530b9baee71a4661977d526b79 GIT binary patch literal 413 zcmV;O0b>4%P)Y zKV)G8CIIhC7;s*z0by@4AT`tppsoNl1E?WLy#mbh85AOO+O~ZG@M)=G7`9PI^3kdb zy-^p|u=r@@A%qY@6qon|#GCI2oJcmx00000NkvXX Hu0mjfR+6Lz literal 0 HcmV?d00001 diff --git a/addons/enhanced_gridmap/icon.png.import b/addons/enhanced_gridmap/icon.png.import new file mode 100644 index 0000000..cbbe597 --- /dev/null +++ b/addons/enhanced_gridmap/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cak1euopkw681" +path="res://.godot/imported/icon.png-55ce7476be277fe78f056db0afa06b3b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/enhanced_gridmap/icon.png" +dest_files=["res://.godot/imported/icon.png-55ce7476be277fe78f056db0afa06b3b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/addons/enhanced_gridmap/meshlibrary/default.tres b/addons/enhanced_gridmap/meshlibrary/default.tres new file mode 100644 index 0000000..2d6bba4 --- /dev/null +++ b/addons/enhanced_gridmap/meshlibrary/default.tres @@ -0,0 +1,49 @@ +[gd_resource type="MeshLibrary" load_steps=7 format=3 uid="uid://54tpx8cmksfc"] + +[ext_resource type="ArrayMesh" uid="uid://dqguomxd16u0i" path="res://assets/models/meshes/start.res" id="1_xdwel"] +[ext_resource type="ArrayMesh" uid="uid://dspusnbkr74hg" path="res://assets/models/meshes/hover.res" id="2_5gp4i"] +[ext_resource type="Material" uid="uid://bsyh0x4cy5qyr" path="res://assets/models/meshes/end.tres" id="3_qi66w"] +[ext_resource type="ArrayMesh" uid="uid://d4himvyb81in8" path="res://assets/models/meshes/non-walkable.res" id="4_h83ju"] +[ext_resource type="ArrayMesh" uid="uid://bgvropltcot0q" path="res://assets/models/meshes/normal.res" id="5_san4u"] + +[sub_resource type="PlaneMesh" id="PlaneMesh_ti6kf"] +material = ExtResource("3_qi66w") +size = Vector2(1, 1) + +[resource] +item/0/name = "normal" +item/0/mesh = ExtResource("1_xdwel") +item/0/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/0/shapes = [] +item/0/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/0/navigation_layers = 1 +item/1/name = "hover" +item/1/mesh = ExtResource("2_5gp4i") +item/1/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/1/shapes = [] +item/1/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/1/navigation_layers = 1 +item/2/name = "start" +item/2/mesh = ExtResource("1_xdwel") +item/2/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/2/shapes = [] +item/2/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/2/navigation_layers = 1 +item/3/name = "end" +item/3/mesh = SubResource("PlaneMesh_ti6kf") +item/3/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/3/shapes = [] +item/3/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/3/navigation_layers = 1 +item/4/name = "non-walkable" +item/4/mesh = ExtResource("4_h83ju") +item/4/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/4/shapes = [] +item/4/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/4/navigation_layers = 1 +item/6/name = "grass" +item/6/mesh = ExtResource("5_san4u") +item/6/mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/6/shapes = [] +item/6/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0) +item/6/navigation_layers = 1 diff --git a/addons/enhanced_gridmap/plugin.cfg b/addons/enhanced_gridmap/plugin.cfg new file mode 100644 index 0000000..da23124 --- /dev/null +++ b/addons/enhanced_gridmap/plugin.cfg @@ -0,0 +1,7 @@ +[plugin] + +name="Enchaced Gridmap" +description="" +author="Bengski" +version="1.0" +script="plugin.gd" diff --git a/addons/enhanced_gridmap/plugin.gd b/addons/enhanced_gridmap/plugin.gd new file mode 100644 index 0000000..0fa0f32 --- /dev/null +++ b/addons/enhanced_gridmap/plugin.gd @@ -0,0 +1,28 @@ +@tool +extends EditorPlugin + +var dock + +func _enter_tree(): + dock = preload("res://addons/enhanced_gridmap/enhanced_gridmap_dock.tscn").instantiate() + add_control_to_dock(DOCK_SLOT_LEFT_UL, dock) + add_custom_type("EnhancedGridMap", "GridMap", preload("res://addons/enhanced_gridmap/enhanced_gridmap.gd"), preload("res://addons/enhanced_gridmap/icon.png")) + +func _exit_tree(): + remove_control_from_docks(dock) + dock.free() + remove_custom_type("EnhancedGridMap") + +func _handles(object): + return object is EnhancedGridMap + +func _make_visible(visible): + if dock: + dock.visible = visible + +func _get_plugin_name(): + return "EnhancedGridMap" + +func _edit(object): + if dock and object is EnhancedGridMap: + dock.set_enhanced_gridmap(object) diff --git a/assets/main-environment.tres b/assets/main-environment.tres new file mode 100644 index 0000000..9794e52 --- /dev/null +++ b/assets/main-environment.tres @@ -0,0 +1,31 @@ +[gd_resource type="Environment" load_steps=3 format=3 uid="uid://jbptgqvstei3"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_lg8b7"] +sky_horizon_color = Color(0.67451, 0.682353, 0.698039, 1) +sky_curve = 0.0175 +ground_bottom_color = Color(1, 1, 1, 1) +ground_curve = 0.171484 + +[sub_resource type="Sky" id="Sky_7bk1c"] +sky_material = SubResource("ProceduralSkyMaterial_lg8b7") + +[resource] +background_mode = 1 +background_color = Color(0.560784, 0.592157, 0.670588, 1) +sky = SubResource("Sky_7bk1c") +ambient_light_source = 2 +ambient_light_color = Color(0.662745, 0.694118, 0.772549, 1) +ambient_light_energy = 0.75 +tonemap_mode = 2 +ssao_enabled = true +ssao_radius = 0.25 +ssao_intensity = 0.5 +ssao_power = 100.0 +ssao_horizon = 0.1 +sdfgi_cascades = 1 +sdfgi_max_distance = 25.6 +sdfgi_energy = 0.5 +glow_levels/2 = 0.6 +glow_levels/3 = 0.6 +glow_levels/5 = 0.0 +glow_intensity = 2.0 diff --git a/assets/models/Textures/colormap.png b/assets/models/Textures/colormap.png new file mode 100644 index 0000000000000000000000000000000000000000..971c13aa833f8d042231240c8b349b045237292d GIT binary patch literal 9385 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 F{tt9)9PR)B literal 0 HcmV?d00001 diff --git a/assets/models/Textures/colormap.png.import b/assets/models/Textures/colormap.png.import new file mode 100644 index 0000000..c7b836b --- /dev/null +++ b/assets/models/Textures/colormap.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://0odt7cvj574e" +path.bptc="res://.godot/imported/colormap.png-8189f3c9e30f86aaa136872b6ce58318.bptc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://assets/models/Textures/colormap.png" +dest_files=["res://.godot/imported/colormap.png-8189f3c9e30f86aaa136872b6ce58318.bptc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=true +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/models/meshes/end.tres b/assets/models/meshes/end.tres new file mode 100644 index 0000000..f78a86e --- /dev/null +++ b/assets/models/meshes/end.tres @@ -0,0 +1,6 @@ +[gd_resource type="StandardMaterial3D" format=3 uid="uid://bsyh0x4cy5qyr"] + +[resource] +transparency = 1 +albedo_color = Color(0, 0, 0, 0.490196) +ao_enabled = true diff --git a/assets/models/meshes/hover.res b/assets/models/meshes/hover.res new file mode 100644 index 0000000000000000000000000000000000000000..fb44c39d082610e53a79e60787a545e6bd57fc46 GIT binary patch literal 3195 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 literal 0 HcmV?d00001 diff --git a/assets/models/meshes/start.res b/assets/models/meshes/start.res new file mode 100644 index 0000000000000000000000000000000000000000..fb431a4e1ab023cb78b9f335e4a02233902ee379 GIT binary patch literal 3257 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 literal 0 HcmV?d00001 diff --git a/assets/models/meshes/tiles_armagedon.glb b/assets/models/meshes/tiles_armagedon.glb new file mode 100644 index 0000000000000000000000000000000000000000..e3efcd88dff5c4cd4d53365b2087e22028411d91 GIT binary patch literal 70288 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 Ef6J#li2wiq literal 0 HcmV?d00001 diff --git a/assets/models/meshes/tiles_armagedon.glb.import b/assets/models/meshes/tiles_armagedon.glb.import new file mode 100644 index 0000000..fa418e2 --- /dev/null +++ b/assets/models/meshes/tiles_armagedon.glb.import @@ -0,0 +1,111 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://djblns7id80rr" +path="res://.godot/imported/tiles_armagedon.glb-f07b282b31fcdfdfd952d6096029c13a.scn" + +[deps] + +source_file="res://assets/models/meshes/tiles_armagedon.glb" +dest_files=["res://.godot/imported/tiles_armagedon.glb-f07b282b31fcdfdfd952d6096029c13a.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/apply_root_scale=true +nodes/root_scale=1.0 +nodes/import_as_skeleton_bones=false +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=false +animation/remove_immutable_tracks=true +animation/import_rest_as_RESET=false +import_script/path="" +_subresources={ +"meshes": { +"tiles_armagedon_Cube_001": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 60.0, +"lods/normal_split_angle": 25.0, +"save_to_file/enabled": true, +"save_to_file/path": "res://assets/models/meshes/tiles_armagedon_a3.res" +}, +"tiles_armagedon_Cube_003": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 60.0, +"lods/normal_split_angle": 25.0, +"save_to_file/enabled": true, +"save_to_file/path": "res://assets/models/meshes/tiles_armagedon_a4.res" +}, +"tiles_armagedon_Cube_005": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 60.0, +"lods/normal_split_angle": 25.0, +"save_to_file/enabled": true, +"save_to_file/path": "res://assets/models/meshes/tiles_armagedon_a2.res" +}, +"tiles_armagedon_Cube_007": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 60.0, +"lods/normal_split_angle": 25.0, +"save_to_file/enabled": true, +"save_to_file/path": "res://tiles_armagedon_a1.res" +}, +"tiles_armagedon_Cube_012": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 60.0, +"lods/normal_split_angle": 25.0, +"save_to_file/enabled": true, +"save_to_file/path": "res://assets/models/meshes/tiles_armagedon_b1.res" +}, +"tiles_armagedon_Cube_013": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 60.0, +"lods/normal_split_angle": 25.0, +"save_to_file/enabled": true, +"save_to_file/path": "res://assets/models/meshes/tiles_armagedon_b2.res" +}, +"tiles_armagedon_Cube_014": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 60.0, +"lods/normal_split_angle": 25.0, +"save_to_file/enabled": true, +"save_to_file/path": "res://assets/models/meshes/tiles_armagedon_b4.res" +}, +"tiles_armagedon_Cube_015": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 60.0, +"lods/normal_split_angle": 25.0, +"save_to_file/enabled": true, +"save_to_file/path": "res://assets/models/meshes/tiles_armagedon_b3.res" +} +} +} +gltf/naming_version=1 +gltf/embedded_image_handling=1 diff --git a/assets/models/meshes/tiles_armagedon_a2.res b/assets/models/meshes/tiles_armagedon_a2.res new file mode 100644 index 0000000000000000000000000000000000000000..be617aff290ae57eeb5868071e298fb3b7b25b63 GIT binary patch literal 13003 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 F{2xN88dLxP literal 0 HcmV?d00001 diff --git a/assets/models/meshes/tiles_armagedon_a3.res b/assets/models/meshes/tiles_armagedon_a3.res new file mode 100644 index 0000000000000000000000000000000000000000..50c5ff8a948be289c20e9472c63b9a20ab93f415 GIT binary patch literal 3257 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 literal 0 HcmV?d00001 diff --git a/assets/models/meshes/tiles_armagedon_b1.res b/assets/models/meshes/tiles_armagedon_b1.res new file mode 100644 index 0000000000000000000000000000000000000000..040beb9d28d5cac28b3d669deac274d7ffa5b116 GIT binary patch literal 11257 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 K@L}|yRsI7%V+*hV literal 0 HcmV?d00001 diff --git a/assets/models/meshes/tiles_armagedon_b3.res b/assets/models/meshes/tiles_armagedon_b3.res new file mode 100644 index 0000000000000000000000000000000000000000..570343ccdc62eb48e25113d1b5fa5dc22700a005 GIT binary patch literal 7605 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 F{{~R;M~MIc literal 0 HcmV?d00001 diff --git a/assets/models/meshes/tiles_armagedon_b4.res b/assets/models/meshes/tiles_armagedon_b4.res new file mode 100644 index 0000000000000000000000000000000000000000..ce7a0f6ed826d09eccc4ee4a25d13dda1ecafc47 GIT binary patch literal 14581 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 KKbLh*2~7ZwSI1QV literal 0 HcmV?d00001 diff --git a/assets/textures/bub.png.import b/assets/textures/bub.png.import new file mode 100644 index 0000000..18086ea --- /dev/null +++ b/assets/textures/bub.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b4y41h16q6m34" +path.s3tc="res://.godot/imported/bub.png-d021652dace83d8c6e324f646b7df76d.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://assets/textures/bub.png" +dest_files=["res://.godot/imported/bub.png-d021652dace83d8c6e324f646b7df76d.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..9d8b7fa --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..bc7092a --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://y43x1uqeguem" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..57aa82c --- /dev/null +++ b/project.godot @@ -0,0 +1,25 @@ +; 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=5 + +[application] + +config/name="tekton-local" +run/main_scene="res://scenes/main_scene.tscn" +config/features=PackedStringArray("4.3", "Forward Plus") +config/icon="res://icon.svg" + +[display] + +window/size/viewport_width=1280 +window/size/viewport_height=720 + +[editor_plugins] + +enabled=PackedStringArray("res://addons/enhanced_gridmap/plugin.cfg") diff --git a/scenes/maiCE47.tmp b/scenes/maiCE47.tmp new file mode 100644 index 0000000..da380eb --- /dev/null +++ b/scenes/maiCE47.tmp @@ -0,0 +1,45 @@ +[gd_scene load_steps=8 format=3 uid="uid://dxn87yj8qnfpp"] + +[ext_resource type="MeshLibrary" uid="uid://54tpx8cmksfc" path="res://addons/enhanced_gridmap/meshlibrary/default.tres" id="1_110wo"] +[ext_resource type="Script" path="res://addons/enhanced_gridmap/enhanced_gridmap.gd" id="2_hbe1v"] +[ext_resource type="Script" path="res://scenes/player.gd" id="3_4ksq5"] +[ext_resource type="PackedScene" uid="uid://faku1hodioqu" path="res://assets/character.glb" id="4_0d1yx"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_xqgey"] +albedo_color = Color(0.85, 0.085, 0.238, 1) + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_l8ldl"] +material = SubResource("StandardMaterial3D_xqgey") + +[sub_resource type="SphereShape3D" id="SphereShape3D_3oo5r"] + +[node name="Main" type="Node3D"] + +[node name="EnhancedGridMap" type="GridMap" parent="."] +mesh_library = ExtResource("1_110wo") +data = { +"cells": PackedInt32Array(0, 0, 0, 0, 1, 0, 0, 2, 4, 0, 3, 0, 0, 4, 4, 0, 5, 0, 0, 6, 0, 0, 7, 0, 0, 8, 4, 0, 9, 0, 1, 0, 0, 1, 1, 0, 1, 2, 0, 1, 3, 0, 1, 4, 0, 1, 5, 4, 1, 6, 0, 1, 7, 0, 1, 8, 4, 1, 9, 4, 2, 0, 0, 2, 1, 0, 2, 2, 4, 2, 3, 0, 2, 4, 0, 2, 5, 4, 2, 6, 0, 2, 7, 0, 2, 8, 0, 2, 9, 0, 3, 0, 0, 3, 1, 0, 3, 2, 0, 3, 3, 0, 3, 4, 0, 3, 5, 0, 3, 6, 0, 3, 7, 0, 3, 8, 4, 3, 9, 0, 4, 0, 0, 4, 1, 4, 4, 2, 0, 4, 3, 0, 4, 4, 4, 4, 5, 0, 4, 6, 0, 4, 7, 0, 4, 8, 0, 4, 9, 4, 5, 0, 4, 5, 1, 0, 5, 2, 0, 5, 3, 4, 5, 4, 0, 5, 5, 4, 5, 6, 0, 5, 7, 0, 5, 8, 0, 5, 9, 0, 6, 0, 0, 6, 1, 0, 6, 2, 0, 6, 3, 0, 6, 4, 0, 6, 5, 0, 6, 6, 0, 6, 7, 0, 6, 8, 0, 6, 9, 4, 7, 0, 0, 7, 1, 0, 7, 2, 4, 7, 3, 0, 7, 4, 0, 7, 5, 0, 7, 6, 0, 7, 7, 0, 7, 8, 0, 7, 9, 0, 8, 0, 0, 8, 1, 0, 8, 2, 0, 8, 3, 0, 8, 4, 0, 8, 5, 0, 8, 6, 0, 8, 7, 0, 8, 8, 4, 8, 9, 0, 9, 0, 0, 9, 1, 0, 9, 2, 4, 9, 3, 0, 9, 4, 4, 9, 5, 0, 9, 6, 4, 9, 7, 0, 9, 8, 0, 9, 9, 0) +} +script = ExtResource("2_hbe1v") + +[node name="CharacterBody3D" type="CharacterBody3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, 0) +script = ExtResource("3_4ksq5") +enhanced_gridmap_path = NodePath("../EnhancedGridMap") +player_path = NodePath(".") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="CharacterBody3D"] +visible = false +mesh = SubResource("CapsuleMesh_l8ldl") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="CharacterBody3D"] +shape = SubResource("SphereShape3D_3oo5r") + +[node name="character2" parent="CharacterBody3D" instance=ExtResource("4_0d1yx")] + +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 10, 30, 10) +fov = 55.0 + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 10, 5) diff --git a/scenes/main.gd b/scenes/main.gd new file mode 100644 index 0000000..bb2c4f7 --- /dev/null +++ b/scenes/main.gd @@ -0,0 +1,133 @@ +extends Node3D # This script is attached to a Node3D (main scene) + +var multiplayer_peer = ENetMultiplayerPeer.new() # Create a new ENet multiplayer peer + +const PORT = 9999 # Define the port for multiplayer +const ADDRESS = "127.0.0.1" # Define the IP address for multiplayer + +var connected_peer_ids = [] # List of connected peer IDs +var local_player_character : CharacterBody3D # Reference to the local player character +var player_scene = preload("res://scenes/player.tscn") # Preload the player scene +var current_turn_index = 0 # Index of the current turn +@export var players = [] # List of player IDs +var game_started = false # Flag to track if the game has started + +var max_message_input_char = 51 # Maximum characters allowed in a message + +func _on_host_pressed(): # Called when the host button is pressed + $NetworkInfo/NetworkSideDisplay.text = "Server" + $Menu.visible = false + multiplayer_peer.create_server(PORT) # Create a multiplayer server + multiplayer.multiplayer_peer = multiplayer_peer + $NetworkInfo/UniquePeerID.text = str(multiplayer.get_unique_id()) + + add_player_character(1) # Add host player character + + players.append(1) # Add host to players list + if players.size() == 2: + start_game() # Start game if two players are connected + + multiplayer_peer.peer_connected.connect(_on_peer_connected) # Connect peer connected signal + +func _on_join_pressed(): # Called when the join button is pressed + $NetworkInfo/NetworkSideDisplay.text = "Client" + $Menu.visible = false + multiplayer_peer.create_client(ADDRESS, PORT) # Create a multiplayer client + multiplayer.multiplayer_peer = multiplayer_peer + $NetworkInfo/UniquePeerID.text = str(multiplayer.get_unique_id()) + +func _on_peer_connected(new_peer_id): # Called when a new peer connects + if multiplayer.is_server(): + await get_tree().create_timer(1).timeout + rpc("add_newly_connected_player_character", new_peer_id) # RPC call to add new player + rpc_id(new_peer_id, "add_previously_connected_player_characters", connected_peer_ids) # RPC call to sync existing players + add_player_character(new_peer_id) + players.append(new_peer_id) + if players.size() == 2 and not game_started: + start_game() # Start game if two players are connected + +func _on_peer_disconnected(peer_id): # Called when a peer disconnects + if multiplayer.is_server(): + connected_peer_ids.erase(peer_id) + players.erase(peer_id) + if players.size() < 2: + game_started = false + +func add_player_character(peer_id): # Add a player character to the game + connected_peer_ids.append(peer_id) + var player_character = player_scene.instantiate() + player_character.set_multiplayer_authority(peer_id) + add_child(player_character) + player_character.add_to_group("Players",true) + if peer_id == multiplayer.get_unique_id(): + local_player_character = player_character + +@rpc +func add_newly_connected_player_character(new_peer_id): # RPC function to add a new player character + add_player_character(new_peer_id) + +@rpc +func add_previously_connected_player_characters(peer_ids): # RPC function to add existing player characters + for peer_id in peer_ids: + add_player_character(peer_id) + +func _process(_delta): # Called every frame + if multiplayer.is_server() and game_started: + rpc("sync_turn_index", current_turn_index) # RPC call to sync turn index + +func start_game(): # Start the game + if multiplayer.is_server(): + game_started = true + connected_peer_ids.sort() + rpc("sync_game_start", connected_peer_ids) # RPC call to sync game start + current_turn_index = -1 + next_turn() + +@rpc +func sync_game_start(peer_ids): # RPC function to sync game start + connected_peer_ids = peer_ids + game_started = true + +@rpc("reliable") +func sync_turn_index(index): # RPC function to sync turn index + current_turn_index = index + +func next_turn(): # Move to the next turn + if multiplayer.is_server(): + current_turn_index += 1 + if current_turn_index >= connected_peer_ids.size(): + current_turn_index = -1 + next_turn() + else: + rpc("set_current_turn", connected_peer_ids[current_turn_index]) # RPC call to set current turn + +func request_next_turn(): # Request to move to the next turn + if multiplayer.is_server(): + end_current_turn() + else: + rpc_id(1, "server_end_current_turn") # RPC call to end turn on server + +@rpc("any_peer") +func server_end_current_turn(): # RPC function to end current turn on server + if multiplayer.is_server(): + end_current_turn() + +@rpc("any_peer", "call_local") +func set_current_turn(player_id): # RPC function to set the current turn + var players = get_tree().get_nodes_in_group("Players") + for player in players: + player.is_my_turn = (player.name == str(player_id)) + if player.is_my_turn: + player.start_turn() + +func end_current_turn(): # End the current turns + if multiplayer.is_server(): + next_turn() + rpc("sync_turn_index", current_turn_index) # RPC call to sync turn index + +func _on_message_input_text_submitted(new_text): # Handle message input + if new_text.length() > max_message_input_char: + new_text = new_text.substr(0, max_message_input_char) + " ... " + local_player_character.rpc("display_message", new_text) # RPC call to display message + $MessageInput.text = "" + $MessageInput.release_focus() diff --git a/scenes/main.tscn b/scenes/main.tscn new file mode 100644 index 0000000..eb3cc03 --- /dev/null +++ b/scenes/main.tscn @@ -0,0 +1,103 @@ +[gd_scene load_steps=5 format=3 uid="uid://dxn87yj8qnfpp"] + +[ext_resource type="MeshLibrary" uid="uid://54tpx8cmksfc" path="res://addons/enhanced_gridmap/meshlibrary/default.tres" id="1_110wo"] +[ext_resource type="Script" path="res://scenes/main.gd" id="1_xcpe3"] +[ext_resource type="Script" path="res://addons/enhanced_gridmap/enhanced_gridmap.gd" id="2_hbe1v"] +[ext_resource type="Environment" uid="uid://jbptgqvstei3" path="res://assets/main-environment.tres" id="4_ky38j"] + +[node name="Main" type="Node3D"] +script = ExtResource("1_xcpe3") + +[node name="EnhancedGridMap" type="GridMap" parent="."] +mesh_library = ExtResource("1_110wo") +cell_size = Vector3(1, 1, 1) +data = { +"cells": PackedInt32Array(0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, 5, 0, 0, 6, 0, 0, 7, 0, 0, 8, 0, 0, 9, 0, 1, 0, 0, 1, 1, 0, 1, 2, 0, 1, 3, 0, 1, 4, 0, 1, 5, 0, 1, 6, 0, 1, 7, 4, 1, 8, 0, 1, 9, 4, 2, 0, 4, 2, 1, 0, 2, 2, 0, 2, 3, 0, 2, 4, 0, 2, 5, 0, 2, 6, 0, 2, 7, 0, 2, 8, 0, 2, 9, 0, 3, 0, 0, 3, 1, 0, 3, 2, 0, 3, 3, 0, 3, 4, 0, 3, 5, 0, 3, 6, 0, 3, 7, 4, 3, 8, 0, 3, 9, 0, 4, 0, 0, 4, 1, 0, 4, 2, 0, 4, 3, 0, 4, 4, 0, 4, 5, 0, 4, 6, 0, 4, 7, 0, 4, 8, 0, 4, 9, 4, 5, 0, 0, 5, 1, 0, 5, 2, 0, 5, 3, 0, 5, 4, 0, 5, 5, 0, 5, 6, 0, 5, 7, 0, 5, 8, 0, 5, 9, 0, 6, 0, 4, 6, 1, 0, 6, 2, 0, 6, 3, 0, 6, 4, 0, 6, 5, 0, 6, 6, 0, 6, 7, 0, 6, 8, 0, 6, 9, 4, 7, 0, 0, 7, 1, 0, 7, 2, 0, 7, 3, 0, 7, 4, 0, 7, 5, 0, 7, 6, 0, 7, 7, 0, 7, 8, 0, 7, 9, 0, 8, 0, 0, 8, 1, 0, 8, 2, 0, 8, 3, 0, 8, 4, 4, 8, 5, 0, 8, 6, 0, 8, 7, 0, 8, 8, 0, 8, 9, 0, 9, 0, 0, 9, 1, 0, 9, 2, 0, 9, 3, 0, 9, 4, 0, 9, 5, 0, 9, 6, 0, 9, 7, 0, 9, 8, 0, 9, 9, 0) +} +script = ExtResource("2_hbe1v") +non_walkable_items = Array[int]([4, 5]) + +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 5, 30, 5) +visible = false +environment = ExtResource("4_ky38j") +fov = 35.5 + +[node name="Camera3D2" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 5, 30, 5) +environment = ExtResource("4_ky38j") +fov = 35.5 + +[node name="Panel" type="Panel" parent="."] +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -77.0 +offset_top = 6.0 +offset_right = 77.0 +offset_bottom = 59.0 +grow_horizontal = 2 + +[node name="NetworkInfo" type="VBoxContainer" parent="."] +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -58.0 +offset_top = 7.0 +offset_right = 58.0 +offset_bottom = 57.0 +grow_horizontal = 2 + +[node name="NetworkSideDisplay" type="Label" parent="NetworkInfo"] +layout_mode = 2 +text = "Network Side" +horizontal_alignment = 1 + +[node name="UniquePeerID" type="Label" parent="NetworkInfo"] +layout_mode = 2 +text = "Unique Peer ID" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Menu" type="VBoxContainer" parent="."] +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -85.0 +offset_top = -33.0 +offset_right = 85.0 +offset_bottom = 33.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Host" type="Button" parent="Menu"] +layout_mode = 2 +text = "Host" + +[node name="Join" type="Button" parent="Menu"] +layout_mode = 2 +text = "Join" + +[node name="MessageInput" type="LineEdit" parent="."] +anchors_preset = 7 +anchor_left = 0.5 +anchor_top = 1.0 +anchor_right = 0.5 +anchor_bottom = 1.0 +offset_left = -210.0 +offset_top = -46.0 +offset_right = 210.0 +offset_bottom = -15.0 +grow_horizontal = 2 +grow_vertical = 0 +placeholder_text = "Chat" +alignment = 1 + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = ExtResource("4_ky38j") + +[connection signal="pressed" from="Menu/Host" to="." method="_on_host_pressed"] +[connection signal="pressed" from="Menu/Join" to="." method="_on_join_pressed"] +[connection signal="text_submitted" from="MessageInput" to="." method="_on_message_input_text_submitted"] diff --git a/scenes/player.gd b/scenes/player.gd new file mode 100644 index 0000000..db81e65 --- /dev/null +++ b/scenes/player.gd @@ -0,0 +1,187 @@ +extends Node3D # This script is attached to a Node3D + +# Export variables for inspector configuration +@export var enhanced_gridmap_path: NodePath = "/root/Main/EnhancedGridMap" # Path to the EnhancedGridMap node +var enhanced_gridmap: EnhancedGridMap # References to the EnhancedGridMap node +@export var current_position: Vector2i # Current grid position of the player +var is_player_moving: bool = false # Flag to prevent movement while already moving + +# Customizable cell size and offset +@export var cell_size: Vector3 = Vector3(2, 2, 2) # Size of each grid cell +@export var cell_offset: Vector3 = Vector3(0, 0, 0) # Offset for the grid + +# Center offset flags +@export var center_x: bool = false # Center the player on X axis +@export var center_y: bool = false # Center the player on Y axis +@export var center_z: bool = false # Center the player on Z axis + +# Diagonal movement flag +@export var use_diagonal_movement: bool = false: # Allow diagonal movement + set(value): + use_diagonal_movement = value + if enhanced_gridmap: + enhanced_gridmap.set_diagonal_movement(value) + +# Turn management variables +@export var is_my_turn: bool = false: # Flag to indicate if it's this player's turn + set(value): + is_my_turn = value + if is_my_turn and is_multiplayer_authority(): + rpc("display_message", "It's your turn!") # RPC call to display turn message +@export var has_moved_this_turn = false # Flag to track if player has moved this turn +@onready var main_scene = get_tree().current_scene # Reference to the main scene + +func _ready(): # Called when the node enters the scene tree + name = str(get_multiplayer_authority()) # Set the node name to the multiplayer authority ID + $Name.text = str(name) # Set the displayed name + + enhanced_gridmap = get_node(enhanced_gridmap_path) # Get the EnhancedGridMap node + + if main_scene: + enhanced_gridmap = main_scene.get_node("EnhancedGridMap") # Get EnhancedGridMap from main scene + else: + push_error("Main scene not found") # Error if main scene not found + + if not enhanced_gridmap: + push_error("EnhancedGridMap node not found. Please set the correct path in the inspector.") + return + + enhanced_gridmap.initialize_astar() # Initialize A* pathfinding + enhanced_gridmap.set_diagonal_movement(use_diagonal_movement) # Set diagonal movement option + + current_position = find_valid_starting_position() # Find a valid starting position + update_player_position(current_position) # Update player's position + + set_process_unhandled_input(is_multiplayer_authority()) # Only process input for the authority + +func find_valid_starting_position() -> Vector2i: # Find a valid starting position + var rng = RandomNumberGenerator.new() + rng.randomize() + + var max_attempts = 100 + var attempts = 0 + + while attempts < max_attempts: + current_position = Vector2i(0, rng.randi_range(0, 9)) # Generate random position + var cell_item = enhanced_gridmap.get_cell_item(Vector3i(current_position.x, 0, current_position.y)) + + if cell_item not in enhanced_gridmap.non_walkable_items: + return current_position # Return valid position + + attempts += 1 + + return Vector2i(0, 0) # Default position if no valid position found + +func _physics_process(_delta): # Called every physics frame + if is_multiplayer_authority(): + rpc("remote_set_position", global_position) # RPC call to sync position + +func _unhandled_input(event): # Handle input events + if not is_multiplayer_authority() or not is_my_turn or is_player_moving: + return + + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + var camera = get_viewport().get_camera_3d() + var from = camera.project_ray_origin(event.position) + var to = from + camera.project_ray_normal(event.position) * 1000 + + var click_position = raycast_to_grid(from, to) + if click_position != Vector2i(-1, -1): + move_player_to_clicked_position(click_position) + +func raycast_to_grid(from: Vector3, to: Vector3) -> Vector2i: # Convert 3D raycast to grid position + var plane = Plane(Vector3.UP, cell_offset.y) + var intersection = plane.intersects_ray(from, to - from) + + if intersection: + var adjusted_intersection = intersection - cell_offset + var grid_position = Vector2i( + floor(adjusted_intersection.x / cell_size.x), + floor(adjusted_intersection.z / cell_size.z) + ) + + if grid_position.x >= 0 and grid_position.x < enhanced_gridmap.columns and \ + grid_position.y >= 0 and grid_position.y < enhanced_gridmap.rows: + return grid_position + + return Vector2i(-1, -1) # Invalid position + +func move_player_to_clicked_position(grid_position: Vector2i): # Move player to clicked position + var cell_item = enhanced_gridmap.get_cell_item(Vector3i(grid_position.x, 0, grid_position.y)) + + if cell_item in enhanced_gridmap.non_walkable_items: + print("Cannot move to non-walkable cell") + return + + var path = enhanced_gridmap.find_path(Vector2(current_position), Vector2(grid_position)) + + if path.size() > 1: + path.pop_front() + move_player_along_path(path) + else: + print("No valid path found") + +func move_player_along_path(path: Array): # Move player along calculated path + is_player_moving = true + var tween = create_tween() + tween.set_trans(Tween.TRANS_CUBIC) + tween.set_ease(Tween.EASE_IN_OUT) + + for point in path: + var target_position = grid_to_world(Vector2i(point.x, point.y)) + tween.tween_property(self, "position", target_position, 0.5) + + tween.tween_callback(func(): + current_position = Vector2i(path[-1].x, path[-1].y) + is_player_moving = false + enhanced_gridmap.clear_path_visualization() + has_moved_this_turn = true + end_turn() + ) + +func update_player_position(grid_position: Vector2i): # Update player's position + position = grid_to_world(grid_position) + +func grid_to_world(grid_position: Vector2i) -> Vector3: # Convert grid position to world position + var world_position = Vector3( + grid_position.x * cell_size.x, + cell_size.y, + grid_position.y * cell_size.z + ) + + world_position.x += cell_size.x * 0.5 + world_position.z += cell_size.z * 0.5 + + if center_x: + world_position.x += cell_size.x * 0.5 + if center_y: + world_position.y += cell_size.y * 0.5 + if center_z: + world_position.z += cell_size.z * 0.5 + + return world_position + cell_offset + +func start_turn(): # Start player's turn + has_moved_this_turn = false + is_my_turn = true + if is_multiplayer_authority(): + rpc("display_message", "It's your turn!") # RPC call to display turn message + +func end_turn(): # End player's turn + is_my_turn = false + has_moved_this_turn = false + if is_multiplayer_authority(): + get_node("/root/Main").request_next_turn() # Request next turn from main scene + +@rpc("any_peer", "call_local", "unreliable") +func remote_set_position(authority_position): # RPC function to sync position + global_position = authority_position + +@rpc("any_peer", "call_local") +func display_message(message): # RPC function to display messages + $Bubble.show() + $Bubble/Message.show() + $Bubble/Message.text = str(message) + await get_tree().create_timer(3).timeout + $Bubble.hide() + $Bubble/Message.hide() diff --git a/scenes/player.tscn b/scenes/player.tscn new file mode 100644 index 0000000..2f60bc6 --- /dev/null +++ b/scenes/player.tscn @@ -0,0 +1,58 @@ +[gd_scene load_steps=7 format=3 uid="uid://1dbdbg3q5778"] + +[ext_resource type="Script" path="res://scenes/player.gd" id="1_qecr4"] +[ext_resource type="Texture2D" uid="uid://b4y41h16q6m34" path="res://assets/textures/bub.png" id="2_5w327"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_xqgey"] +albedo_color = Color(0.85, 0.085, 0.238, 1) + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_l8ldl"] +material = SubResource("StandardMaterial3D_xqgey") + +[sub_resource type="SphereShape3D" id="SphereShape3D_3oo5r"] + +[sub_resource type="FontVariation" id="FontVariation_q2tkp"] +spacing_glyph = 5 + +[node name="CharacterBody3D" type="CharacterBody3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, 0) +script = ExtResource("1_qecr4") +cell_size = Vector3(1, 1, 1) + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = SubResource("CapsuleMesh_l8ldl") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("SphereShape3D_3oo5r") + +[node name="Name" type="Label3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.47085, 0) +billboard = 1 +modulate = Color(0.32, 0.614667, 1, 1) +text = "username" +font = SubResource("FontVariation_q2tkp") +font_size = 48 +outline_size = 26 +uppercase = true +autowrap_mode = 2 + +[node name="Bubble" type="Sprite3D" parent="."] +transform = Transform3D(1.4, 0, 0, 0, 1.4, 0, 0, 0, 1.4, 0, 7.5, 0) +visible = false +billboard = 1 +texture = ExtResource("2_5w327") + +[node name="Message" type="Label3D" parent="Bubble"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.00144005, 0) +billboard = 1 +double_sided = false +no_depth_test = true +render_priority = 1 +text = ". . ." +font = SubResource("FontVariation_q2tkp") +font_size = 48 +outline_size = 26 +uppercase = true +autowrap_mode = 3 +justification_flags = 171 +width = 700.0 diff --git a/tiles_armagedon_a1.res b/tiles_armagedon_a1.res new file mode 100644 index 0000000000000000000000000000000000000000..a65fb122ae00c675b2ad4eb777b4ffaa7ae43e23 GIT binary patch literal 11257 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 eRM`U?@nunD@A4}!gNQ|;d|x-+u$*d4>T1 literal 0 HcmV?d00001