diff options
Diffstat (limited to 'game/src/GameSession')
-rw-r--r-- | game/src/GameSession/GameSession.gd | 2 | ||||
-rw-r--r-- | game/src/GameSession/GameSession.tscn | 33 | ||||
-rw-r--r-- | game/src/GameSession/MapControlPanel.gd | 40 | ||||
-rw-r--r-- | game/src/GameSession/MapControlPanel.tscn | 43 | ||||
-rw-r--r-- | game/src/GameSession/MapView.gd | 257 | ||||
-rw-r--r-- | game/src/GameSession/MapView.tscn | 29 | ||||
-rw-r--r-- | game/src/GameSession/Minimap.gd | 89 | ||||
-rw-r--r-- | game/src/GameSession/ProvinceOverviewPanel.gd | 22 | ||||
-rw-r--r-- | game/src/GameSession/ProvinceOverviewPanel.tscn | 42 | ||||
-rw-r--r-- | game/src/GameSession/TerrainMap.gdshader | 45 |
10 files changed, 583 insertions, 19 deletions
diff --git a/game/src/GameSession/GameSession.gd b/game/src/GameSession/GameSession.gd index 0d69bf2..38eaba1 100644 --- a/game/src/GameSession/GameSession.gd +++ b/game/src/GameSession/GameSession.gd @@ -1,4 +1,4 @@ -extends Control +extends Node @export var _game_session_menu : Control diff --git a/game/src/GameSession/GameSession.tscn b/game/src/GameSession/GameSession.tscn index f984daf..e270f8a 100644 --- a/game/src/GameSession/GameSession.tscn +++ b/game/src/GameSession/GameSession.tscn @@ -1,23 +1,20 @@ -[gd_scene load_steps=4 format=3 uid="uid://bgnupcshe1m7r"] +[gd_scene load_steps=6 format=3 uid="uid://bgnupcshe1m7r"] [ext_resource type="Script" path="res://src/GameSession/GameSession.gd" id="1_eklvp"] [ext_resource type="PackedScene" uid="uid://g524p8lr574w" path="res://src/GameSession/MapControlPanel.tscn" id="3_afh6d"] [ext_resource type="PackedScene" uid="uid://dvdynl6eir40o" path="res://src/GameSession/GameSessionMenu.tscn" id="3_bvmqh"] +[ext_resource type="PackedScene" uid="uid://dkehmdnuxih2r" path="res://src/GameSession/MapView.tscn" id="4_xkg5j"] +[ext_resource type="PackedScene" uid="uid://byq323jbel48u" path="res://src/GameSession/ProvinceOverviewPanel.tscn" id="5_osjnn"] -[node name="GameSession" type="Control" node_paths=PackedStringArray("_game_session_menu")] -editor_description = "SS-102" -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 +[node name="GameSession" type="Node" node_paths=PackedStringArray("_game_session_menu")] +editor_description = "SS-102, UI-546" script = ExtResource("1_eklvp") _game_session_menu = NodePath("GameSessionMenu") +[node name="MapView" parent="." instance=ExtResource("4_xkg5j")] + [node name="GameSessionMenu" parent="." instance=ExtResource("3_bvmqh")] visible = false -layout_mode = 1 anchors_preset = 8 anchor_left = 0.5 anchor_top = 0.5 @@ -27,14 +24,28 @@ grow_horizontal = 2 grow_vertical = 2 [node name="MapControlPanel" parent="." instance=ExtResource("3_afh6d")] -layout_mode = 1 anchors_preset = 3 anchor_left = 1.0 anchor_top = 1.0 anchor_right = 1.0 anchor_bottom = 1.0 +offset_left = -350.0 +offset_top = -210.0 grow_horizontal = 0 grow_vertical = 0 +[node name="ProvinceOverviewPanel" parent="." instance=ExtResource("5_osjnn")] +anchors_preset = -1 +anchor_top = 0.583333 +anchor_right = 0.15625 +offset_top = 0.0 +offset_right = 0.0 + +[connection signal="map_view_camera_changed" from="MapView" to="MapControlPanel" method="_on_map_view_camera_changed"] +[connection signal="province_selected" from="MapView" to="ProvinceOverviewPanel" method="_on_province_selected"] [connection signal="close_button_pressed" from="GameSessionMenu" to="." method="_on_game_session_menu_close_button_pressed"] [connection signal="game_session_menu_button_pressed" from="MapControlPanel" to="." method="_on_game_session_menu_button_pressed"] +[connection signal="mapmode_changed" from="MapControlPanel" to="MapView" method="_update_colour_texture"] +[connection signal="minimap_clicked" from="MapControlPanel" to="MapView" method="_on_minimap_clicked"] +[connection signal="mouse_entered" from="MapControlPanel" to="MapView" method="_on_mouse_exited_viewport"] +[connection signal="mouse_exited" from="MapControlPanel" to="MapView" method="_on_mouse_entered_viewport"] diff --git a/game/src/GameSession/MapControlPanel.gd b/game/src/GameSession/MapControlPanel.gd index ad56536..693890f 100644 --- a/game/src/GameSession/MapControlPanel.gd +++ b/game/src/GameSession/MapControlPanel.gd @@ -1,8 +1,46 @@ extends PanelContainer signal game_session_menu_button_pressed +signal mapmode_changed +signal map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_right : Vector2, near_right : Vector2) +signal minimap_clicked(pos_clicked : Vector2) + +@export var _mapmodes_grid : GridContainer + +var _mapmode_button_group : ButtonGroup + +# REQUIREMENTS: +# * UI-550, UI-554 +func _add_mapmode_button(identifier : String) -> void: + var button := Button.new() + button.text = identifier + button.tooltip_text = identifier + button.toggle_mode = true + button.button_group = _mapmode_button_group + _mapmodes_grid.add_child(button) + if _mapmode_button_group.get_pressed_button() == null: + button.button_pressed = true + +func _ready(): + _mapmode_button_group = ButtonGroup.new() + _mapmode_button_group.pressed.connect(_mapmode_pressed) + for index in MapSingleton.get_mapmode_count(): + _add_mapmode_button(MapSingleton.get_mapmode_identifier(index)) # REQUIREMENTS: # * UIFUN-10 -func _on_game_session_menu_button_pressed(): +func _on_game_session_menu_button_pressed() -> void: game_session_menu_button_pressed.emit() + +# REQUIREMENTS: +# * SS-76 +# * UIFUN-129, UIFUN-133 +func _mapmode_pressed(button : BaseButton) -> void: + MapSingleton.set_mapmode(button.tooltip_text) + mapmode_changed.emit() + +func _on_map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_right : Vector2, near_right : Vector2) -> void: + map_view_camera_changed.emit(near_left, far_left, far_right, near_right) + +func _on_minimap_clicked(pos_clicked : Vector2) -> void: + minimap_clicked.emit(pos_clicked) diff --git a/game/src/GameSession/MapControlPanel.tscn b/game/src/GameSession/MapControlPanel.tscn index 71d43e7..70d7eab 100644 --- a/game/src/GameSession/MapControlPanel.tscn +++ b/game/src/GameSession/MapControlPanel.tscn @@ -1,24 +1,52 @@ -[gd_scene load_steps=2 format=3 uid="uid://g524p8lr574w"] +[gd_scene load_steps=7 format=3 uid="uid://g524p8lr574w"] [ext_resource type="Script" path="res://src/GameSession/MapControlPanel.gd" id="1_ign64"] +[ext_resource type="Texture2D" uid="uid://c0sm1jfu4kyv3" path="res://art/ui/minimap.png" id="2_r613r"] +[ext_resource type="Script" path="res://src/GameSession/Minimap.gd" id="3_s4dml"] +[ext_resource type="Texture2D" uid="uid://vr1hq2stk8ny" path="res://art/ui/minimap_frame.png" id="4_f1exl"] -[node name="PanelContainer" type="PanelContainer"] +[sub_resource type="InputEventAction" id="InputEventAction_5nck3"] +action = &"ui_cancel" + +[sub_resource type="Shortcut" id="Shortcut_fc1tk"] +events = [SubResource("InputEventAction_5nck3")] + +[node name="MapControlPanel" type="PanelContainer" node_paths=PackedStringArray("_mapmodes_grid")] editor_description = "SS-103" +mouse_filter = 1 script = ExtResource("1_ign64") +_mapmodes_grid = NodePath("HBoxContainer/VBoxContainer/MapmodesGrid") [node name="HBoxContainer" type="HBoxContainer" parent="."] layout_mode = 2 +alignment = 2 [node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"] layout_mode = 2 -[node name="MapmodesPlaceholder" type="Label" parent="HBoxContainer/VBoxContainer"] +[node name="MapmodesGrid" type="GridContainer" parent="HBoxContainer/VBoxContainer"] +editor_description = "UI-750" +layout_mode = 2 +columns = 11 + +[node name="Minimap" type="PanelContainer" parent="HBoxContainer/VBoxContainer"] +editor_description = "UI-549" +layout_mode = 2 +mouse_filter = 1 + +[node name="TextureRect" type="TextureRect" parent="HBoxContainer/VBoxContainer/Minimap"] +editor_description = "UI-751, FS-338" +layout_mode = 2 +texture = ExtResource("2_r613r") + +[node name="ViewportQuad" type="Control" parent="HBoxContainer/VBoxContainer/Minimap"] layout_mode = 2 -text = "MAPMODES" +mouse_filter = 2 +script = ExtResource("3_s4dml") -[node name="MinimapPlaceholder" type="Label" parent="HBoxContainer/VBoxContainer"] +[node name="Frame" type="NinePatchRect" parent="HBoxContainer/VBoxContainer/Minimap"] layout_mode = 2 -text = "MINIMAP" +texture = ExtResource("4_f1exl") [node name="AuxiliaryPanel" type="VBoxContainer" parent="HBoxContainer"] editor_description = "UI-761" @@ -27,6 +55,9 @@ layout_mode = 2 [node name="GameSessionMenuButton" type="Button" parent="HBoxContainer/AuxiliaryPanel"] editor_description = "UI-9" layout_mode = 2 +shortcut = SubResource("Shortcut_fc1tk") text = "ESC" +[connection signal="map_view_camera_changed" from="." to="HBoxContainer/VBoxContainer/Minimap/ViewportQuad" method="_on_map_view_camera_changed"] +[connection signal="minimap_clicked" from="HBoxContainer/VBoxContainer/Minimap/ViewportQuad" to="." method="_on_minimap_clicked"] [connection signal="pressed" from="HBoxContainer/AuxiliaryPanel/GameSessionMenuButton" to="." method="_on_game_session_menu_button_pressed"] diff --git a/game/src/GameSession/MapView.gd b/game/src/GameSession/MapView.gd new file mode 100644 index 0000000..54d8df8 --- /dev/null +++ b/game/src/GameSession/MapView.gd @@ -0,0 +1,257 @@ +extends Node3D + +signal province_selected(identifier : String) +signal map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_right : Vector2, near_right : Vector2) + +const _action_north : StringName = &"map_north" +const _action_east : StringName = &"map_east" +const _action_south : StringName = &"map_south" +const _action_west : StringName = &"map_west" +const _action_zoomin : StringName = &"map_zoomin" +const _action_zoomout : StringName = &"map_zoomout" +const _action_drag : StringName = &"map_drag" +const _action_click : StringName = &"map_click" + +const _shader_param_province_index : StringName = &"province_index_tex" +const _shader_param_province_colour : StringName = &"province_colour_tex" +const _shader_param_hover_index : StringName = &"hover_index" +const _shader_param_selected_index : StringName = &"selected_index" + +@export var _camera : Camera3D + +@export var _cardinal_move_speed : float = 1.0 +@export var _edge_move_threshold: float = 0.02 +@export var _edge_move_speed: float = 2.5 +var _drag_anchor : Vector2 +var _drag_active : bool = false + +var _mouse_over_viewport : bool = false + +@export var _zoom_target_min : float = 0.2 +@export var _zoom_target_max : float = 5.0 +@export var _zoom_target_step : float = 0.1 +@export var _zoom_epsilon : float = _zoom_target_step * 0.1 +@export var _zoom_speed : float = 5.0 +@export var _zoom_target : float = 1.0: + get: return _zoom_target + set(v): _zoom_target = clamp(v, _zoom_target_min, _zoom_target_max) + +@export var _map_mesh_instance : MeshInstance3D +var _map_mesh : MapMesh +var _map_shader_material : ShaderMaterial +var _map_image_size : Vector2 +var _map_province_index_image : Image +var _map_province_colour_image : Image +var _map_province_colour_texture : ImageTexture +var _map_mesh_corner : Vector2 +var _map_mesh_dims : Vector2 + +var _mouse_pos_viewport : Vector2 = Vector2(0.5, 0.5) +var _mouse_pos_map : Vector2 = Vector2(0.5, 0.5) +var _viewport_dims : Vector2 = Vector2(1, 1) + +# ??? Strange Godot/GDExtension Bug ??? +# Upon first opening a clone of this repo with the Godot Editor, +# if MapSingleton.get_province_index_image is called before MapMesh +# is referenced in the script below, then the editor will crash due +# to a failed HashMap lookup. I'm not sure if this is a bug in the +# editor, GDExtension, my own extension, or a combination of them. +# This was an absolute pain to track down. --- hop311 +func _ready(): + if _camera == null: + push_error("MapView's _camera variable hasn't been set!") + return + _zoom_target = _camera.position.y + if _map_mesh_instance == null: + push_error("MapView's _map_mesh variable hasn't been set!") + return + + # Shader Material + var map_material := _map_mesh_instance.get_active_material(0) + if map_material == null: + push_error("Map mesh is missing material!") + return + if not map_material is ShaderMaterial: + push_error("Invalid map mesh material class: ", map_material.get_class()) + return + _map_shader_material = map_material + + # Province index texture + _map_province_index_image = MapSingleton.get_province_index_image() + if _map_province_index_image == null: + push_error("Failed to get province index image!") + return + var province_index_texture := ImageTexture.create_from_image(_map_province_index_image) + _map_shader_material.set_shader_parameter(_shader_param_province_index, province_index_texture) + + # Province colour texture + _map_province_colour_image = MapSingleton.get_province_colour_image() + if _map_province_colour_image == null: + push_error("Failed to get province colour image!") + return + _map_province_colour_texture = ImageTexture.create_from_image(_map_province_colour_image) + _map_shader_material.set_shader_parameter(_shader_param_province_colour, _map_province_colour_texture) + + if not _map_mesh_instance.mesh is MapMesh: + push_error("Invalid map mesh class: ", _map_mesh_instance.mesh.get_class(), "(expected MapMesh)") + return + _map_mesh = _map_mesh_instance.mesh + + # Set map mesh size and get bounds + _map_image_size = Vector2(Vector2i(MapSingleton.get_width(), MapSingleton.get_height())) + _map_mesh.aspect_ratio = _map_image_size.x / _map_image_size.y + var map_mesh_aabb := _map_mesh.get_core_aabb() * _map_mesh_instance.transform + _map_mesh_corner = Vector2( + min(map_mesh_aabb.position.x, map_mesh_aabb.end.x), + min(map_mesh_aabb.position.z, map_mesh_aabb.end.z) + ) + _map_mesh_dims = abs(Vector2( + map_mesh_aabb.position.x - map_mesh_aabb.end.x, + map_mesh_aabb.position.z - map_mesh_aabb.end.z + )) + +func _notification(what : int): + match what: + NOTIFICATION_WM_MOUSE_ENTER: # Mouse inside window + _on_mouse_entered_viewport() + NOTIFICATION_WM_MOUSE_EXIT: # Mouse out of window + _on_mouse_exited_viewport() + +func _update_colour_texture() -> void: + MapSingleton.update_colour_image() + _map_province_colour_texture.update(_map_province_colour_image) + +func _world_to_map_coords(pos : Vector3) -> Vector2: + return (Vector2(pos.x, pos.z) - _map_mesh_corner) / _map_mesh_dims + +func _viewport_to_map_coords(pos_viewport : Vector2) -> Vector2: + var ray_origin := _camera.project_ray_origin(pos_viewport) + var ray_normal := _camera.project_ray_normal(pos_viewport) + # Plane with normal (0,1,0) facing upwards, at a distance 0 from the origin + var intersection = Plane(0, 1, 0, 0).intersects_ray(ray_origin, ray_normal) + if typeof(intersection) == TYPE_VECTOR3: + return _world_to_map_coords(intersection as Vector3) + else: + # Normals parallel to the xz-plane could cause null intersections, + # but the camera's orientation should prevent such normals + push_error("Invalid intersection: ", intersection) + return Vector2(0.5, 0.5) + +# REQUIREMENTS +# * SS-31 +func _unhandled_input(event : InputEvent): + if event.is_action_pressed(_action_click): + # Check if the mouse is outside of bounds + if _map_mesh.is_valid_uv_coord(_mouse_pos_map): + var selected_index := MapSingleton.get_province_index_from_uv_coords(_mouse_pos_map) + _map_shader_material.set_shader_parameter(_shader_param_selected_index, selected_index) + var province_identifier := MapSingleton.get_province_identifier_from_uv_coords(_mouse_pos_map) + province_selected.emit(province_identifier) + else: + print("Clicked outside the map!") + elif event.is_action_pressed(_action_drag): + if _drag_active: + push_warning("Drag being activated while already active!") + _drag_active = true + _drag_anchor = _mouse_pos_map + elif event.is_action_released(_action_drag): + if not _drag_active: + push_warning("Drag being deactivated while already not active!") + _drag_active = false + elif event.is_action_pressed(_action_zoomin, true): + _zoom_target -= _zoom_target_step + elif event.is_action_pressed(_action_zoomout, true): + _zoom_target += _zoom_target_step + +func _physics_process(delta : float): + _mouse_pos_viewport = get_viewport().get_mouse_position() + _viewport_dims = Vector2(Resolution.get_current_resolution()) + # Process movement + _movement_process(delta) + # Keep within map bounds + _clamp_over_map() + # Process zooming + _zoom_process(delta) + # Orient based on height + _update_orientation() + # Update viewport on minimap + _update_minimap_viewport() + # Calculate where the mouse lies on the map + _update_mouse_map_position() + +# REQUIREMENTS +# * UIFUN-124 +func _movement_process(delta : float) -> void: + var direction : Vector2 + if _drag_active: + direction = (_drag_anchor - _mouse_pos_map) * _map_mesh_dims + else: + direction = _edge_scrolling_vector() + _cardinal_movement_vector() + # Scale movement speed with height + direction *= _camera.position.y * delta + _camera.position += Vector3(direction.x, 0, direction.y) + +# REQUIREMENTS +# * UIFUN-125 +func _edge_scrolling_vector() -> Vector2: + if not _mouse_over_viewport: + return Vector2() + var mouse_vector := _mouse_pos_viewport / _viewport_dims - Vector2(0.5, 0.5) + if abs(mouse_vector.x) < 0.5 - _edge_move_threshold and abs(mouse_vector.y) < 0.5 - _edge_move_threshold: + mouse_vector *= 0 + return mouse_vector * _edge_move_speed + +# REQUIREMENTS +# * SS-75 +func _cardinal_movement_vector() -> Vector2: + var move := Vector2( + float(Input.is_action_pressed(_action_east)) - float(Input.is_action_pressed(_action_west)), + float(Input.is_action_pressed(_action_south)) - float(Input.is_action_pressed(_action_north)) + ) + return move * _cardinal_move_speed + +func _clamp_over_map() -> void: + _camera.position.x = _map_mesh_corner.x + fposmod(_camera.position.x - _map_mesh_corner.x, _map_mesh_dims.x) + _camera.position.z = clamp(_camera.position.z, _map_mesh_corner.y, _map_mesh_corner.y + _map_mesh_dims.y) + +# REQUIREMENTS +# * SS-74 +# * UIFUN-123 +func _zoom_process(delta : float) -> void: + var height := _camera.position.y + var zoom := _zoom_target - height + height += zoom * _zoom_speed * delta + var new_zoom := _zoom_target - height + # Set to target if height is within _zoom_epsilon of it or has overshot past it + if abs(new_zoom) < _zoom_epsilon or sign(zoom) != sign(new_zoom): + height = _zoom_target + _camera.position.y = height + +func _update_orientation() -> void: + var dir := Vector3(0, -1, -exp(-_camera.position.y * 2.0 + 0.5)) + _camera.look_at(_camera.position + dir) + +func _update_minimap_viewport() -> void: + var near_left := _viewport_to_map_coords(Vector2(0, _viewport_dims.y)) + var far_left := _viewport_to_map_coords(Vector2(0, 0)) + var far_right := _viewport_to_map_coords(Vector2(_viewport_dims.x, 0)) + var near_right := _viewport_to_map_coords(_viewport_dims) + map_view_camera_changed.emit(near_left, far_left, far_right, near_right) + +func _update_mouse_map_position() -> void: + _mouse_pos_map = _viewport_to_map_coords(_mouse_pos_viewport) + var hover_index := MapSingleton.get_province_index_from_uv_coords(_mouse_pos_map) + if _mouse_over_viewport: + _map_shader_material.set_shader_parameter(_shader_param_hover_index, hover_index) + +func _on_mouse_entered_viewport(): + _mouse_over_viewport = true + +func _on_mouse_exited_viewport(): + _mouse_over_viewport = false + +func _on_minimap_clicked(pos_clicked : Vector2): + pos_clicked *= _map_mesh_dims + _camera.position.x = pos_clicked.x + _camera.position.z = pos_clicked.y + _clamp_over_map()
\ No newline at end of file diff --git a/game/src/GameSession/MapView.tscn b/game/src/GameSession/MapView.tscn new file mode 100644 index 0000000..93fc162 --- /dev/null +++ b/game/src/GameSession/MapView.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=6 format=3 uid="uid://dkehmdnuxih2r"] + +[ext_resource type="Script" path="res://src/GameSession/MapView.gd" id="1_exccw"] +[ext_resource type="Shader" path="res://src/GameSession/TerrainMap.gdshader" id="1_upocn"] +[ext_resource type="Texture2D" uid="uid://cmw0pvjthnn8c" path="res://art/terrain/terrain.png" id="3_l8pnf"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_tayeg"] +render_priority = 0 +shader = ExtResource("1_upocn") +shader_parameter/hover_index = null +shader_parameter/selected_index = null +shader_parameter/terrain_tex = ExtResource("3_l8pnf") + +[sub_resource type="MapMesh" id="MapMesh_3gtsd"] + +[node name="MapView" type="Node3D" node_paths=PackedStringArray("_camera", "_map_mesh_instance")] +editor_description = "SS-73" +script = ExtResource("1_exccw") +_camera = NodePath("MapCamera") +_map_mesh_instance = NodePath("MapMeshInstance") + +[node name="MapCamera" type="Camera3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 0.707107, 0.707107, 0, -0.707107, 0.707107, 0.25, 1.5, -2.75) + +[node name="MapMeshInstance" type="MeshInstance3D" parent="."] +editor_description = "FS-343" +transform = Transform3D(10, 0, 0, 0, 10, 0, 0, 0, 10, 0, 0, 0) +material_override = SubResource("ShaderMaterial_tayeg") +mesh = SubResource("MapMesh_3gtsd") diff --git a/game/src/GameSession/Minimap.gd b/game/src/GameSession/Minimap.gd new file mode 100644 index 0000000..25c7cac --- /dev/null +++ b/game/src/GameSession/Minimap.gd @@ -0,0 +1,89 @@ +extends Control + +signal minimap_clicked(pos_clicked : Vector2) + +const _action_click : StringName = &"map_click" + +var _viewport_points : PackedVector2Array + +# REQUIREMENTS +# * SS-80 +# * UI-752 +func _draw() -> void: + if _viewport_points.size() > 1: + draw_multiline(_viewport_points, Color.WHITE, -1) + +# REQUIREMENTS +# * SS-81 +# * UIFUN-127 +func _unhandled_input(event : InputEvent): + if event is InputEventMouse and Input.is_action_pressed(_action_click): + var pos_clicked := get_local_mouse_position() / size - Vector2(0.5, 0.5) + if abs(pos_clicked.x) < 0.5 and abs(pos_clicked.y) < 0.5: + minimap_clicked.emit(pos_clicked) + +# Returns the point on the line going through p and q with the specific x coord +func _intersect_x(p : Vector2, q : Vector2, x : float) -> Vector2: + if p.x == q.x: + return Vector2(x, 0.5 * (p.y + q.y)) + var t := (x - q.x) / (p.x - q.x) + return q + t * (p - q) + +# Returns the point on the line going through p and q with the specific y coord +func _intersect_y(p : Vector2, q : Vector2, y : float) -> Vector2: + if p.y == q.y: + return Vector2(0.5 * (p.x + q.x), y) + var t := (y - q.y) / (p.y - q.y) + return q + t * (p - q) + +const _one_x := Vector2(1, 0) + +func _add_line_looped_over_x(left : Vector2, right : Vector2) -> void: + if left.x < 0: + if right.x < 0: + _viewport_points.push_back(left + _one_x) + _viewport_points.push_back(right + _one_x) + else: + var mid_point := _intersect_x(left, right, 0) + _viewport_points.push_back(mid_point) + _viewport_points.push_back(right) + mid_point.x = 1 + _viewport_points.push_back(left + _one_x) + _viewport_points.push_back(mid_point) + elif right.x > 1: + if left.x > 1: + _viewport_points.push_back(left - _one_x) + _viewport_points.push_back(right - _one_x) + else: + var mid_point := _intersect_x(left, right, 1) + _viewport_points.push_back(left) + _viewport_points.push_back(mid_point) + mid_point.x = 0 + _viewport_points.push_back(mid_point) + _viewport_points.push_back(right - _one_x) + else: + _viewport_points.push_back(left) + _viewport_points.push_back(right) + +# This can break if the viewport is rotated too far! +func _on_map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_right : Vector2, near_right : Vector2) -> void: + # Bound far y coords + if far_left.y < 0: + far_left = _intersect_y(near_left, far_left, 0) + if far_right.y < 0: + far_right = _intersect_y(near_right, far_right, 0) + # Bound near y coords + if near_left.y > 1: + near_left = _intersect_y(near_left, far_left, 1) + if near_right.y > 1: + near_right = _intersect_y(near_right, far_right, 1) + + _viewport_points.clear() + _add_line_looped_over_x(near_left, near_right) + _add_line_looped_over_x(far_left, far_right) + _add_line_looped_over_x(far_left, near_left) + _add_line_looped_over_x(near_right, far_right) + + for i in _viewport_points.size(): + _viewport_points[i] *= size + queue_redraw() diff --git a/game/src/GameSession/ProvinceOverviewPanel.gd b/game/src/GameSession/ProvinceOverviewPanel.gd new file mode 100644 index 0000000..434f6b1 --- /dev/null +++ b/game/src/GameSession/ProvinceOverviewPanel.gd @@ -0,0 +1,22 @@ +extends Panel + +@export var _province_name_label : Label + +@export var province_identifier: String = "": + get: return province_identifier + set(v): + province_identifier = v + update_info() + +func _ready(): + update_info() + +func update_info() -> void: + _province_name_label.text = province_identifier + "_NAME" + visible = not province_identifier.is_empty() + +func _on_province_selected(identifier : String) -> void: + province_identifier = identifier + +func _on_button_pressed() -> void: + province_identifier = "" diff --git a/game/src/GameSession/ProvinceOverviewPanel.tscn b/game/src/GameSession/ProvinceOverviewPanel.tscn new file mode 100644 index 0000000..2df95fb --- /dev/null +++ b/game/src/GameSession/ProvinceOverviewPanel.tscn @@ -0,0 +1,42 @@ +[gd_scene load_steps=2 format=3 uid="uid://byq323jbel48u"] + +[ext_resource type="Script" path="res://src/GameSession/ProvinceOverviewPanel.gd" id="1_3n8k5"] + +[node name="ProvinceOverviewPanel" type="Panel" node_paths=PackedStringArray("_province_name_label")] +editor_description = "UI-56" +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_top = -300.0 +offset_right = 200.0 +grow_vertical = 0 +script = ExtResource("1_3n8k5") +_province_name_label = NodePath("VBoxContainer/ProvinceName") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = -1 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = 10.0 +offset_top = 5.0 +offset_right = -10.0 +offset_bottom = -5.0 + +[node name="ProvinceName" type="Label" parent="VBoxContainer"] +editor_description = "UI-57" +layout_mode = 2 +text = "PROVINCE_NAME" +vertical_alignment = 1 + +[node name="Button" type="Button" parent="."] +custom_minimum_size = Vector2(30, 30) +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.85 +anchor_right = 1.0 +anchor_bottom = 0.103333 +grow_horizontal = 0 +text = "X" + +[connection signal="pressed" from="Button" to="." method="_on_button_pressed"] diff --git a/game/src/GameSession/TerrainMap.gdshader b/game/src/GameSession/TerrainMap.gdshader new file mode 100644 index 0000000..7aca0f9 --- /dev/null +++ b/game/src/GameSession/TerrainMap.gdshader @@ -0,0 +1,45 @@ +shader_type spatial; + +render_mode unshaded; + +// Cosmetic terrain texture +uniform sampler2D terrain_tex: source_color, repeat_enable, filter_linear; +// Province index texture +uniform sampler2D province_index_tex : repeat_enable, filter_nearest; +// Province colour texture +uniform sampler2D province_colour_tex: source_color, repeat_enable, filter_nearest; +// Index of the mouse over the map mesh +uniform uint hover_index; +// Index of the currently selected province +uniform uint selected_index; + +uvec2 vec2_to_uvec2(vec2 v) { + return uvec2(v * 255.0); +} + +uint uvec2_to_uint(uvec2 v) { + return (v.y << 8u) | v.x; +} + +uvec2 read_uvec2(sampler2D tex, vec2 uv) { + return vec2_to_uvec2(texture(tex, uv).rg); +} + +uint read_uint16(sampler2D tex, vec2 uv) { + return uvec2_to_uint(read_uvec2(tex, uv)); +} + +void fragment() { + uvec2 prov_idx_split = read_uvec2(province_index_tex, UV); + uint prov_index = uvec2_to_uint(prov_idx_split); + + // Boost prov_colour's contribution if it matches hover_colour or selected_colour + float mix_val = 0.3 + float(prov_index == hover_index) * 0.3 + float(prov_index == selected_index) * 0.3; + // Don't mix if the province index is 0 + mix_val *= float(prov_index != 0u); + + vec3 terrain_colour = texture(terrain_tex, UV).rgb; + vec3 province_colour = texelFetch(province_colour_tex, ivec2(prov_idx_split), 0).rgb; + + ALBEDO = mix(terrain_colour, province_colour, mix_val); +} |