308 lines
11 KiB
GDScript
308 lines
11 KiB
GDScript
extends Node2D
|
|
|
|
@onready var tile_layer: TileMapLayer = $main_game_background_tiles
|
|
@onready var camera: Camera2D = $main_game_camera
|
|
|
|
@export var RENDER_RADIUS: int = 20
|
|
@export_range(0.0, 1.0) var producer_spawn_chance: float = 0.015 # 1.5% chance per tile
|
|
|
|
# preload scenes that we can place:
|
|
const EXTRACTOR_SCENE = preload("res://extractor.tscn")
|
|
const BELT_SCENE = preload("res://belt.tscn")
|
|
const ADDER_SCENE = preload("res://adder.tscn")
|
|
const DELETE_SCENE = preload("res://delete.tscn")
|
|
|
|
enum Mode { NONE, PLACE_EXTRACTOR, PLACE_BELT, PLACE_ADDER, DELETE }
|
|
var current_mode = Mode.NONE
|
|
var preview_item: Node2D = null # Holds the visual for the mouse
|
|
|
|
# These should likely be changed to be the same format as above
|
|
@export var producer_scene: PackedScene
|
|
@export var consumer_scene: PackedScene
|
|
|
|
var render_radius_x = RENDER_RADIUS
|
|
var render_radius_y = RENDER_RADIUS
|
|
|
|
# This dictionary tracks what physical entity occupies a grid coordinate.
|
|
# Format: Vector2i(x, y) : { "type": "consumer" } or { "type": "producer", "value": 10 }
|
|
var grid_data: Dictionary = {}
|
|
|
|
# This dictionary keeps track of where the floor tiles have already been visually placed
|
|
var rendered_tiles: Dictionary = {}
|
|
|
|
func _ready() -> void:
|
|
# Explicitly pre-seed the center of the world data structure
|
|
initialize_world_center()
|
|
|
|
func _process(_delta: float) -> void:
|
|
#print(camera.position)
|
|
#print(get_global_mouse_position())
|
|
#print(get_grid_snapped_mouse_position())
|
|
if camera:
|
|
render_radius_x = RENDER_RADIUS / camera.zoom[0]
|
|
render_radius_y = RENDER_RADIUS / camera.zoom[1]
|
|
generate_and_render_around_camera()
|
|
|
|
# Update the preview item's position and snap to the grid
|
|
if preview_item and current_mode != Mode.NONE:
|
|
var grid_pos = get_grid_snapped_mouse_position()
|
|
preview_item.global_position = tile_layer.map_to_local(grid_pos)
|
|
# var scaled_pos = (get_grid_snapped_mouse_position() * 32)
|
|
# scaled_pos[0] += 32
|
|
# scaled_pos[1] += 32
|
|
# preview_item.global_position = scaled_pos
|
|
|
|
func _input(event):
|
|
# Detect left mouse click
|
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
|
match current_mode:
|
|
Mode.PLACE_EXTRACTOR:
|
|
place_item(EXTRACTOR_SCENE)
|
|
Mode.PLACE_BELT:
|
|
place_item(BELT_SCENE)
|
|
Mode.DELETE:
|
|
delete_item(get_grid_snapped_mouse_position())
|
|
|
|
# Right-click cancels the current action
|
|
elif event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
|
|
clear_preview()
|
|
current_mode = Mode.NONE
|
|
|
|
|
|
# Returns the grid position of the mouse
|
|
func get_grid_snapped_mouse_position() -> Vector2:
|
|
return tile_layer.local_to_map(get_global_mouse_position())
|
|
|
|
|
|
# Define the absolute center data rule
|
|
func initialize_world_center() -> void:
|
|
# Loop from -1 to 1 on both X and Y to cover a 3x3 area
|
|
for x in range(-1, 2):
|
|
for y in range(-1, 2):
|
|
var current_coord = Vector2i(x, y)
|
|
|
|
grid_data[current_coord] = [{
|
|
"type": "consumer",
|
|
"is_center": (x == 0 and y == 0) # Only (0,0) is marked as the visual center
|
|
}]
|
|
print("World Core Initialized: 3x3 Consumer registered from (-1,-1) to (1,1)")
|
|
if producer_scene:
|
|
print("Placing consumer at (0, 0)")
|
|
# 1. Create a live instance of the saved scene file
|
|
var consumer = consumer_scene.instantiate()
|
|
# 2. Add it as a child of the world so it exists in the active game tree
|
|
add_child(consumer)
|
|
|
|
# 3. Convert grid coordinates back to exact engine pixel coordinates
|
|
# map_to_local gives us the exact center point of that grid tile
|
|
var pixel_position = tile_layer.map_to_local(Vector2i(0, 0))
|
|
consumer.global_position = pixel_position
|
|
|
|
# Combined loop to manage both world data rules and visual rendering
|
|
func generate_and_render_around_camera() -> void:
|
|
var camera_grid_pos = tile_layer.local_to_map(camera.global_position)
|
|
|
|
for x in range(camera_grid_pos.x - render_radius_x, camera_grid_pos.x + render_radius_x):
|
|
for y in range(camera_grid_pos.y - render_radius_y, camera_grid_pos.y + render_radius_y):
|
|
var tile_coords = Vector2i(x, y)
|
|
|
|
# 1. VISUAL RENDERING (Always draw the background floor)
|
|
if not rendered_tiles.has(tile_coords):
|
|
# Draw your background tile asset (Source ID: 0, Atlas Coords: 0,0)
|
|
tile_layer.set_cell(tile_coords, 0, Vector2i(0, 0))
|
|
rendered_tiles[tile_coords] = true
|
|
|
|
# 2. DATA PROCESSING PLACEHOLDER
|
|
# This is where Phase 2 will plug in to evaluate if a producer should spawn
|
|
process_grid_data_at(tile_coords)
|
|
|
|
|
|
# PHASE 2: Distance Math & Random Generation
|
|
func process_grid_data_at(coords: Vector2i) -> void:
|
|
# If this coordinate is already occupied (like by our 3x3 Consumer), skip it!
|
|
# This ensures that there is a 3x3 space to spawn item in. This is required for the producers
|
|
for x in range(-1, 2):
|
|
for y in range(-1, 2):
|
|
if grid_data.has(coords + Vector2i(x, y)):
|
|
return
|
|
|
|
# Roll the dice to see if a Producer should spawn here
|
|
if randf() < producer_spawn_chance:
|
|
|
|
# Calculate Manhattan Distance from the absolute center (0,0)
|
|
var distance = abs(coords.x) + abs(coords.y)
|
|
|
|
|
|
# Formula: Start at 1, add 1 extra value for every 10 tiles away
|
|
var producer_value = 1 + floor(distance / 10.0)
|
|
|
|
# Register the producer in our global data registry
|
|
# disabled: for x in range(-1, 2):
|
|
# disabled: for y in range(-1, 2):
|
|
# disabled: grid_data[coords + Vector2i(x, y)] = {
|
|
# disabled: "type": "producer",
|
|
# disabled: "value": producer_value
|
|
# disabled: }
|
|
|
|
|
|
grid_data[coords] = [{
|
|
"type": "producer",
|
|
"value": producer_value
|
|
}]
|
|
|
|
if producer_scene:
|
|
print("Placing producer at " + str(coords.x) + ", " + str(coords.y))
|
|
# 1. Create a live instance of the saved scene file
|
|
var new_producer = producer_scene.instantiate()
|
|
var pixel_position = tile_layer.map_to_local(coords)
|
|
new_producer.global_position = pixel_position
|
|
new_producer.setup(int(producer_value))
|
|
|
|
# 2. Add it as a child of the world so it exists in the active game tree
|
|
add_child(new_producer)
|
|
|
|
|
|
# Spawns the preview item following the cursor
|
|
func set_preview_mode(mode_type: Mode, scene_to_preview: PackedScene):
|
|
clear_preview()
|
|
current_mode = mode_type
|
|
|
|
if scene_to_preview:
|
|
preview_item = scene_to_preview.instantiate()
|
|
# Optional: Make the preview slightly transparent
|
|
preview_item.modulate.a = 0.5
|
|
add_child(preview_item)
|
|
|
|
func clear_preview():
|
|
if preview_item:
|
|
preview_item.queue_free()
|
|
preview_item = null
|
|
|
|
# Places an item onto the game board
|
|
func place_item(item: PackedScene):
|
|
var spawn_pos = get_grid_snapped_mouse_position()
|
|
|
|
print("attempting placement at %s, %s" % [spawn_pos[0], spawn_pos[1]])
|
|
# Check if there is something under us
|
|
var object_at_coords = get_object_at_coordinate(spawn_pos)
|
|
print(object_at_coords)
|
|
if object_at_coords != null:
|
|
if item == EXTRACTOR_SCENE && object_at_coords['type'] == 'producer':
|
|
print("You are placing an extractor and it is on top of a producer")
|
|
pass # Do not return if we are placing an extractor and we are on top of a producer
|
|
else:
|
|
print("Failed to place due to %s in the way" % object_at_coords['type'])
|
|
return
|
|
else: # If we are not over something
|
|
if item == EXTRACTOR_SCENE: # And we are placing an extractor
|
|
print("You must place an extractor on top of a producer")
|
|
return # Do not place
|
|
|
|
print("Placing object")
|
|
var new_item = item.instantiate()
|
|
new_item.global_position = tile_layer.map_to_local(spawn_pos)
|
|
new_item.add_to_group("placed_objects")
|
|
add_child(new_item)
|
|
register_object_at_coordinate(
|
|
spawn_pos,
|
|
{
|
|
"type": new_item.name,
|
|
})
|
|
|
|
# Custom setup needed
|
|
if item == EXTRACTOR_SCENE:
|
|
new_item.setup(object_at_coords['value'])
|
|
elif item == BELT_SCENE:
|
|
new_item.setup(spawn_pos, 0)
|
|
else:
|
|
print("No custom setup for %s" % item)
|
|
|
|
func delete_item(target_pos: Vector2i):
|
|
var targets = get_tree().get_nodes_in_group("placed_objects")
|
|
for item in targets:
|
|
print("Trying to match delete at: " + str(tile_layer.local_to_map(item.global_position)[0]) + ", " + str(tile_layer.local_to_map(item.global_position)[1]))
|
|
if tile_layer.local_to_map(item.global_position) == target_pos:
|
|
print("deleting item")
|
|
item.queue_free()
|
|
delete_object_at_coordinate(target_pos)
|
|
break # Prevents deleting multiple items.
|
|
|
|
# Helper function: Check if a coordinate is occupied by a building
|
|
func get_object_at_coordinate(coords: Vector2i):
|
|
if grid_data.has(coords):
|
|
print(grid_data)
|
|
return grid_data[coords][-1] # Return the last item there
|
|
return null # Returns null if it's just an empty background tile
|
|
|
|
# Helper function: Force place an object into the grid data registry
|
|
func register_object_at_coordinate(coords: Vector2i, object_data: Dictionary) -> void:
|
|
if grid_data.has(coords):
|
|
grid_data[coords].append(object_data)
|
|
else:
|
|
grid_data[coords] = [object_data]
|
|
|
|
func delete_object_at_coordinate(coords: Vector2i):
|
|
grid_data[coords].pop_back()
|
|
|
|
|
|
# --- UI Button Signals ---
|
|
func _on_extractor_button_pressed():
|
|
set_preview_mode(Mode.PLACE_EXTRACTOR, EXTRACTOR_SCENE)
|
|
|
|
func _on_belt_button_pressed() -> void:
|
|
set_preview_mode(Mode.PLACE_BELT, BELT_SCENE)
|
|
|
|
func _on_adder_button_pressed() -> void:
|
|
set_preview_mode(Mode.PLACE_ADDER, ADDER_SCENE)
|
|
|
|
func _on_delete_button_pressed() -> void:
|
|
set_preview_mode(Mode.DELETE, DELETE_SCENE)
|
|
|
|
# Run the extractor update
|
|
func _on_extractor_timer_timeout() -> void:
|
|
var targets = get_tree().get_nodes_in_group("placed_objects")
|
|
var production_coords: Array
|
|
for target in targets:
|
|
# Check if we are an extractor:
|
|
if target.scene_file_path == 'res://extractor.tscn':
|
|
position = tile_layer.local_to_map(target.global_position)
|
|
print("Extractor produced a %s at %s, %s" % [target.value(), position[0], position[1]])
|
|
# Make a list of positions where we need to try to produce:
|
|
for x in range(-1, 2):
|
|
for y in range(-1, 2):
|
|
production_coords.append([Vector2(position[0] + x, position[1] + y), target.value()])
|
|
|
|
# Now loop through and see if there are empty conveyer belts in the correct positions
|
|
for target in targets:
|
|
# Check if this is a belt:
|
|
if target.scene_file_path == 'res://belt.tscn' and target.empty:
|
|
position = tile_layer.local_to_map(target.global_position)
|
|
for prod in production_coords:
|
|
if position == prod[0]:
|
|
print("Extractor produced %s onto a belt!" %prod[1])
|
|
target.collect(prod[1])
|
|
|
|
|
|
func _on_belt_timer_timeout() -> void:
|
|
#print('belt tick')
|
|
# Gather the belts:
|
|
var targets = get_tree().get_nodes_in_group("placed_objects")
|
|
var next_belt_coords: Array
|
|
var belts: Array
|
|
for target in targets:
|
|
# Check if this is a belt:
|
|
if target.scene_file_path == 'res://belt.tscn':
|
|
belts.append(target)
|
|
if !target.empty:
|
|
var result = target.move()
|
|
if result != null:
|
|
# Try to move the number to the next belt
|
|
print("value fell off of belt")
|
|
next_belt_coords.append(result)
|
|
for belt in belts:
|
|
for itm in next_belt_coords:
|
|
position = tile_layer.local_to_map(belt.global_position)
|
|
print("(%s, %s), (%s, %s)" %[position[0], position[1], itm[0][0], itm[0][1]])
|
|
if position == itm[0]:
|
|
belt.collect(itm[1])
|