diff --git a/assets/Ships/Spaceship_10.png b/assets/Ships/Spaceship_10.png index 094ef6d..fb1cdc3 100644 Binary files a/assets/Ships/Spaceship_10.png and b/assets/Ships/Spaceship_10.png differ diff --git a/assets/Ships/Spaceship_11.png b/assets/Ships/Spaceship_11.png index 6a34de5..ac8ba54 100644 Binary files a/assets/Ships/Spaceship_11.png and b/assets/Ships/Spaceship_11.png differ diff --git a/assets/Ships/Spaceship_5.png b/assets/Ships/Spaceship_5.png index 358ac5c..2d4b6a8 100644 Binary files a/assets/Ships/Spaceship_5.png and b/assets/Ships/Spaceship_5.png differ diff --git a/assets/Ships/Spaceship_6.png b/assets/Ships/Spaceship_6.png index 69b71c4..c894ddb 100644 Binary files a/assets/Ships/Spaceship_6.png and b/assets/Ships/Spaceship_6.png differ diff --git a/assets/Ships/Spaceship_7.png b/assets/Ships/Spaceship_7.png index 7f352f4..6cc5291 100644 Binary files a/assets/Ships/Spaceship_7.png and b/assets/Ships/Spaceship_7.png differ diff --git a/assets/Ships/Spaceship_8.png b/assets/Ships/Spaceship_8.png index 795fa96..b856da1 100644 Binary files a/assets/Ships/Spaceship_8.png and b/assets/Ships/Spaceship_8.png differ diff --git a/assets/Ships/Spaceship_9.png b/assets/Ships/Spaceship_9.png index c2c3d06..797103f 100644 Binary files a/assets/Ships/Spaceship_9.png and b/assets/Ships/Spaceship_9.png differ diff --git a/project.godot b/project.godot index e60596a..0a888fb 100644 --- a/project.godot +++ b/project.godot @@ -29,6 +29,25 @@ window/size/window_height_override=720 window/stretch/mode="canvas_items" window/stretch/aspect="expand" +[input] + +ship_thrust={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"physical_keycode":87)] +} +ship_brake={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"physical_keycode":83)] +} +ship_turn_left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"physical_keycode":65)] +} +ship_turn_right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"physical_keycode":68)] +} + [editor_plugins] enabled=PackedStringArray("res://addons/godot_mcp_toolkit/plugin.cfg") diff --git a/scenes/game/game.tscn b/scenes/game/game.tscn index 55b18fb..9c92b1f 100644 --- a/scenes/game/game.tscn +++ b/scenes/game/game.tscn @@ -1,13 +1,74 @@ -[gd_scene load_steps=2 format=3] +[gd_scene load_steps=9 format=3] -[ext_resource type="Script" path="res://scripts/game/game.gd" id="1"] +[ext_resource type="Script" path="res://scripts/game/game.gd" id="1_game"] +[ext_resource type="Script" path="res://scripts/game/space_level_background.gd" id="2_background"] +[ext_resource type="PackedScene" path="res://scenes/game/player_ship.tscn" id="3_player"] +[ext_resource type="Script" path="res://scripts/game/flight_hud.gd" id="4_hud"] -[node name="Game" type="Control"] +[sub_resource type="RectangleShape2D" id="TopWall"] +size = Vector2(5200, 40) + +[sub_resource type="RectangleShape2D" id="BottomWall"] +size = Vector2(5200, 40) + +[sub_resource type="RectangleShape2D" id="LeftWall"] +size = Vector2(40, 3600) + +[sub_resource type="RectangleShape2D" id="RightWall"] +size = Vector2(40, 3600) + +[node name="Game" type="Node2D"] +script = ExtResource("1_game") + +[node name="SpaceBackground" type="Node2D" parent="."] +z_index = -20 +script = ExtResource("2_background") + +[node name="WorldBounds" type="StaticBody2D" parent="."] +collision_layer = 1 +collision_mask = 0 + +[node name="TopWall" type="CollisionShape2D" parent="WorldBounds"] +position = Vector2(0, -1780) +shape = SubResource("TopWall") + +[node name="BottomWall" type="CollisionShape2D" parent="WorldBounds"] +position = Vector2(0, 1780) +shape = SubResource("BottomWall") + +[node name="LeftWall" type="CollisionShape2D" parent="WorldBounds"] +position = Vector2(-2580, 0) +shape = SubResource("LeftWall") + +[node name="RightWall" type="CollisionShape2D" parent="WorldBounds"] +position = Vector2(2580, 0) +shape = SubResource("RightWall") + +[node name="PlayerShip" parent="." instance=ExtResource("3_player")] + +[node name="HUD" type="CanvasLayer" parent="."] +script = ExtResource("4_hud") + +[node name="HUDRoot" type="Control" parent="HUD"] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -script = ExtResource("1") +[node name="ReadoutBackground" type="ColorRect" parent="HUD/HUDRoot"] +offset_left = 18.0 +offset_top = 18.0 +offset_right = 292.0 +offset_bottom = 166.0 +color = Color(0.018, 0.045, 0.085, 0.86) + +[node name="Readout" type="Label" parent="HUD/HUDRoot"] +offset_left = 32.0 +offset_top = 28.0 +offset_right = 278.0 +offset_bottom = 156.0 +theme_override_colors/font_color = Color(0.72, 0.88, 0.98, 1) +theme_override_font_sizes/font_size = 17 +text = "W — ciąg S — hamulec\nA / D — płynny obrót\n\nPRĘDKOŚĆ 000 px/s\nPOZYCJA 0, 0" diff --git a/scenes/game/player_ship.tscn b/scenes/game/player_ship.tscn new file mode 100644 index 0000000..348ce18 --- /dev/null +++ b/scenes/game/player_ship.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=3 format=3] + +[ext_resource type="Script" path="res://scripts/game/ship_controller.gd" id="1_controller"] + +[sub_resource type="CircleShape2D" id="ShipCollision"] +radius = 24.0 + +[node name="PlayerShip" type="CharacterBody2D"] +collision_layer = 2 +collision_mask = 1 +script = ExtResource("1_controller") + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture_filter = 1 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("ShipCollision") + +[node name="Camera2D" type="Camera2D" parent="."] +position_smoothing_enabled = true +position_smoothing_speed = 4.0 diff --git a/scripts/game/flight_hud.gd b/scripts/game/flight_hud.gd new file mode 100644 index 0000000..e7a42c3 --- /dev/null +++ b/scripts/game/flight_hud.gd @@ -0,0 +1,9 @@ +extends CanvasLayer + +@onready var readout: Label = $HUDRoot/Readout +@onready var player: CharacterBody2D = get_parent().get_node("PlayerShip") + +func _process(_delta: float) -> void: + var speed := player.velocity.length() + var position := player.global_position + readout.text = "W — ciąg S — hamulec / wsteczny (50%)\nA / D — płynny obrót\n\nPRĘDKOŚĆ %03d px/s\nPOZYCJA %d, %d" % [roundi(speed), roundi(position.x), roundi(position.y)] diff --git a/scripts/game/flight_hud.gd.uid b/scripts/game/flight_hud.gd.uid new file mode 100644 index 0000000..89735ff --- /dev/null +++ b/scripts/game/flight_hud.gd.uid @@ -0,0 +1 @@ +uid://bcfvflaqnmfa5 diff --git a/scripts/game/game.gd b/scripts/game/game.gd index 5a81f78..4792e17 100644 --- a/scripts/game/game.gd +++ b/scripts/game/game.gd @@ -1,25 +1,14 @@ -extends Control +extends Node2D -var ship_sprite: Sprite2D +const WORLD_SIZE := Vector2(5200.0, 3600.0) + +@onready var player: CharacterBody2D = $PlayerShip func _ready() -> void: - if GameState.selected_ship_path.is_empty() or not ResourceLoader.exists(GameState.selected_ship_path): - push_warning("Nie wybrano statku do nowej gry.") - return - - var ship_texture := load(GameState.selected_ship_path) as Texture2D - if ship_texture == null: - push_warning("Nie udało się wczytać wybranego statku.") - return - - ship_sprite = Sprite2D.new() - ship_sprite.name = "PlayerShip" - ship_sprite.texture = ship_texture - add_child(ship_sprite) - _update_game_layout() - get_viewport().size_changed.connect(_update_game_layout) - -func _update_game_layout() -> void: - if ship_sprite: - var viewport_size := get_viewport_rect().size - ship_sprite.position = viewport_size * 0.5 + # Keep the camera limits aligned with the test map if its size is tuned later. + var camera := player.get_node_or_null("Camera2D") as Camera2D + if camera: + camera.limit_left = int(-WORLD_SIZE.x * 0.5) + camera.limit_right = int(WORLD_SIZE.x * 0.5) + camera.limit_top = int(-WORLD_SIZE.y * 0.5) + camera.limit_bottom = int(WORLD_SIZE.y * 0.5) diff --git a/scripts/game/ship_controller.gd b/scripts/game/ship_controller.gd new file mode 100644 index 0000000..ec68ce1 --- /dev/null +++ b/scripts/game/ship_controller.gd @@ -0,0 +1,71 @@ +extends CharacterBody2D + +const DEFAULT_SHIP_TEXTURE := "res://assets/Ships/Spaceship_0.png" + +@export var max_speed := 420.0 +@export var reverse_max_speed := 210.0 +@export var thrust_acceleration := 260.0 +@export var brake_acceleration := 540.0 +@export var space_drag := 24.0 +@export var max_turn_speed := 2.8 +@export var turn_acceleration := 7.0 +@export var turn_damping := 4.0 + +var angular_velocity := 0.0 +var _sprite: Sprite2D + +func _ready() -> void: + _ensure_input_action(&"ship_thrust", KEY_W) + _ensure_input_action(&"ship_brake", KEY_S) + _ensure_input_action(&"ship_turn_left", KEY_A) + _ensure_input_action(&"ship_turn_right", KEY_D) + + _sprite = $Sprite2D + var texture_path := GameState.selected_ship_path + if texture_path.is_empty() or not ResourceLoader.exists(texture_path): + texture_path = DEFAULT_SHIP_TEXTURE + + var ship_texture := load(texture_path) as Texture2D + if ship_texture: + _sprite.texture = ship_texture + var largest_dimension := maxf(ship_texture.get_width(), ship_texture.get_height()) + _sprite.scale = Vector2.ONE * (92.0 / largest_dimension) + +func _ensure_input_action(action_name: StringName, physical_key: Key) -> void: + if not InputMap.has_action(action_name): + InputMap.add_action(action_name, 0.5) + var event := InputEventKey.new() + event.physical_keycode = physical_key + if not InputMap.action_has_event(action_name, event): + InputMap.action_add_event(action_name, event) + +func _physics_process(delta: float) -> void: + var turn_input := Input.get_axis("ship_turn_left", "ship_turn_right") + if not is_zero_approx(turn_input): + angular_velocity = move_toward( + angular_velocity, + turn_input * max_turn_speed, + turn_acceleration * delta + ) + else: + angular_velocity = move_toward(angular_velocity, 0.0, turn_damping * delta) + rotation += angular_velocity * delta + + var forward := Vector2.UP.rotated(rotation) + if Input.is_action_pressed("ship_brake"): + if velocity.length_squared() > 1.0: + velocity = velocity.move_toward(Vector2.ZERO, brake_acceleration * delta) + else: + velocity -= forward * thrust_acceleration * delta + var reverse_speed := velocity.dot(forward) + if reverse_speed < -reverse_max_speed: + velocity += forward * (-reverse_max_speed - reverse_speed) + elif Input.is_action_pressed("ship_thrust"): + velocity += forward * thrust_acceleration * delta + var forward_speed := velocity.dot(forward) + if forward_speed > max_speed: + velocity += forward * (max_speed - forward_speed) + else: + velocity = velocity.move_toward(Vector2.ZERO, space_drag * delta) + + move_and_slide() diff --git a/scripts/game/ship_controller.gd.uid b/scripts/game/ship_controller.gd.uid new file mode 100644 index 0000000..3f81b89 --- /dev/null +++ b/scripts/game/ship_controller.gd.uid @@ -0,0 +1 @@ +uid://b1sdo5uwspn5d diff --git a/scripts/game/space_level_background.gd b/scripts/game/space_level_background.gd new file mode 100644 index 0000000..186cd94 --- /dev/null +++ b/scripts/game/space_level_background.gd @@ -0,0 +1,88 @@ +extends Node2D + +const WORLD_SIZE := Vector2(5200.0, 3600.0) +const GRID_STEP := 320 + +func _draw() -> void: + var half_size := WORLD_SIZE * 0.5 + draw_rect(Rect2(-half_size, WORLD_SIZE), Color("050b18"), true) + _draw_grid(half_size) + _draw_stars(half_size) + _draw_landmarks() + _draw_world_border(half_size) + +func _draw_grid(half_size: Vector2) -> void: + for x in range(-2600, 2601, GRID_STEP): + var major := x % (GRID_STEP * 4) == 0 + var color := Color(0.16, 0.28, 0.42, 0.2 if major else 0.075) + draw_line(Vector2(x, -half_size.y), Vector2(x, half_size.y), color, 1.0) + for y in range(-1800, 1801, GRID_STEP): + var major := y % (GRID_STEP * 4) == 0 + var color := Color(0.16, 0.28, 0.42, 0.2 if major else 0.075) + draw_line(Vector2(-half_size.x, y), Vector2(half_size.x, y), color, 1.0) + +func _draw_stars(half_size: Vector2) -> void: + var rng := RandomNumberGenerator.new() + rng.seed = 640_118 + for index in 420: + var point := Vector2( + rng.randf_range(-half_size.x + 35.0, half_size.x - 35.0), + rng.randf_range(-half_size.y + 35.0, half_size.y - 35.0) + ) + var radius := rng.randf_range(0.7, 2.0) + var alpha := rng.randf_range(0.2, 0.72) + var color := Color(0.58, 0.76, 1.0, alpha) + if index % 9 == 0: + color = Color(0.35, 0.88, 0.94, alpha) + draw_circle(point, radius, color) + +func _draw_landmarks() -> void: + # Safe starting area around the spawn point. + draw_circle(Vector2.ZERO, 300.0, Color(0.08, 0.65, 0.72, 0.035)) + draw_arc(Vector2.ZERO, 300.0, 0.0, TAU, 96, Color(0.16, 0.78, 0.84, 0.32), 2.0) + _draw_label(Vector2(28, -316), "STREFA STARTOWA", Color(0.38, 0.81, 0.87, 0.78)) + + _draw_planet(Vector2(-1420, -760), 150.0, Color("e9904e"), "HELION") + _draw_planet(Vector2(1420, -940), 112.0, Color("397fa9"), "NEREIDA") + _draw_planet(Vector2(1110, 1170), 86.0, Color("8d6cbb"), "VESPER") + _draw_planet(Vector2(-1580, 1120), 72.0, Color("72a58b"), "TUBYLCY") + _draw_asteroid_field(Vector2(420, 160), 400.0, 34, 9_183) + _draw_asteroid_field(Vector2(-930, 460), 270.0, 22, 3_277) + _draw_navigation_beacon(Vector2(520, -190)) + +func _draw_navigation_beacon(center: Vector2) -> void: + draw_circle(center, 58.0, Color(0.1, 0.72, 0.86, 0.07)) + draw_arc(center, 44.0, 0.0, TAU, 64, Color(0.25, 0.82, 0.9, 0.75), 2.0) + draw_arc(center, 31.0, 0.0, TAU, 64, Color(0.25, 0.82, 0.9, 0.4), 1.0) + draw_line(center + Vector2(-54, 0), center + Vector2(54, 0), Color(0.25, 0.82, 0.9, 0.6), 1.0) + draw_line(center + Vector2(0, -54), center + Vector2(0, 54), Color(0.25, 0.82, 0.9, 0.6), 1.0) + draw_circle(center, 12.0, Color(0.35, 0.92, 1.0, 0.9)) + _draw_label(center + Vector2(-88, 78), "BOJA NAWIGACYJNA", Color(0.45, 0.83, 0.9, 0.82)) + +func _draw_planet(center: Vector2, radius: float, color: Color, label: String) -> void: + draw_circle(center, radius * 1.55, Color(color.r, color.g, color.b, 0.055)) + draw_arc(center, radius * 1.42, 0.0, TAU, 96, Color(color.r, color.g, color.b, 0.2), 2.0) + draw_circle(center, radius, color.darkened(0.48)) + draw_circle(center + Vector2(-radius * 0.22, -radius * 0.24), radius * 0.68, color.darkened(0.15)) + draw_arc(center, radius * 1.26, deg_to_rad(-18), deg_to_rad(205), 64, Color(0.64, 0.79, 0.91, 0.55), 5.0) + _draw_label(center + Vector2(-radius, radius + 34.0), label, Color(0.77, 0.85, 0.95, 0.76)) + +func _draw_asteroid_field(center: Vector2, spread: float, count: int, seed_value: int) -> void: + var rng := RandomNumberGenerator.new() + rng.seed = seed_value + for index in count: + var angle := rng.randf_range(0.0, TAU) + var distance := sqrt(rng.randf()) * spread + var point := center + Vector2.RIGHT.rotated(angle) * distance + var size := rng.randf_range(5.0, 15.0) + var color := Color(0.42, 0.53, 0.65, rng.randf_range(0.35, 0.75)) + draw_circle(point, size, color) + draw_circle(point + Vector2(-size * 0.25, -size * 0.25), size * 0.55, color.lightened(0.18)) + +func _draw_world_border(half_size: Vector2) -> void: + var bounds := Rect2(-half_size, WORLD_SIZE) + draw_rect(bounds, Color(0.12, 0.52, 0.68, 0.48), false, 3.0) + _draw_label(Vector2(-half_size.x + 22.0, -half_size.y + 38.0), "SEKTOR TESTOWY 01 / 5200 × 3600", Color(0.4, 0.7, 0.8, 0.7)) + +func _draw_label(position: Vector2, text: String, color: Color) -> void: + draw_string(ThemeDB.fallback_font, position, text, HORIZONTAL_ALIGNMENT_LEFT, -1.0, 18, color) diff --git a/scripts/game/space_level_background.gd.uid b/scripts/game/space_level_background.gd.uid new file mode 100644 index 0000000..02731fd --- /dev/null +++ b/scripts/game/space_level_background.gd.uid @@ -0,0 +1 @@ +uid://b20w2c0ucytmg