commit 6b9360d4dc7706108e5f245996e6a0e3b3aa190c Author: adtpdn Date: Tue Oct 22 16:15:56 2024 +0800 first commit 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 0000000..7378e7b Binary files /dev/null and b/addons/enhanced_gridmap/icon.png differ 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 0000000..971c13a Binary files /dev/null and b/assets/models/Textures/colormap.png differ 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 0000000..fb44c39 Binary files /dev/null and b/assets/models/meshes/hover.res differ diff --git a/assets/models/meshes/non-walkable.res b/assets/models/meshes/non-walkable.res new file mode 100644 index 0000000..df4573d Binary files /dev/null and b/assets/models/meshes/non-walkable.res differ diff --git a/assets/models/meshes/normal.res b/assets/models/meshes/normal.res new file mode 100644 index 0000000..5318ae0 Binary files /dev/null and b/assets/models/meshes/normal.res differ diff --git a/assets/models/meshes/start.res b/assets/models/meshes/start.res new file mode 100644 index 0000000..fb431a4 Binary files /dev/null and b/assets/models/meshes/start.res differ diff --git a/assets/models/meshes/tiles_armagedon.glb b/assets/models/meshes/tiles_armagedon.glb new file mode 100644 index 0000000..e3efcd8 Binary files /dev/null and b/assets/models/meshes/tiles_armagedon.glb differ 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 0000000..be617af Binary files /dev/null and b/assets/models/meshes/tiles_armagedon_a2.res differ diff --git a/assets/models/meshes/tiles_armagedon_a3.res b/assets/models/meshes/tiles_armagedon_a3.res new file mode 100644 index 0000000..50c5ff8 Binary files /dev/null and b/assets/models/meshes/tiles_armagedon_a3.res differ diff --git a/assets/models/meshes/tiles_armagedon_a4.res b/assets/models/meshes/tiles_armagedon_a4.res new file mode 100644 index 0000000..0cc509d Binary files /dev/null and b/assets/models/meshes/tiles_armagedon_a4.res differ diff --git a/assets/models/meshes/tiles_armagedon_b1.res b/assets/models/meshes/tiles_armagedon_b1.res new file mode 100644 index 0000000..040beb9 Binary files /dev/null and b/assets/models/meshes/tiles_armagedon_b1.res differ diff --git a/assets/models/meshes/tiles_armagedon_b2.res b/assets/models/meshes/tiles_armagedon_b2.res new file mode 100644 index 0000000..b8faa0b Binary files /dev/null and b/assets/models/meshes/tiles_armagedon_b2.res differ diff --git a/assets/models/meshes/tiles_armagedon_b3.res b/assets/models/meshes/tiles_armagedon_b3.res new file mode 100644 index 0000000..570343c Binary files /dev/null and b/assets/models/meshes/tiles_armagedon_b3.res differ diff --git a/assets/models/meshes/tiles_armagedon_b4.res b/assets/models/meshes/tiles_armagedon_b4.res new file mode 100644 index 0000000..ce7a0f6 Binary files /dev/null and b/assets/models/meshes/tiles_armagedon_b4.res differ diff --git a/assets/models/non-walkable.tres b/assets/models/non-walkable.tres new file mode 100644 index 0000000..94a08cb --- /dev/null +++ b/assets/models/non-walkable.tres @@ -0,0 +1,10 @@ +[gd_resource type="StandardMaterial3D" format=3 uid="uid://dk01buuhoefgd"] + +[resource] +resource_name = "tile_a1" +cull_mode = 2 +albedo_color = Color(0.335217, 0.328683, 0.29189, 1) +metallic = 1.0 +roughness = 0.5 +emission_enabled = true +emission = Color(0.62, 0, 0, 1) diff --git a/assets/models/normal.tres b/assets/models/normal.tres new file mode 100644 index 0000000..9495f75 --- /dev/null +++ b/assets/models/normal.tres @@ -0,0 +1,7 @@ +[gd_resource type="StandardMaterial3D" format=3 uid="uid://cfkjh370756i4"] + +[resource] +resource_name = "tile_a1" +cull_mode = 2 +albedo_color = Color(0.335217, 0.328683, 0.29189, 1) +roughness = 0.5 diff --git a/assets/models/start.tres b/assets/models/start.tres new file mode 100644 index 0000000..0a75c9d --- /dev/null +++ b/assets/models/start.tres @@ -0,0 +1,7 @@ +[gd_resource type="StandardMaterial3D" format=3 uid="uid://dw0oxaf64g6sl"] + +[resource] +resource_name = "tile_a1" +cull_mode = 2 +albedo_color = Color(0.3, 0.941667, 1, 1) +roughness = 0.5 diff --git a/assets/textures/bub.png b/assets/textures/bub.png new file mode 100644 index 0000000..99795cb Binary files /dev/null and b/assets/textures/bub.png differ 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 0000000..a65fb12 Binary files /dev/null and b/tiles_armagedon_a1.res differ