diff --git a/src/addons/touchscreenbuttoncontrol/button_style.tres b/src/addons/touchscreenbuttoncontrol/button_style.tres index 52a9a3f..c5738ac 100644 --- a/src/addons/touchscreenbuttoncontrol/button_style.tres +++ b/src/addons/touchscreenbuttoncontrol/button_style.tres @@ -7,7 +7,7 @@ border_width_top = 2 border_width_right = 2 border_width_bottom = 2 border_color = Color(0.8, 0.8, 0.8, 0.9) -corner_radius_top_left = 40 -corner_radius_top_right = 40 -corner_radius_bottom_right = 40 -corner_radius_bottom_left = 40 +corner_radius_top_left = 96 +corner_radius_top_right = 96 +corner_radius_bottom_right = 96 +corner_radius_bottom_left = 96 diff --git a/src/project.godot b/src/project.godot index 1c7e1b6..f59de57 100644 --- a/src/project.godot +++ b/src/project.godot @@ -30,9 +30,8 @@ LogManager="*res://scripts/log_manager.gd" window/size/viewport_width=1280 window/size/viewport_height=720 -window/stretch/mode="canvas_items" +window/stretch/mode="viewport" window/stretch/aspect="expand" -window/stretch/scale_mode="integer" [editor_plugins] diff --git a/src/scenes/game_world.tscn b/src/scenes/game_world.tscn index e0233c6..b7cf688 100644 --- a/src/scenes/game_world.tscn +++ b/src/scenes/game_world.tscn @@ -20,7 +20,7 @@ zoom = Vector2(3, 3) [node name="Environment" type="Node2D" parent="." unique_id=1877699223] [node name="DungeonLayer0" type="TileMapLayer" parent="Environment" unique_id=1234567891] -z_index = -1 +z_index = -2 tile_set = ExtResource("9") [node name="TileMapLayerAbove" type="TileMapLayer" parent="Environment" unique_id=1234567892] diff --git a/src/scenes/ingame_hud.tscn b/src/scenes/ingame_hud.tscn index ad11a22..ab9d89b 100644 --- a/src/scenes/ingame_hud.tscn +++ b/src/scenes/ingame_hud.tscn @@ -141,7 +141,6 @@ horizontal_alignment = 1 [node name="LabelRoomCode" type="Label" parent="UpperRight/HBoxContainer/VBoxContainerHost" unique_id=1807484689] layout_mode = 2 theme = SubResource("Theme_standard_font") -text = "" horizontal_alignment = 1 [node name="VBoxContainerBoss" type="VBoxContainer" parent="UpperRight/HBoxContainer" unique_id=1933444957] @@ -159,11 +158,9 @@ layout_mode = 2 texture_progress = ExtResource("4_hearts_filled") [node name="CenterTop" type="MarginContainer" parent="." unique_id=22752256] -anchors_preset = 7 +anchors_preset = 5 anchor_left = 0.5 -anchor_top = 0.0 anchor_right = 0.5 -anchor_bottom = 0.0 offset_left = -150.0 offset_top = 8.0 offset_right = 150.0 @@ -171,13 +168,13 @@ offset_bottom = 28.0 grow_horizontal = 2 theme = SubResource("Theme_standard_font") -[node name="LabelDisconnected" type="Label" parent="CenterTop" unique_id=869912310] +[node name="LabelDisconnected" type="Label" parent="CenterTop" unique_id=1260056884] +visible = false layout_mode = 2 theme = SubResource("Theme_standard_font") text = "Disconnected - Reconnecting..." horizontal_alignment = 1 vertical_alignment = 1 -visible = false [node name="MobileInput" type="Control" parent="." unique_id=1373461519] layout_mode = 3 @@ -215,9 +212,9 @@ anchor_left = 1.0 anchor_top = 0.5 anchor_right = 1.0 anchor_bottom = 0.5 -offset_left = -40.0 -offset_top = -20.0 -offset_bottom = 20.0 +offset_left = -320.0 +offset_top = -160.0 +offset_bottom = 160.0 grow_horizontal = 0 grow_vertical = 2 @@ -228,13 +225,12 @@ anchor_left = 1.0 anchor_top = 0.5 anchor_right = 1.0 anchor_bottom = 0.5 -offset_left = -205.765 -offset_top = -94.475 -offset_right = -5.7649994 -offset_bottom = 25.525024 +offset_left = -320.0 +offset_top = -160.0 +offset_bottom = 160.0 grow_horizontal = 0 grow_vertical = 2 -theme_override_constants/margin_right = 42 +theme_override_constants/margin_right = 0 [node name="B_ButtonContainer" type="Control" parent="MobileInput/Control/MarginContainer" unique_id=297390115] layout_mode = 2 @@ -244,9 +240,9 @@ layout_mode = 1 anchors_preset = 4 anchor_top = 0.5 anchor_bottom = 0.5 -offset_top = -32.0 -offset_right = 64.0 -offset_bottom = 32.0 +offset_top = -64.0 +offset_right = 128.0 +offset_bottom = 64.0 grow_vertical = 2 theme_override_styles/panel = ExtResource("8_yeruy") @@ -288,9 +284,9 @@ anchor_left = 1.0 anchor_top = 0.5 anchor_right = 1.0 anchor_bottom = 0.5 -offset_left = -64.0 -offset_top = -32.0 -offset_bottom = 32.0 +offset_left = -128.0 +offset_top = -64.0 +offset_bottom = 64.0 grow_horizontal = 0 grow_vertical = 2 theme_override_styles/panel = ExtResource("8_yeruy") @@ -323,4 +319,46 @@ touchscreen_only = true input_action = "grab" metadata/_custom_type_script = "uid://bh5a3ydiu51eo" +[node name="X_ButtonContainer" type="Control" parent="MobileInput/Control/MarginContainer" unique_id=1591990397] +layout_mode = 2 + +[node name="X_Circle" type="Panel" parent="MobileInput/Control/MarginContainer/X_ButtonContainer" unique_id=325379818] +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -64.0 +offset_right = 64.0 +offset_bottom = 128.0 +grow_horizontal = 2 +theme_override_styles/panel = ExtResource("8_yeruy") + +[node name="X_Label" type="Label" parent="MobileInput/Control/MarginContainer/X_ButtonContainer/X_Circle" unique_id=1443974385] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +text = "X" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TouchScreenButtonControl" type="TextureButton" parent="MobileInput/Control/MarginContainer/X_ButtonContainer/X_Circle" unique_id=1520840278] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +focus_mode = 0 +mouse_filter = 2 +action_mode = 0 +ignore_texture_size = true +stretch_mode = 5 +script = ExtResource("8_cu5yl") +touchscreen_only = true +input_action = "attack" +metadata/_custom_type_script = "uid://bh5a3ydiu51eo" + [connection signal="analogic_changed" from="MobileInput/VirtualJoystick" to="MobileInput" method="_on_virtual_joystick_analogic_changed"] diff --git a/src/scenes/player.tscn b/src/scenes/player.tscn index 9e0bde1..2975b29 100644 --- a/src/scenes/player.tscn +++ b/src/scenes/player.tscn @@ -62,8 +62,9 @@ fill = 1 fill_from = Vector2(0.51304346, 0.46086955) fill_to = Vector2(0, 0) -[sub_resource type="CircleShape2D" id="CircleShape2D_1"] -radius = 5.0 +[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_pf23h"] +radius = 3.0 +height = 12.0 [sub_resource type="CircleShape2D" id="CircleShape2D_2"] radius = 8.0 @@ -120,8 +121,7 @@ collision_mask = 16384 shape = SubResource("CircleShape2D_pf23h") [node name="Shadow" type="Sprite2D" parent="." unique_id=937683521] -z_index = -1 -position = Vector2(0, 7) +position = Vector2(0, 8) texture = SubResource("GradientTexture2D_jej6c") script = ExtResource("3") @@ -187,7 +187,8 @@ vframes = 8 [node name="CollisionShape2D" type="CollisionShape2D" parent="." unique_id=989315141] position = Vector2(0, 4) -shape = SubResource("CircleShape2D_1") +rotation = -1.5707964 +shape = SubResource("CapsuleShape2D_pf23h") [node name="GrabArea" type="Area2D" parent="." unique_id=518653365] position = Vector2(0, 4) diff --git a/src/scripts/create_shadow_sprite.gd b/src/scripts/create_shadow_sprite.gd index 2ff0369..e4a6116 100644 --- a/src/scripts/create_shadow_sprite.gd +++ b/src/scripts/create_shadow_sprite.gd @@ -3,6 +3,7 @@ extends Sprite2D # Creates a simple shadow circle sprite at runtime func _ready(): + ' var size = 48 # Shadow size var image = Image.create(size, size, false, Image.FORMAT_RGBA8) @@ -20,5 +21,4 @@ func _ready(): # Create texture from image texture = ImageTexture.create_from_image(image) - centered = true - + centered = true' diff --git a/src/scripts/game_world.gd b/src/scripts/game_world.gd index dd486b4..29b4c07 100644 --- a/src/scripts/game_world.gd +++ b/src/scripts/game_world.gd @@ -7,6 +7,8 @@ extends Node2D @onready var network_manager = $"/root/NetworkManager" var local_players = [] +const BASE_CAMERA_ZOOM: float = 4.0 +const REFERENCE_ASPECT: float = 16.0 / 9.0 # Dungeon generation var dungeon_data: Dictionary = {} @@ -97,6 +99,11 @@ func _send_gameworld_ready(): if not is_inside_tree(): return if multiplayer.has_multiplayer_peer() and not multiplayer.is_server(): + # Ensure host peer (1) is known before sending RPC to avoid errors on web + if 1 not in multiplayer.get_peers(): + # Retry shortly; host peer may not be registered yet + get_tree().create_timer(0.2).timeout.connect(func(): _send_gameworld_ready()) + return var peer_id = multiplayer.get_unique_id() _notify_gameworld_ready.rpc_id(1, peer_id) @@ -917,6 +924,16 @@ func _update_camera(): # Smooth camera movement camera.position = camera.position.lerp(center, 0.1) + # Base zoom with aspect ratio adjustment (show more on wider screens) + var viewport_size = get_viewport().get_visible_rect().size + var aspect = viewport_size.x / max(1.0, viewport_size.y) + var aspect_factor = 1.0 + if aspect > REFERENCE_ASPECT: + # Wider than 16:9 -> zoom out to show more + aspect_factor = REFERENCE_ASPECT / aspect + + var target_zoom = BASE_CAMERA_ZOOM * aspect_factor + # Adjust zoom based on player spread (for split-screen effect) if local_players.size() > 1: var max_distance = 0.0 @@ -925,8 +942,10 @@ func _update_camera(): max_distance = max(max_distance, distance) # Adjust zoom to fit all players - var target_zoom = clamp(800.0 / (max_distance + 400.0), 0.5, 1.5) - camera.zoom = camera.zoom.lerp(Vector2.ONE * target_zoom, 0.05) + var spread_zoom = clamp(800.0 / (max_distance + 400.0), 0.5, 1.5) + target_zoom *= spread_zoom + + camera.zoom = camera.zoom.lerp(Vector2.ONE * target_zoom, 0.05) func _generate_dungeon(): print("GameWorld: _generate_dungeon() called - is_server: ", multiplayer.is_server(), ", has_peer: ", multiplayer.has_multiplayer_peer()) diff --git a/src/scripts/ingame_hud.gd b/src/scripts/ingame_hud.gd index 5338337..6a7a7ac 100644 --- a/src/scripts/ingame_hud.gd +++ b/src/scripts/ingame_hud.gd @@ -24,6 +24,7 @@ var level_start_time: float = 0.0 var player_search_attempts: int = 0 var max_player_search_attempts: int = 100 # Limit retries to prevent infinite recursion var timer_running: bool = true # Flag to stop/start timer +const HUD_BASE_SIZE: Vector2 = Vector2(1280, 720) func _ready(): print("IngameHUD: _ready() called") @@ -80,6 +81,10 @@ func _ready(): # Start level timer level_start_time = Time.get_ticks_msec() / 1000.0 + # Keep HUD text crisp with integer scaling + _update_hud_scale() + get_viewport().size_changed.connect(_update_hud_scale) + # Find local player (with retry limit) player_search_attempts = 0 _find_local_player() @@ -184,6 +189,14 @@ func _process(_delta): # Update boss health (if boss exists) _update_boss_health() +func _update_hud_scale(): + # Scale HUD to an integer factor to keep pixel text crisp + var viewport_size = get_viewport().get_visible_rect().size + var scale_factor = int(floor(min(viewport_size.x / HUD_BASE_SIZE.x, viewport_size.y / HUD_BASE_SIZE.y))) + if scale_factor < 1: + scale_factor = 1 + scale = Vector2.ONE * scale_factor + func _update_player_health(): if not local_player or not texture_progress_bar_hp: return diff --git a/src/scripts/interactable_object.gd b/src/scripts/interactable_object.gd index 5472306..1b7e981 100644 --- a/src/scripts/interactable_object.gd +++ b/src/scripts/interactable_object.gd @@ -162,6 +162,19 @@ func _handle_air_collision(): var collision = get_slide_collision(i) var collider = collision.get_collider() + # Pot special case: break on wall collision + if object_type == "Pot" and _is_wall_collider(collider): + # Only process on server to prevent duplicates + if not multiplayer.is_server(): + continue + if is_destroyable: + if multiplayer.has_multiplayer_peer(): + var game_world = get_tree().get_first_node_in_group("game_world") + if game_world and game_world.has_method("_rpc_to_ready_peers"): + game_world._rpc_to_ready_peers("_sync_object_break", [name]) + _break_into_pieces() + return + # Hit an enemy! Damage them if collider.is_in_group("enemy"): # Only process collision on server to prevent duplicates @@ -325,6 +338,17 @@ func _break_into_pieces(silent: bool = false): # Remove self queue_free() +func _is_wall_collider(collider) -> bool: + if not collider: + return false + # TileMapLayer collisions + if collider is TileMapLayer: + return true + # Any CollisionObject2D with wall layer (7) enabled + if collider is CollisionObject2D and collider.get_collision_layer_value(7): + return true + return false + func can_be_grabbed() -> bool: return is_grabbable and not is_being_held