diff --git a/project.godot b/project.godot index 8b29468..1cc327f 100644 --- a/project.godot +++ b/project.godot @@ -28,6 +28,7 @@ enabled=PackedStringArray() player="" "delivery spot"="valid for spawning in contracts" +"Telescope Label"="This label will be visible in the telescope" [gui] diff --git a/scenes/sign.tscn b/scenes/sign.tscn index f5f66eb..c6a500e 100644 --- a/scenes/sign.tscn +++ b/scenes/sign.tscn @@ -15,7 +15,7 @@ script = ExtResource("1_rtygf") scale = Vector2(0.4, 0.4) texture = ExtResource("1_6ooja") -[node name="Dialogue" parent="." instance=ExtResource("2_yp6hc")] +[node name="Dialogue" parent="." groups=["Telescope Label"] instance=ExtResource("2_yp6hc")] offset_top = -185.445 offset_bottom = -145.445 diff --git a/scenes/telescope.tscn b/scenes/telescope.tscn new file mode 100644 index 0000000..c91dc8c --- /dev/null +++ b/scenes/telescope.tscn @@ -0,0 +1,35 @@ +[gd_scene load_steps=7 format=3 uid="uid://8t14gs1yhyox"] + +[ext_resource type="Texture2D" uid="uid://ddjktr536c58t" path="res://sprites/telescope.png" id="1_k0xkm"] +[ext_resource type="Script" path="res://scripts/telescope.gd" id="1_xa4e0"] +[ext_resource type="Script" path="res://scripts/MapCamera2D.gd" id="3_87vjt"] +[ext_resource type="PackedScene" uid="uid://dpwqurhly8osd" path="res://scenes/interactible_manager.tscn" id="3_los3i"] +[ext_resource type="PackedScene" uid="uid://sk2uc8hcxhcj" path="res://scenes/physical_dialogue.tscn" id="4_u4gnh"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_gpdtu"] +radius = 123.26 + +[node name="Telescope" type="Area2D"] +script = ExtResource("1_xa4e0") + +[node name="Sprite2D" type="Sprite2D" parent="."] +scale = Vector2(0.5, 0.5) +texture = ExtResource("1_k0xkm") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CircleShape2D_gpdtu") + +[node name="Camera2D" type="Camera2D" parent="."] +script = ExtResource("3_87vjt") +zoom_max = 1.0 + +[node name="InteractibleManager" parent="." node_paths=PackedStringArray("ItemShown") instance=ExtResource("3_los3i")] +ItemShown = NodePath("../Dialogue") + +[node name="Dialogue" parent="." instance=ExtResource("4_u4gnh")] +offset_top = -195.08 +offset_bottom = -155.08 +DefaultText = "Telescope to see around the map" + +[connection signal="body_entered" from="." to="InteractibleManager" method="_on_body_entered"] +[connection signal="body_exited" from="." to="InteractibleManager" method="_on_body_exited"] diff --git a/scripts/MapCamera2D.gd b/scripts/MapCamera2D.gd new file mode 100644 index 0000000..de6c8cf --- /dev/null +++ b/scripts/MapCamera2D.gd @@ -0,0 +1,252 @@ +class_name MapCamera2D +extends Camera2D +## A node that adds mouse, keyboard and gesture zooming, panning and dragging to [Camera2D]. + +## Zoom speed: multiplies [member Camera2D.zoom] each mouse wheel scroll (set to 1 to disable zooming). +@export_range(0.1, 10) var zoom_factor := 1.25 +## Minimum [member Camera2D.zoom]. +@export_range(0.01, 100) var zoom_min := 0.1 +## Maximum [member Camera2D.zoom]. +@export_range(0.01, 100) var zoom_max := 10.0 +## If [code]true[/code], [member MapCamera2D.zoom_min] is effectively increased (up to [member MapCamera2D.zoom_max]) to stay within limits. +@export var zoom_limited := true +## If [code]true[/code], mouse zooming is done relative to the cursor (instead of to the center of the screen). +@export var zoom_relative := true +## If [code]true[/code], zooming can also be done with the plus and minus keys. +@export var zoom_keyboard := true + +## Pan speed: adds to [member Camera2D.offset] while the cursor is near the viewport's edges (set to 0 to disable panning). +@export_range(0, 10000) var pan_speed := 250.0 +## Maximum number of pixels away from the viewport's edges for the cursor to be considered near. +@export_range(0, 1000) var pan_margin := 25.0 +## If [code]true[/code], panning can also be done with the arrow keys (and space bar for centering). +@export var pan_keyboard := true + +## If [code]true[/code], the map can be dragged while holding the left mouse button. +@export var drag := true +## Slide after dragging: multiplies the final drag movement each second (set to 0 to stop immediately). +@export_range(0, 1) var drag_inertia := 0.1 + +var _tween_offset +var _tween_zoom +var _pan_direction: set = _set_pan_direction +var _pan_direction_mouse = Vector2.ZERO +var _dragging = false +var _drag_movement = Vector2() + +@onready var _target_zoom = zoom + +func _ready(): + _pan_direction = Vector2.ZERO + + change_zoom() + + get_viewport().size_changed.connect(change_zoom) + +func _process(delta): + if _drag_movement == Vector2.ZERO: + clamp_offset(_pan_direction * pan_speed * delta / zoom) + else: + _drag_movement *= drag_inertia ** delta + + clamp_offset(-_drag_movement / zoom) + + if _drag_movement.length_squared() < 0.01: + set_process(false) + set_physics_process(false) + +func _physics_process(delta): + _process(delta) + +func _unhandled_input(event): + if event is InputEventMagnifyGesture: + change_zoom(1 + ((zoom_factor if zoom_factor > 1 else 1 / zoom_factor) - 1) * (event.factor - 1) * 2.5) + elif event is InputEventPanGesture: + change_zoom(1 + (1 / zoom_factor - 1) * event.delta.y / 7.5) + elif event is InputEventMouseButton: + if event.pressed: + match event.button_index: + MOUSE_BUTTON_WHEEL_UP: + change_zoom(zoom_factor) + MOUSE_BUTTON_WHEEL_DOWN: + change_zoom(1 / zoom_factor) + MOUSE_BUTTON_LEFT: + if drag: + Input.set_default_cursor_shape(Input.CURSOR_DRAG) # delete to disable drag cursor + + _dragging = true + _drag_movement = Vector2() + + set_process(false) + set_physics_process(false) + elif event.button_index == MOUSE_BUTTON_LEFT and _dragging: + Input.set_default_cursor_shape(Input.CURSOR_ARROW) + + _dragging = false + + if _drag_movement != Vector2.ZERO || _pan_direction != Vector2.ZERO: + set_process(process_callback == CAMERA2D_PROCESS_IDLE) + set_physics_process(process_callback == CAMERA2D_PROCESS_PHYSICS) + elif event is InputEventMouseMotion: + if _pan_direction_mouse != Vector2.ZERO: + _pan_direction -= _pan_direction_mouse + + _pan_direction_mouse = Vector2() + + if _dragging: + if _tween_offset != null: + _tween_offset.kill() + + clamp_offset(-event.relative / zoom) + + _drag_movement = event.relative + elif pan_margin > 0: + var camera_size = get_viewport_rect().size + + if event.position.x < pan_margin: + _pan_direction_mouse.x -= 1 + + if event.position.x >= camera_size.x - pan_margin: + _pan_direction_mouse.x += 1 + + if event.position.y < pan_margin: + _pan_direction_mouse.y -= 1 + + if event.position.y >= camera_size.y - pan_margin: + _pan_direction_mouse.y += 1 + + if _pan_direction_mouse != Vector2.ZERO: + _pan_direction += _pan_direction_mouse + elif event is InputEventKey: + if zoom_keyboard && event.pressed: + match event.keycode: + KEY_MINUS: + change_zoom(zoom_factor if zoom_factor < 1 else 1 / zoom_factor, false) + KEY_EQUAL: + change_zoom(zoom_factor if zoom_factor > 1 else 1 / zoom_factor, false) + + if pan_keyboard && !event.echo: + match event.keycode: + KEY_LEFT: + _pan_direction -= Vector2(1 if event.pressed else -1, 0) + KEY_RIGHT: + _pan_direction += Vector2(1 if event.pressed else -1, 0) + KEY_UP: + _pan_direction -= Vector2(0, 1 if event.pressed else -1) + KEY_DOWN: + _pan_direction += Vector2(0, 1 if event.pressed else -1) + KEY_SPACE: # delete to disable keyboard centering + if event.pressed: + if _tween_offset != null: + _tween_offset.kill() + + offset = Vector2.ZERO + +func _set_pan_direction(value): + _pan_direction = value + + if _pan_direction == Vector2.ZERO || _dragging: + set_process(false) + set_physics_process(false) + elif pan_speed > 0: + set_process(process_callback == CAMERA2D_PROCESS_IDLE) + set_physics_process(process_callback == CAMERA2D_PROCESS_PHYSICS) + + _drag_movement = Vector2() + + if _tween_offset != null: + _tween_offset.kill() + +## After changing the node's global position, set [code]offset = offset[/code] then call this to stay within limits. +func clamp_offset(relative := Vector2()): + var camera_size = get_viewport_rect().size / zoom + var camera_rect = Rect2(get_screen_center_position() + relative - camera_size / 2, camera_size) + + if camera_rect.position.x < limit_left: + _drag_movement.x = 0 + relative.x += limit_left - camera_rect.position.x + camera_rect.end.x += limit_left - camera_rect.position.x + + if camera_rect.end.x > limit_right: + _drag_movement.x = 0 + relative.x -= camera_rect.end.x - limit_right + + if camera_rect.end.y > limit_bottom: + _drag_movement.y = 0 + relative.y -= camera_rect.end.y - limit_bottom + camera_rect.position.y -= camera_rect.end.y - limit_bottom + + if camera_rect.position.y < limit_top: + _drag_movement.y = 0 + relative.y += limit_top - camera_rect.position.y + + if relative != Vector2.ZERO: + offset += relative + +## After changing the node's limits, call this without arguments to stay within limits. +func change_zoom(factor = null, with_cursor = true): + var limited_zoom_min = zoom_min + + if zoom_limited: + var min_zoom_within_limits = get_viewport_rect().size / Vector2(limit_right - limit_left, limit_bottom - limit_top) + + limited_zoom_min = min(max(zoom_min, min_zoom_within_limits.x, min_zoom_within_limits.y), zoom_max) + + if factor != null: + if factor < 1: + if _target_zoom.x < limited_zoom_min || is_equal_approx(_target_zoom.x, limited_zoom_min): + return + + if _target_zoom.y < limited_zoom_min || is_equal_approx(_target_zoom.y, limited_zoom_min): + return + elif factor > 1: + if _target_zoom.x > zoom_max || is_equal_approx(_target_zoom.x, zoom_max): + return + + if _target_zoom.y > zoom_max || is_equal_approx(_target_zoom.y, zoom_max): + return + else: + return + + _target_zoom *= factor + + var clamped_zoom = _target_zoom + + clamped_zoom *= max(1, limited_zoom_min / _target_zoom.x, limited_zoom_min / _target_zoom.y) + clamped_zoom *= min(1, zoom_max / _target_zoom.x, zoom_max / _target_zoom.y) + + if factor == null: + _set_zoom_level(clamped_zoom) + + _target_zoom = zoom + elif position_smoothing_enabled && position_smoothing_speed > 0: + if zoom_relative && with_cursor && !is_processing() && !is_physics_processing(): + var relative_position = get_global_mouse_position() - global_position - offset + var relative = relative_position - relative_position * zoom / clamped_zoom + + if _tween_offset != null: + _tween_offset.kill() + + _tween_offset = create_tween().set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT).set_process_mode(process_callback as Tween.TweenProcessMode) + _tween_offset.tween_property(self, 'offset', offset + relative, 2.5 / position_smoothing_speed) + + if _tween_zoom != null: + _tween_zoom.kill() + + _tween_zoom = create_tween().set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT).set_process_mode(process_callback as Tween.TweenProcessMode) + _tween_zoom.tween_method(func(value): _set_zoom_level(Vector2.ONE / value), Vector2.ONE / zoom, Vector2.ONE / clamped_zoom, 2.5 / position_smoothing_speed) + else: + if zoom_relative && with_cursor: + var relative_position = get_global_mouse_position() - global_position - offset + var relative = relative_position - relative_position * zoom / clamped_zoom + + zoom = clamped_zoom + + clamp_offset(relative) + else: + _set_zoom_level(clamped_zoom) + +func _set_zoom_level(value): + zoom = value + + clamp_offset() diff --git a/scripts/player.gd b/scripts/player.gd index c02786e..990f75c 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -10,8 +10,12 @@ var ActiveContract : Node2D var Money : int = 0 +var Frozen = false + @onready var PackageSprite = $PackageSprite +@onready var Camera = $Camera2D + # Called when the node enters the scene tree for the first time. func _ready() -> void: pass # Replace with function body. @@ -20,20 +24,22 @@ func _ready() -> void: # Called every frame. 'delta' is the elapsed time since the previous frame. func _physics_process(delta: float) -> void: - var direction = Vector2(0,0) + if not Frozen: - if Input.is_action_pressed("up"): - direction.y -= 1 - if Input.is_action_pressed("down"): - direction.y += 1 - if Input.is_action_pressed("left"): - direction.x -= 1 - if Input.is_action_pressed("right"): - direction.x += 1 - - direction = direction.normalized() - - velocity += direction * Speed * delta + var direction = Vector2(0,0) + + if Input.is_action_pressed("up"): + direction.y -= 1 + if Input.is_action_pressed("down"): + direction.y += 1 + if Input.is_action_pressed("left"): + direction.x -= 1 + if Input.is_action_pressed("right"): + direction.x += 1 + + direction = direction.normalized() + + velocity += direction * Speed * delta velocity = velocity.move_toward(Vector2(0,0), delta * clamp(velocity.length() * FrictionMult, 1000, 10000)) diff --git a/scripts/telescope.gd b/scripts/telescope.gd new file mode 100644 index 0000000..c1135ec --- /dev/null +++ b/scripts/telescope.gd @@ -0,0 +1,33 @@ +extends Area2D + +@export var Player : Node2D + +@onready var InteractibleManager = $InteractibleManager + +@onready var Camera : Camera2D = $Camera2D + +var selected = false + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + if Input.is_action_just_pressed("interact") and InteractibleManager.selected: + if not selected: + Camera.enabled = true + Player.Camera.enabled = false + Player.Frozen = true + selected = true + + get_tree().call_group("Telescope Label", "show") + + else: + Camera.enabled = false + Player.Camera.enabled = true + Player.Frozen = false + selected = false + + get_tree().call_group("Telescope Label", "hide") diff --git a/sprites/telescope.png b/sprites/telescope.png new file mode 100644 index 0000000..6586076 Binary files /dev/null and b/sprites/telescope.png differ diff --git a/sprites/telescope.png.import b/sprites/telescope.png.import new file mode 100644 index 0000000..7c140f7 --- /dev/null +++ b/sprites/telescope.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddjktr536c58t" +path="res://.godot/imported/telescope.png-ae14fb22b824c1abbd817dcdf812dee8.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://sprites/telescope.png" +dest_files=["res://.godot/imported/telescope.png-ae14fb22b824c1abbd817dcdf812dee8.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