├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── actors ├── actor_top_down.gd ├── actor_top_down.tscn ├── demon_cute │ ├── demon_cute.gd │ └── demon_cute.tscn ├── enemies │ ├── enemy.gd │ └── enemy.tscn ├── player │ ├── FootstepManager.gd │ ├── ItemManager.gd │ ├── camera_viewbob.gd │ ├── input_axis.gd │ ├── input_direction.gd │ ├── input_direction.tscn │ ├── interact_box.gd │ ├── mouse_grabber.gd │ ├── orbit_point.gd │ ├── player_base_state.gd │ ├── player_controller.gd │ ├── player_idle_state.gd │ ├── player_top_down.gd │ ├── player_top_down.tscn │ ├── player_walk_state.gd │ ├── soul_orb.gd │ └── tpcc_test.gd ├── test_flash.gd ├── top_down_2d │ ├── player_top_down_2d.gd │ └── player_top_down_2d.tscn ├── top_down_base_state.gd └── top_down_move_state.gd ├── icon.svg ├── icon.svg.import ├── levels ├── area_container.gd ├── example │ ├── geometry │ │ ├── 5x_5_tile.tscn │ │ ├── geometry_example_1.gd │ │ ├── geometry_example_1.tscn │ │ ├── geometry_example_2.tscn │ │ ├── sprite_3d_barrel.tscn │ │ ├── sprite_3d_pillar.tscn │ │ ├── wall_dungeon_1x2.tscn │ │ ├── water_box.gd │ │ └── water_box.tscn │ ├── launcher_example.gd │ ├── launcher_example.tscn │ ├── level_example_1.gd │ ├── level_example_1.tscn │ ├── level_example_2.gd │ ├── level_example_2.tscn │ ├── level_example_3.gd │ ├── main_menu_example.gd │ ├── main_menu_example.tscn │ └── tool_color_rect.gd ├── intro.gd ├── intro.tscn ├── intro │ ├── made_with_tools.gd │ └── made_with_tools.tscn ├── launcher.gd ├── launcher.tscn ├── level_3d.gd ├── level_3d.tscn ├── npc_container.gd ├── patrol_path.gd ├── patrol_path.tscn ├── player_container.gd ├── player_spawn_position.gd └── tool_color_rect.gd ├── management ├── camera_manager_3d.gd ├── camera_manager_3d.tscn ├── camera_manger_2d.gd ├── camera_manger_2d.tscn ├── data_structures │ ├── game_data.gd │ ├── resource_data │ │ ├── ambience_data.gd │ │ ├── dialogue_data.gd │ │ ├── instance_data.gd │ │ ├── level_data.gd │ │ ├── music_data.gd │ │ ├── resource_data.gd │ │ └── sfx_data.gd │ ├── resource_load_data.gd │ └── save_metadata.gd ├── debug_menu.gd ├── debug_menu.tscn ├── debug_options_list.gd ├── fade_rect.gd ├── fade_rect.tscn ├── game_manager.gd ├── game_manager_no_subviewport.tscn ├── game_manager_with_subviewport.tscn ├── game_state_saver.gd ├── global.gd ├── resource_manager.gd ├── resource_manager.tscn ├── save_game_manager.gd ├── save_game_manager.tscn ├── save_metadata.gd ├── screen_space_shader_manager.gd ├── screen_space_shader_manager.tscn ├── signal_manager.gd ├── sound_manager.gd └── sound_manager.tscn ├── materials └── textures │ ├── Tiles012_1K_Color.jpg │ ├── Tiles012_1K_Color.jpg.import │ ├── decorative.png │ ├── decorative.png.import │ ├── demon_blob.png │ ├── demon_blob.png.import │ ├── tile.png │ ├── tile.png.import │ ├── warning_background.png │ └── warning_background.png.import ├── project.godot ├── readme_screenshots ├── Screenshot1.png ├── Screenshot1.png.import ├── Screenshot2.png ├── Screenshot2.png.import ├── Screenshot3.png ├── Screenshot3.png.import ├── screenshot4.png └── screenshot4.png.import ├── script_templates ├── launcher │ └── launcher_template.gd └── level │ └── level_template.gd ├── shaders ├── B&W.gdshader ├── BetterCC.gdshader ├── Blur.gdshader ├── ColorPrecission.gdshader ├── Glitch.gdshader ├── Grain.gdshader ├── JpegCompression.gdshader ├── LensDistortion.gdshader ├── NTSC.gdshader ├── NTSCBasic.gdshader ├── Sharpness.gdshader ├── SimpleGlitch.gdshader ├── SimpleGrain.gdshader ├── TV.gdshader ├── VHS.gdshader ├── VHSPause.gdshader ├── gpsx │ ├── 2d │ │ ├── gpsx_2d.gdshader │ │ ├── gpsx_2d_add.gdshader │ │ ├── gpsx_2d_avg.gdshader │ │ ├── gpsx_2d_qadd.gdshader │ │ └── gpsx_2d_sub.gdshader │ ├── 3d │ │ ├── gpsx_3d.gdshader │ │ ├── gpsx_3d_add.gdshader │ │ ├── gpsx_3d_avg.gdshader │ │ ├── gpsx_3d_qadd.gdshader │ │ └── gpsx_3d_sub.gdshader │ └── misc │ │ └── gpsx_sky.gdshader ├── grain.jpg ├── grain.jpg.import ├── lcd_post_process.gdshader ├── psx_dither.gdshader ├── psx_fade.gdshader ├── psx_lit.gdshader └── psx_unlit.gdshader ├── sounds ├── blip2shortest.wav ├── blip2shortest.wav.import ├── carnelia.ogg ├── carnelia.ogg.import ├── fire_bit.wav ├── fire_bit.wav.import ├── foghorn_four_times.ogg ├── foghorn_four_times.ogg.import ├── its_sax.ogg ├── its_sax.ogg.import ├── its_sax8bit.ogg ├── its_sax8bit.ogg.import ├── lost_ambienceLOOPING.ogg ├── lost_ambienceLOOPING.ogg.import ├── njb_intro.ogg ├── njb_intro.ogg.import ├── shifting_loss.ogg └── shifting_loss.ogg.import ├── tools ├── damage_source.gd ├── entity_components │ ├── 2d │ │ ├── damage_number_component_2d.gd │ │ ├── damage_number_component_2d.tscn │ │ ├── damage_number_display_2d.gd │ │ ├── damage_number_display_2d.tscn │ │ ├── death_component_2d.gd │ │ ├── death_component_2d.tscn │ │ ├── hitbox_component_2d.gd │ │ ├── hitbox_component_2d.tscn │ │ ├── hurtbox_component_2d.gd │ │ ├── hurtbox_component_2d.tscn │ │ ├── movement_component_2D.gd │ │ ├── movement_component_2D.tscn │ │ ├── navigation_component_2d.gd │ │ ├── navigation_component_2d.tscn │ │ ├── player_detector_2d.gd │ │ ├── sprite_component.gd │ │ └── sprite_component.tscn │ ├── 3d │ │ ├── hitbox_component_3d.gd │ │ ├── hitbox_component_3d.tscn │ │ ├── hurtbox_component_3d.gd │ │ ├── hurtbox_component_3d.tscn │ │ ├── mesh_component.gd │ │ ├── mesh_component.tscn │ │ ├── movement_component_3D.gd │ │ ├── movement_component_3D.tscn │ │ ├── movement_component_3d.tscn │ │ ├── navigation_component_3d.gd │ │ ├── navigation_component_3d.tscn │ │ └── player_detector_3d.gd │ ├── experience_drop_component.gd │ ├── experience_drop_component.tscn │ └── general │ │ ├── damage_source.gd │ │ ├── health_component.gd │ │ ├── health_component.tscn │ │ └── screenshake.gd ├── follow_camera.gd ├── follow_camera.tscn ├── instance_spawner.gd ├── instance_spawner.tscn ├── map_data.gd ├── map_event.gd ├── map_event.tscn ├── map_generator.gd ├── player_detector.gd ├── player_trigger.gd ├── screenshake.gd ├── screenshake.tscn └── tool_color_rect.gd └── ui ├── example_theme.tres ├── fonts ├── BIOSfontII.ttf ├── BIOSfontII.ttf.import ├── MMXSNES.ttf └── MMXSNES.ttf.import ├── held_input_check.gd ├── held_input_check.tscn ├── menu_selection.gd ├── menu_selection.tscn ├── menu_selection_container.gd ├── menu_selection_container.tscn ├── ui_level_controls.gd └── ui_level_controls.tscn /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 HeroRobb 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /actors/actor_top_down.gd: -------------------------------------------------------------------------------- 1 | class_name TopDownActor 2 | extends CharacterBody3D 3 | 4 | 5 | enum DIRECTIONS {BOTTOM, RIGHT, TOP, LEFT} 6 | 7 | 8 | const ANGULAR_ACCELERATION = 10.0 9 | const ACCELERATION := .5 10 | const AIR_GRAVITY = -9.8 11 | const GROUND_GRAVITY = -0.1 12 | const GROUND_ACCELERATION = 2.0 13 | const FRICTION = 0.25 14 | 15 | @export var max_speed = 10.0 # (float, 10.0, 25.0, 0.5) 16 | 17 | var direction: Vector2 18 | 19 | @onready var _mesh_container := $MeshContainer 20 | @onready var _hitbox: Area3D = $Hitbox 21 | 22 | 23 | func _calculate_gravity() -> void: 24 | velocity.y = GROUND_GRAVITY if is_on_floor() else AIR_GRAVITY 25 | 26 | 27 | func _calculate_movement(delta: float) -> void: 28 | if direction.x != 0 or direction.y != 0: 29 | velocity.x = lerp(velocity.x, direction.x * max_speed, GROUND_ACCELERATION * delta) 30 | velocity.z = lerp(velocity.z, direction.y * max_speed, GROUND_ACCELERATION * delta) 31 | return 32 | 33 | velocity.x = lerp(velocity.x, 0.0, FRICTION) 34 | velocity.z = lerp(velocity.z, 0.0, FRICTION) 35 | 36 | 37 | func _get_horizontal_speed() -> float: 38 | return Vector2(velocity.x, velocity.z).length() 39 | 40 | 41 | func die() -> void: 42 | _mesh_container.hide() 43 | _hitbox.monitoring = false 44 | 45 | -------------------------------------------------------------------------------- /actors/actor_top_down.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://actors/actor_top_down.gd" type="Script" id=1] 4 | 5 | [node name="Actor" type="CharacterBody3D"] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /actors/demon_cute/demon_cute.gd: -------------------------------------------------------------------------------- 1 | extends Node3D 2 | 3 | 4 | @onready var _animation_player: AnimationPlayer = $AnimationPlayer 5 | 6 | 7 | func _ready() -> void: 8 | await get_tree().create_timer(randf_range(0.2, 0.5)).timeout 9 | move_to_random_location() 10 | rotate_to_random_direction() 11 | play_random_animation() 12 | 13 | 14 | func move_to_random_location(range: float = 8.0) -> void: 15 | randomize() 16 | var rand_x: float = randf_range(0 - range, range) 17 | var rand_y: float = randf_range(0 - range, range) 18 | var random_vector: Vector2 = Vector2(rand_x, rand_y) 19 | 20 | global_transform.origin = Vector3(rand_x, 0, rand_y) 21 | 22 | 23 | func rotate_to_random_direction() -> void: 24 | randomize() 25 | var rand_rotation: float = randf_range(0, 2 * PI) 26 | rotate_y(rand_rotation) 27 | 28 | 29 | func play_random_animation() -> void: 30 | randomize() 31 | var random_number = randi_range(0, 2) 32 | 33 | match random_number: 34 | 0: 35 | _animation_player.play("Jump") 36 | 1: 37 | _animation_player.play("Dance") 38 | 2: 39 | _animation_player.play("Bite_Front") 40 | -------------------------------------------------------------------------------- /actors/player/FootstepManager.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | enum STEPS { 4 | GRASS, 5 | METAL, 6 | SAND, 7 | } 8 | 9 | const WALKSTEP_TIME := 0.6 10 | const SPRINTSTEP_TIME := 0.4 11 | const METAL_VOLUME := -5.0 12 | const SAND_VOLUME := -15.0 13 | 14 | @export(NodePath) onready var _agent = get_node(_agent) as CharacterBody3D 15 | 16 | var active := false : set = set_active 17 | var current_step_type: int = STEPS.GRASS 18 | 19 | var _step_volume := -20.0 20 | var _steps := { 21 | STEPS.GRASS: [ 22 | Global.SFX.STEP_GRASS1, 23 | Global.SFX.STEP_GRASS2, 24 | Global.SFX.STEP_GRASS3, 25 | ] 26 | } 27 | var _step_volumes := { 28 | STEPS.GRASS: -5.0, 29 | STEPS.METAL: -5.0, 30 | STEPS.SAND: -15.0, 31 | } 32 | 33 | @onready var _footstep_timer := $FootstepTimer 34 | 35 | 36 | func _ready() -> void: 37 | # warning-ignore-all:return_value_discarded 38 | SignalManager.connect("footsteps_area_entered",Callable(self,"_on_footsteps_area_entered")) 39 | SignalManager.connect("player_playable_changed",Callable(self,"_on_playable_changed")) 40 | 41 | if _agent: 42 | set_active(true) 43 | 44 | 45 | func _physics_process(_delta: float) -> void: 46 | if abs(_agent.flat_velocity) > 0.1 and _agent.is_on_floor() and _footstep_timer.is_stopped(): 47 | _play_footstep() 48 | 49 | 50 | func _play_footstep() -> void: 51 | var rand_number := (randi() % 3) + 1 52 | if _agent.sprinting: 53 | _footstep_timer.wait_time = SPRINTSTEP_TIME 54 | else: 55 | _footstep_timer.wait_time = WALKSTEP_TIME 56 | 57 | var footstep_id = _get_random_current_footstep_sound_id() 58 | SoundManager.play_sfx(_steps[current_step_type][footstep_id], randf_range(0.8, 1.2), _step_volume) 59 | _footstep_timer.start() 60 | 61 | 62 | func _stop_footsteps() -> void: 63 | _footstep_timer.stop() 64 | 65 | 66 | func set_active(new_active: bool) -> void: 67 | if active != new_active: 68 | active = new_active 69 | set_physics_process(active) 70 | 71 | 72 | func _get_random_current_footstep_sound_id() -> int: 73 | randomize() 74 | var random_index := int( round( randf_range(0, _steps[current_step_type].size()) ) ) 75 | return random_index 76 | 77 | 78 | func _on_footsteps_area_entered(new_footstep_id: int) -> void: 79 | current_step_type = new_footstep_id 80 | 81 | _step_volume = _step_volumes[current_step_type] 82 | 83 | 84 | func _on_playable_changed(new_playable: bool) -> void: 85 | set_physics_process(new_playable) 86 | 87 | if not new_playable: 88 | _stop_footsteps() 89 | -------------------------------------------------------------------------------- /actors/player/ItemManager.gd: -------------------------------------------------------------------------------- 1 | extends Node3D 2 | 3 | 4 | #onready var pizza_slice := $Sliced_pizza 5 | 6 | 7 | func _ready() -> void: 8 | # warning-ignore-all:return_value_discarded 9 | SignalManager.connect("item_picked_up",Callable(self,"_on_item_picked_up")) 10 | SignalManager.connect("item_dropped",Callable(self,"_on_item_dropped")) 11 | 12 | 13 | func _hold_item(_item_id: String) -> void: 14 | _hide_all_items() 15 | # match item_id: 16 | # "pizza_slice": 17 | # pizza_slice.show() 18 | 19 | 20 | func _drop_item(_item_id: String) -> void: 21 | _hide_all_items() 22 | 23 | 24 | func _hide_all_items() -> void: 25 | for child in get_children(): 26 | child.hide() 27 | 28 | 29 | func _on_item_picked_up(item_id: String) -> void: 30 | _hold_item(item_id) 31 | 32 | 33 | func _on_item_dropped(item_id: String) -> void: 34 | _drop_item(item_id) 35 | -------------------------------------------------------------------------------- /actors/player/input_axis.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | # Updates input axis directions based checked input actions 3 | 4 | const AXIS_LENGTH_MIN := -1 5 | const AXIS_LENGTH_MAX := 1 6 | 7 | @export var _up_action := "ui_up" 8 | @export var _down_action := "ui_down" 9 | @export var _left_action := "ui_left" 10 | @export var _right_action := "ui_right" 11 | 12 | var fake_direction: Vector2 : set = set_fake_direction # nullable Vector2 13 | var _up := 0.0 14 | var _down := 0.0 15 | var _left := 0.0 16 | var _right := 0.0 17 | 18 | 19 | func _unhandled_input(event: InputEvent): 20 | if event.is_action_type(): 21 | if event.is_action(self._up_action): 22 | self._up = event.get_action_strength(self._up_action) 23 | if event.is_action(self._down_action): 24 | self._down = event.get_action_strength(self._down_action) 25 | if event.is_action(self._left_action): 26 | self._left = event.get_action_strength(self._left_action) 27 | if event.is_action(self._right_action): 28 | self._right = event.get_action_strength(self._right_action) 29 | 30 | 31 | func get_raw_direction() -> Vector2: 32 | return ( 33 | self.fake_direction 34 | if self.fake_direction != null 35 | else Vector2(self._right - self._left, self._down - self._up) 36 | ) 37 | 38 | 39 | # Sets `fake_direction` to `direction` if `direction` is a `Vector2`, otherwise `null` 40 | func set_fake_direction(direction): 41 | fake_direction = ( 42 | Vector2( 43 | clamp(direction.x, AXIS_LENGTH_MIN, AXIS_LENGTH_MAX), clamp(direction.y, AXIS_LENGTH_MIN, AXIS_LENGTH_MAX) 44 | ) 45 | if direction is Vector2 46 | else null 47 | ) 48 | -------------------------------------------------------------------------------- /actors/player/input_direction.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | # Returns move and look directions based checked input axes 3 | 4 | const InputAxis := preload("input_axis.gd") 5 | 6 | @onready var _axis_move_joy := $MoveJoy as InputAxis 7 | @onready var _axis_move_key := $MoveKey as InputAxis 8 | @onready var _axis_look_joy := $LookJoy as InputAxis 9 | 10 | 11 | func get_look_direction() -> Vector2: 12 | return self._axis_look_joy.get_raw_direction().normalized() 13 | 14 | 15 | func get_move_direction() -> Vector2: 16 | return (self._axis_move_key.get_raw_direction() + self._axis_move_joy.get_raw_direction()).normalized() 17 | 18 | 19 | func get_axis_look_joy() -> InputAxis: 20 | return self._axis_look_joy 21 | 22 | 23 | func get_axis_move_joy() -> InputAxis: 24 | return self._axis_move_joy 25 | -------------------------------------------------------------------------------- /actors/player/input_direction.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://actors/player/input_axis.gd" type="Script" id=1] 4 | [ext_resource path="res://actors/player/input_direction.gd" type="Script" id=2] 5 | 6 | [node name="InputDirection" type="Node"] 7 | script = ExtResource( 2 ) 8 | 9 | [node name="MoveJoy" type="Node" parent="."] 10 | script = ExtResource( 1 ) 11 | __meta__ = { 12 | "_editor_description_": "Needs to be calculated seperately from keyboard to not have joypad events overwrite keyboard ones" 13 | } 14 | _up_action = "move_forward" 15 | _down_action = "move_back" 16 | _left_action = "move_left" 17 | _right_action = "move_right" 18 | 19 | [node name="MoveKey" type="Node" parent="."] 20 | script = ExtResource( 1 ) 21 | __meta__ = { 22 | "_editor_description_": "Needs to be calculated seperately from joypad to not have joypad events overwrite keyboard ones" 23 | } 24 | _up_action = "move_forward_k" 25 | _down_action = "move_back_k" 26 | _left_action = "move_left_k" 27 | _right_action = "move_right_k" 28 | 29 | [node name="LookJoy" type="Node" parent="."] 30 | script = ExtResource( 1 ) 31 | _up_action = "look_up" 32 | _down_action = "look_down" 33 | _left_action = "look_left" 34 | _right_action = "look_right" 35 | -------------------------------------------------------------------------------- /actors/player/interact_box.gd: -------------------------------------------------------------------------------- 1 | extends Area3D 2 | 3 | signal interacted_with 4 | 5 | @export var is_active := true : set = set_is_active 6 | @export var one_shot := false 7 | 8 | @onready var _active_collision_layer_bit := collision_layer 9 | 10 | 11 | func _on_ready(): 12 | # call setter checked ready (doesn't seem to work in a `tool` script) 13 | self.is_active = self.is_active 14 | 15 | 16 | func on_interacted_with(): 17 | # print("[DEBUG] %s HAS BEEN INTERACTED WITH" % name) 18 | emit_signal("interacted_with") 19 | 20 | if self.one_shot: 21 | self.is_active = false 22 | 23 | 24 | func set_is_active(new_active: bool): 25 | is_active = new_active 26 | collision_layer = self._active_collision_layer_bit if new_active else 0 27 | 28 | 29 | func get_active_collision_layer_bit() -> int: 30 | return self._active_collision_layer_bit 31 | -------------------------------------------------------------------------------- /actors/player/mouse_grabber.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | # Handles capturing and releasing of the mouse, primarily through user input. 3 | # Designed to be used to intercept mouse input before it reaches the player's character controller, in order to lessen responsibility. 4 | 5 | signal mouse_grabbed(is_grabbed) 6 | 7 | # The input action name to watch for triggering a release. 8 | @export var _release_action_name := "release_mouse" 9 | 10 | # Controls whether automatically grab focus checked `_ready()`. 11 | # Typically, this is desired behavior. 12 | @export var _should_grab_on_ready := true 13 | 14 | # Forces mouse to be released when focus is lost. 15 | # Useful for triggering callbacks, such as automatically displaying a pause screen when the player alt-tabs. 16 | @export var _should_stay_released_on_refocus := true 17 | 18 | var is_mouse_grabbed := false : get = get_mouse_grabbed, set = set_mouse_grabbed 19 | 20 | func _ready(): 21 | # setgets aren't called with onready 22 | if self._should_grab_on_ready and not OS.get_name() == "HTML5": 23 | self.is_mouse_grabbed = true 24 | 25 | 26 | func _notification(what: int): 27 | var is_focus_lost := what == NOTIFICATION_APPLICATION_FOCUS_OUT 28 | if is_focus_lost and self._should_stay_released_on_refocus: 29 | self.is_mouse_grabbed = false 30 | 31 | 32 | func _unhandled_input(_event: InputEvent): 33 | if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) and not self.is_mouse_grabbed: 34 | self.is_mouse_grabbed = true 35 | get_viewport().set_input_as_handled() 36 | 37 | elif Input.is_action_just_pressed(self._release_action_name) and self.is_mouse_grabbed: 38 | self.is_mouse_grabbed = false 39 | get_viewport().set_input_as_handled() 40 | 41 | 42 | func set_mouse_grabbed(is_grabbed: bool): 43 | is_mouse_grabbed = is_grabbed 44 | emit_signal("mouse_grabbed", is_grabbed) 45 | _update_mouse_mode(is_grabbed) 46 | 47 | 48 | func get_mouse_grabbed() -> bool: 49 | return Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED 50 | 51 | 52 | # Stub this to avoid side-effects in testing 53 | func _update_mouse_mode(is_grabbed: bool): 54 | Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED if is_grabbed else Input.MOUSE_MODE_VISIBLE) 55 | -------------------------------------------------------------------------------- /actors/player/orbit_point.gd: -------------------------------------------------------------------------------- 1 | extends Marker3D 2 | 3 | 4 | @export_range(1.0, 4.0, 0.5) var rotation_speed: float = 2.0 5 | 6 | 7 | func _process(delta: float) -> void: 8 | rotate_y(rotation_speed * delta) 9 | -------------------------------------------------------------------------------- /actors/player/player_base_state.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends State 3 | 4 | 5 | # FUNCTIONS AVAILABLE TO INHERIT 6 | 7 | func _on_enter(_args) -> void: 8 | pass 9 | 10 | func _after_enter(_args) -> void: 11 | pass 12 | 13 | func _on_update(delta) -> void: 14 | _calculate_gravity(delta) 15 | # _calculate_movement(delta) 16 | 17 | func _after_update(_delta) -> void: 18 | pass 19 | 20 | func _before_exit(_args) -> void: 21 | pass 22 | 23 | func _on_exit(_args) -> void: 24 | pass 25 | 26 | func _on_timeout(_name) -> void: 27 | pass 28 | 29 | 30 | func _input(event: InputEvent) -> void: 31 | if event.is_action_pressed("attack"): 32 | change_state("Attack") 33 | elif event.is_action_pressed("rotate_camera_right"): 34 | target.rotate_camera_right() 35 | elif event.is_action_pressed("rotate_camera_left"): 36 | target.rotate_camera_left() 37 | 38 | 39 | func _calculate_gravity(delta: float) -> void: 40 | var current_gravity 41 | current_gravity = target.GROUND_GRAVITY if target.is_on_floor() else target.AIR_GRAVITY 42 | target.velocity.y += delta * current_gravity 43 | 44 | 45 | #func _calculate_movement(delta: float) -> void: 46 | # if direction.x != 0 or direction.y != 0: 47 | # velocity.x = lerp(velocity.x, direction.x * max_speed, GROUND_ACCELERATION * delta) 48 | # velocity.z = lerp(velocity.z, direction.y * max_speed, GROUND_ACCELERATION * delta) 49 | # return 50 | # 51 | # velocity.x = lerp(velocity.x, 0, FRICTION) 52 | # velocity.z = lerp(velocity.z, 0, FRICTION) 53 | -------------------------------------------------------------------------------- /actors/player/player_idle_state.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends State 3 | 4 | 5 | # FUNCTIONS AVAILABLE TO INHERIT 6 | 7 | func _on_enter(_args) -> void: 8 | play("Idle") 9 | 10 | func _after_enter(_args) -> void: 11 | pass 12 | 13 | func _on_update(_delta) -> void: 14 | pass 15 | 16 | func _after_update(_delta) -> void: 17 | _check_transition() 18 | 19 | func _before_exit(_args) -> void: 20 | pass 21 | 22 | func _on_exit(_args) -> void: 23 | pass 24 | 25 | func _on_timeout(_name) -> void: 26 | pass 27 | 28 | 29 | func _check_transition() -> void: 30 | if not target.direction == Vector3.ZERO: 31 | change_state("Walk") 32 | -------------------------------------------------------------------------------- /actors/player/player_walk_state.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends State 3 | 4 | 5 | # FUNCTIONS AVAILABLE TO INHERIT 6 | 7 | func _on_enter(_args) -> void: 8 | play("Walk") 9 | 10 | 11 | func _after_enter(_args) -> void: 12 | pass 13 | 14 | func _on_update(_delta) -> void: 15 | pass 16 | 17 | func _after_update(_delta) -> void: 18 | _check_transition() 19 | 20 | func _before_exit(_args) -> void: 21 | pass 22 | 23 | func _on_exit(_args) -> void: 24 | pass 25 | 26 | func _on_timeout(_name) -> void: 27 | pass 28 | 29 | 30 | func _check_transition() -> void: 31 | if target.direction == Vector2.ZERO and target.velocity.length() < 0.1: 32 | change_state("Idle") 33 | -------------------------------------------------------------------------------- /actors/player/soul_orb.gd: -------------------------------------------------------------------------------- 1 | extends MeshInstance3D 2 | -------------------------------------------------------------------------------- /actors/player/tpcc_test.gd: -------------------------------------------------------------------------------- 1 | extends CharacterBody3D 2 | 3 | 4 | @onready var head = $HEAD 5 | 6 | @export var speed : float = 10 7 | @export var gravity : float = 40 8 | @export var ground_accel : float = 8 9 | @export var air_accel : float = 2 10 | @export var jump_force : float = 20 11 | @export var friction : float = 0.25 12 | 13 | var dir : Vector3 14 | var vel : Vector3 15 | 16 | 17 | func _process(delta): 18 | dir.x = Input.get_action_strength("LEFT") - Input.get_action_strength("RIGHT") 19 | dir.z = Input.get_action_strength("FORWARD") - Input.get_action_strength("BACKWARD") 20 | dir = dir.rotated(Vector3.UP, head.rotation.y).normalized() 21 | 22 | 23 | func _physics_process(delta): 24 | grav(delta) 25 | jump(delta) 26 | mov(delta) 27 | 28 | 29 | func grav(delta): 30 | vel.y -= gravity * delta 31 | set_velocity(vel) 32 | set_up_direction(Vector3.UP) 33 | move_and_slide() 34 | vel = velocity 35 | 36 | 37 | func mov(delta): 38 | if is_on_floor(): 39 | if dir.x != 0 or dir.z != 0: 40 | vel.x = lerp(vel.x, dir.x * speed, ground_accel * delta) 41 | vel.z = lerp(vel.z, dir.z * speed, ground_accel * delta) 42 | else: 43 | vel.x = lerp(vel.x, 0, friction) 44 | vel.z = lerp(vel.z, 0, friction) 45 | else: 46 | if dir.x != 0 or dir.z != 0: 47 | vel.x = lerp(vel.x, dir.x * speed, air_accel * delta) 48 | vel.z = lerp(vel.z, dir.z * speed, air_accel * delta) 49 | 50 | 51 | func jump(delta): 52 | if is_on_floor(): 53 | if Input.is_action_just_pressed("JUMP"): 54 | vel.y = jump_force 55 | -------------------------------------------------------------------------------- /actors/test_flash.gd: -------------------------------------------------------------------------------- 1 | extends Node3D 2 | 3 | var _white_tex := preload("res://materials/textures/white_square.png") 4 | var _green_tex := preload("res://materials/textures/GreenDemon_Texture.png") 5 | 6 | var _flashing := false 7 | 8 | @onready var _mesh := $Demon/MonsterArmature/Skeleton3D/Demon001 9 | 10 | 11 | func _ready() -> void: 12 | $Timer.connect("timeout",Callable(self,"_flash")) 13 | 14 | 15 | func _flash() -> void: 16 | var new_tex 17 | if _flashing: 18 | new_tex = _green_tex 19 | else: 20 | new_tex = _white_tex 21 | 22 | _mesh.mesh.surface_get_material(0).set_shader_parameter("albedoTex", new_tex) 23 | _flashing = not _flashing 24 | -------------------------------------------------------------------------------- /actors/top_down_base_state.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | 3 | 4 | # FUNCTIONS AVAILABLE TO INHERIT 5 | 6 | func _on_enter(_args) -> void: 7 | pass 8 | 9 | func _after_enter() -> void: 10 | pass 11 | 12 | func _on_update(delta) -> void: 13 | var current_gravity 14 | current_gravity = target.GROUND_GRAVITY if target.is_on_floor() else target.AIR_GRAVITY 15 | target.velocity.y += delta * current_gravity 16 | 17 | func _after_update(_delta) -> void: 18 | pass 19 | 20 | func _before_exit() -> void: 21 | pass 22 | 23 | func _on_exit(_args) -> void: 24 | pass 25 | 26 | func _on_timeout(_name) -> void: 27 | pass 28 | -------------------------------------------------------------------------------- /actors/top_down_move_state.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends AnimationState 3 | 4 | 5 | func _on_enter(_args) -> void: 6 | target.velocity = Vector3.ZERO 7 | 8 | func _after_enter() -> void: 9 | pass 10 | 11 | func _on_update(_delta) -> void: 12 | pass 13 | 14 | func _after_update(delta) -> void: 15 | if target.get_horizontal_speed() > 0.1: 16 | target.mesh_container.rotation.y = lerp_angle(target.mesh_container.rotation.y, atan2(-target.velocity.x, -target.velocity.z), delta * target.ANGULAR_ACCELERATION) 17 | # if not is_playing("Walk"): play("Walk") 18 | 19 | target.set_velocity(target.velocity) 20 | target.set_up_direction(Vector3.UP) 21 | target.move_and_slide() 22 | target.velocity = target.velocity 23 | target.navigation_agent.set_velocity(target.velocity) 24 | 25 | func _before_exit() -> void: 26 | pass 27 | 28 | func _on_exit(_args) -> void: 29 | pass 30 | 31 | func _on_timeout(_name) -> void: 32 | pass 33 | -------------------------------------------------------------------------------- /icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://ccg7oxvj0iowx" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /levels/area_container.gd: -------------------------------------------------------------------------------- 1 | class_name AreaContainer 2 | extends Node3D 3 | 4 | ## A container for areas in levels. 5 | ## 6 | ## Put Area3D's as children of SafetyNetContainer and they will call 7 | ## teleport_player_to_safety() in PlayerContainer. I use this for places a 8 | ## player could fall into infinity. 9 | 10 | 11 | @onready var _safety_net_container := $SafetyNetContainer 12 | 13 | var _safety_nets: Array 14 | 15 | 16 | func _ready() -> void: 17 | var safety_net_children = _safety_net_container.get_children() 18 | if not safety_net_children.is_empty(): 19 | 20 | for n in safety_net_children.size(): 21 | _safety_nets.append( safety_net_children[n] ) 22 | 23 | 24 | ## This is called by [Level] when it is connecting signals from the areas in 25 | ## the SafetyNetContainer. 26 | func get_safety_nets() -> Array: 27 | return _safety_nets 28 | -------------------------------------------------------------------------------- /levels/example/geometry/geometry_example_1.gd: -------------------------------------------------------------------------------- 1 | extends Node3D 2 | 3 | ## This script is just so that I could see the inside of the level in the 4 | ## editor and then show the ceiling when the game is running. 5 | 6 | 7 | func _ready() -> void: 8 | $Ceiling.show() 9 | -------------------------------------------------------------------------------- /levels/example/geometry/geometry_example_1.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://dxx63vu3xoado"] 2 | 3 | [ext_resource type="Script" path="res://levels/example/geometry/geometry_example_1.gd" id="1_h7aop"] 4 | [ext_resource type="PackedScene" uid="uid://djbdcyxoi76xn" path="res://levels/example/geometry/5x_5_tile.tscn" id="2_jagab"] 5 | 6 | [node name="GeometryExample1" type="Node3D"] 7 | script = ExtResource("1_h7aop") 8 | 9 | [node name="5x5Tile" parent="." instance=ExtResource("2_jagab")] 10 | 11 | [node name="Ceiling" parent="." instance=ExtResource("2_jagab")] 12 | transform = Transform3D(-1, 8.74228e-08, 0, -8.74228e-08, -1, 0, 0, 0, 1, 0, 20, 0) 13 | visible = false 14 | 15 | [node name="5x5Tile2" parent="." instance=ExtResource("2_jagab")] 16 | transform = Transform3D(-4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0, 1, 10, 10, 0) 17 | 18 | [node name="5x5Tile3" parent="." instance=ExtResource("2_jagab")] 19 | transform = Transform3D(4.37114e-08, 1, -8.74228e-08, 1, -4.37114e-08, 0, -3.82137e-15, -8.74228e-08, -1, -10, 10, 0) 20 | 21 | [node name="5x5Tile4" parent="." instance=ExtResource("2_jagab")] 22 | transform = Transform3D(1, -4.37114e-08, 4.37114e-08, -4.37114e-08, 1.31134e-07, 1, -4.37114e-08, -1, 1.31134e-07, 0, 10, 10) 23 | 24 | [node name="5x5Tile5" parent="." instance=ExtResource("2_jagab")] 25 | transform = Transform3D(-1, 1.31134e-07, -4.37114e-08, -4.37114e-08, 1.31134e-07, 1, 1.31134e-07, 1, -1.31134e-07, 0, 10, -10) 26 | 27 | [node name="5x5Tile6" parent="." instance=ExtResource("2_jagab")] 28 | transform = Transform3D(-1, 1.31134e-07, -4.37114e-08, -4.37114e-08, 1.31134e-07, 1, 1.31134e-07, 1, -1.31134e-07, 0, 10, -10) 29 | -------------------------------------------------------------------------------- /levels/example/geometry/sprite_3d_barrel.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cvyagwmgkm00v"] 2 | 3 | [sub_resource type="CompressedTexture2D" id="CompressedTexture2D_dxmai"] 4 | load_path = "res://.godot/imported/decorative.png-58f07883c9c2143dae59c57ab5b0405c.s3tc.ctex" 5 | 6 | [sub_resource type="AtlasTexture" id="AtlasTexture_4gnug"] 7 | atlas = SubResource("CompressedTexture2D_dxmai") 8 | region = Rect2(144, 172, 16, 20) 9 | 10 | [node name="Sprite3DBarrel" type="Node3D"] 11 | 12 | [node name="Sprite3D" type="Sprite3D" parent="."] 13 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) 14 | pixel_size = 0.1 15 | billboard = 2 16 | texture_filter = 0 17 | texture = SubResource("AtlasTexture_4gnug") 18 | -------------------------------------------------------------------------------- /levels/example/geometry/sprite_3d_pillar.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://bbychmkacijcm"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://sk2w5b38nhgt" path="res://materials/textures/decorative.png" id="1_shg8f"] 4 | 5 | [sub_resource type="AtlasTexture" id="AtlasTexture_7bpij"] 6 | atlas = ExtResource("1_shg8f") 7 | region = Rect2(17, 16, 13, 48) 8 | 9 | [node name="Sprite3DPillar" type="Node3D"] 10 | 11 | [node name="Sprite3D" type="Sprite3D" parent="."] 12 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.44301, 0) 13 | pixel_size = 0.1 14 | billboard = 2 15 | texture_filter = 0 16 | texture = SubResource("AtlasTexture_7bpij") 17 | -------------------------------------------------------------------------------- /levels/example/geometry/wall_dungeon_1x2.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3] 2 | 3 | [ext_resource type="Shader" path="res://shaders/gpsx/3d/gpsx_3d.gdshader" id="1_nq11h"] 4 | [ext_resource type="Texture2D" uid="uid://ixemw6m2rw56" path="res://materials/textures/tile.png" id="2_03cgs"] 5 | 6 | [sub_resource type="BoxMesh" id="BoxMesh_lpmdv"] 7 | size = Vector3(2, 2, 2) 8 | subdivide_width = 1 9 | subdivide_height = 1 10 | subdivide_depth = 1 11 | 12 | [sub_resource type="ShaderMaterial" id="ShaderMaterial_csdgi"] 13 | render_priority = 0 14 | shader = ExtResource("1_nq11h") 15 | shader_parameter/psx_fixed_point_precision = 48.16 16 | shader_parameter/dithering = true 17 | shader_parameter/banding = true 18 | shader_parameter/fog = true 19 | shader_parameter/fog_color = Color(0, 0, 0, 1) 20 | shader_parameter/dist_fade_min = 15.0 21 | shader_parameter/dist_fade_max = 20.0 22 | shader_parameter/alpha_cutoff = 0.1 23 | shader_parameter/tex = ExtResource("2_03cgs") 24 | 25 | [node name="WallDungeon1x2" type="Node3D"] 26 | 27 | [node name="CSGMesh3D" type="CSGMesh3D" parent="."] 28 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 1) 29 | mesh = SubResource("BoxMesh_lpmdv") 30 | material = SubResource("ShaderMaterial_csdgi") 31 | 32 | [node name="CSGMesh3D2" type="CSGMesh3D" parent="."] 33 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, -1) 34 | mesh = SubResource("BoxMesh_lpmdv") 35 | material = SubResource("ShaderMaterial_csdgi") 36 | 37 | [node name="CSGMesh3D3" type="CSGMesh3D" parent="."] 38 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, -1) 39 | mesh = SubResource("BoxMesh_lpmdv") 40 | material = SubResource("ShaderMaterial_csdgi") 41 | 42 | [node name="CSGMesh3D4" type="CSGMesh3D" parent="."] 43 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1) 44 | mesh = SubResource("BoxMesh_lpmdv") 45 | material = SubResource("ShaderMaterial_csdgi") 46 | -------------------------------------------------------------------------------- /levels/example/geometry/water_box.gd: -------------------------------------------------------------------------------- 1 | extends Node3D 2 | 3 | 4 | @export var water_color: Color = Color() 5 | -------------------------------------------------------------------------------- /levels/example/geometry/water_box.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3] 2 | 3 | [ext_resource type="Shader" path="res://shaders/gpsx/3d/gpsx_3d_qadd.gdshader" id="2_mm8me"] 4 | 5 | [sub_resource type="BoxMesh" id="BoxMesh_uw8la"] 6 | size = Vector3(8, 4, 4) 7 | subdivide_width = 1 8 | subdivide_height = 1 9 | subdivide_depth = 1 10 | 11 | [sub_resource type="ShaderMaterial" id="ShaderMaterial_s8yd5"] 12 | render_priority = 0 13 | shader = ExtResource("2_mm8me") 14 | shader_parameter/psx_fixed_point_precision = 48.16 15 | shader_parameter/dithering = true 16 | shader_parameter/banding = true 17 | shader_parameter/fog = true 18 | shader_parameter/fog_color = Color(0, 0, 0, 1) 19 | shader_parameter/dist_fade_min = 10.0 20 | shader_parameter/dist_fade_max = 20.0 21 | shader_parameter/alpha_cutoff = 0.1 22 | 23 | [node name="WaterBox" type="Node3D"] 24 | 25 | [node name="CSGMesh" type="CSGMesh3D" parent="."] 26 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2, 0) 27 | instance_shader_parameters/mixer = Color(0.14902, 0.619608, 0.662745, 1) 28 | mesh = SubResource("BoxMesh_uw8la") 29 | material = SubResource("ShaderMaterial_s8yd5") 30 | -------------------------------------------------------------------------------- /levels/example/launcher_example.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends Launcher 3 | 4 | ## This is an example of how to extend the launcher and override the necessary 5 | ## functions. 6 | 7 | var _selection_made: bool = false 8 | 9 | 10 | ## This is the function that will be called if the top selection is chosen. 11 | func handle_first_selection() -> void: 12 | if _selection_made: return 13 | 14 | SignalManager.set_window_mode.emit(Global.WINDOW_MODES.FULLSCREEN) 15 | _selection_made = true 16 | 17 | 18 | ## This is the function that will be called if the middle selection is chosen. 19 | func handle_second_selection() -> void: 20 | if _selection_made: return 21 | 22 | SignalManager.set_window_mode.emit(Global.WINDOW_MODES.WINDOWED_MED) 23 | _selection_made = true 24 | -------------------------------------------------------------------------------- /levels/example/launcher_example.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://difhaik1vv3o"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://dud7cgk12ctah" path="res://levels/launcher.tscn" id="1_dfn2o"] 4 | [ext_resource type="Script" path="res://levels/example/launcher_example.gd" id="2_eefnp"] 5 | 6 | [node name="LauncherExample" instance=ExtResource("1_dfn2o")] 7 | script = ExtResource("2_eefnp") 8 | background_color = Color(0.239216, 0.101961, 0.219608, 1) 9 | 10 | [node name="BackgroundColorRect" parent="." index="0"] 11 | color = Color(0.239216, 0.101961, 0.219608, 1) 12 | 13 | [node name="MenuSelectionContainer" parent="CenterContainer" index="0"] 14 | title = "Example Game" 15 | 16 | [node name="Label" type="Label" parent="CenterContainer/MenuSelectionContainer/FirstSelection" index="2"] 17 | layout_mode = 2 18 | text = " (reccomended)" 19 | -------------------------------------------------------------------------------- /levels/example/level_example_2.gd: -------------------------------------------------------------------------------- 1 | # meta_description: Contains all of the functions you should override 2 | # meta_default: true 3 | 4 | 5 | extends Level3D 6 | 7 | 8 | const NEXT_SCENE = Global.LEVELS.TEST 9 | const MINIMUM_TRANSITION_SECONDS = 3.0 10 | const MAXIMUM_TRANSITION_SECONDS = 6.0 11 | 12 | var _level_default_shaders: Array[int] = [ 13 | Global.SHADERS.SHARPNESS 14 | ] 15 | var camera_index: int = 0 16 | 17 | @onready var _ui_level_controls: UILevelControls = $UILevelControls 18 | @onready var _cameras: Array = [ 19 | $PlayerContainer/Camera3D, 20 | $PlayerContainer/Camera3D5, 21 | $PlayerContainer/Camera3D2, 22 | $PlayerContainer/Camera3D3, 23 | $PlayerContainer/Camera3D4, 24 | ] 25 | 26 | 27 | func _ready() -> void: 28 | super() 29 | _activate_shaders() 30 | SignalManager.camera_transition_finished.connect(_on_camera_transition_finished) 31 | SignalManager.camera_cut_requested.emit(_cameras[camera_index]) 32 | _transition_to_next_camera() 33 | 34 | 35 | func _input(event: InputEvent) -> void: 36 | _ui_level_controls.handle_input(event) 37 | 38 | 39 | func favorite_scene() -> void: 40 | ResourceManager.add_global_data("favorite_scene", level_id) 41 | 42 | 43 | func change_to_next_scene() -> void: 44 | SignalManager.change_scene_requested.emit(NEXT_SCENE) 45 | 46 | 47 | func _transition_to_next_camera() -> void: 48 | camera_index += 1 49 | 50 | if camera_index >= _cameras.size(): 51 | camera_index = 0 52 | 53 | var transition_seconds: float = randf_range(MINIMUM_TRANSITION_SECONDS, MAXIMUM_TRANSITION_SECONDS) 54 | 55 | SignalManager.camera_transition_requested.emit(_cameras[camera_index], transition_seconds) 56 | 57 | 58 | func _activate_shaders() -> void: 59 | for shader in _level_default_shaders: 60 | SignalManager.pp_enabled_changed.emit(shader, true) 61 | 62 | 63 | func _connect_signals() -> void: 64 | super() 65 | _ui_level_controls.next_scene_requested.connect(change_to_next_scene) 66 | _ui_level_controls.favorite_scene_requested.connect(favorite_scene) 67 | SignalManager.game_save_load_finished.connect(_on_game_save_loaded) 68 | 69 | 70 | func _on_camera_transition_finished() -> void: 71 | _transition_to_next_camera() 72 | 73 | 74 | func _on_game_save_loaded() -> void: 75 | var favorite_scene_id: Global.LEVELS = ResourceManager.get_global_data("favorite_scene") 76 | if not favorite_scene_id: 77 | favorite_scene_id = Global.LEVELS.TEST 78 | 79 | SignalManager.change_scene_requested.emit(favorite_scene_id) 80 | -------------------------------------------------------------------------------- /levels/example/level_example_2.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://cxyjn2dechgaj"] 2 | 3 | [ext_resource type="PackedScene" uid="uid://e4biybi2mnck" path="res://levels/level_3d.tscn" id="1_p6i4b"] 4 | [ext_resource type="Script" path="res://levels/example/level_example_2.gd" id="2_7xdr3"] 5 | [ext_resource type="PackedScene" path="res://levels/example/geometry/geometry_example_2.tscn" id="3_rxxrd"] 6 | [ext_resource type="PackedScene" uid="uid://ebtmki4hbuka" path="res://ui/ui_level_controls.tscn" id="4_2pg6w"] 7 | 8 | [node name="LevelExample2" instance=ExtResource("1_p6i4b")] 9 | script = ExtResource("2_7xdr3") 10 | level_id = 3 11 | use_default_shaders = false 12 | starting_music_id = 2 13 | 14 | [node name="Camera3D" parent="PlayerContainer" index="0"] 15 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 6.437, 1.5, -9) 16 | 17 | [node name="Camera3D5" type="Camera3D" parent="PlayerContainer" index="2"] 18 | transform = Transform3D(1.91069e-15, -1, -4.37114e-08, -4.37114e-08, -4.37114e-08, 1, -1, 0, -4.37114e-08, -6, 5.5, -12) 19 | 20 | [node name="Camera3D2" type="Camera3D" parent="PlayerContainer" index="3"] 21 | transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 9.1606, 0.5, -14.6057) 22 | 23 | [node name="Camera3D3" type="Camera3D" parent="PlayerContainer" index="4"] 24 | transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -28.6733, 1, -14.4839) 25 | 26 | [node name="Camera3D4" type="Camera3D" parent="PlayerContainer" index="5"] 27 | transform = Transform3D(0.7674, 0, -0.641168, 0, 1, 0, 0.641168, 0, 0.7674, -23.9128, 1, -12.7898) 28 | 29 | [node name="GeometryExample2" parent="GeometryContainer" index="0" instance=ExtResource("3_rxxrd")] 30 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -17) 31 | 32 | [node name="UILevelControls" parent="." index="9" instance=ExtResource("4_2pg6w")] 33 | -------------------------------------------------------------------------------- /levels/example/level_example_3.gd: -------------------------------------------------------------------------------- 1 | # meta_description: Contains all of the functions you should override 2 | # meta_default: true 3 | 4 | 5 | extends Level 6 | 7 | 8 | enum LEVEL_TYPE {PEACEFUL, ADVENTURE} 9 | 10 | ## This is the name of the level/scene. This is primarily used in conjunction 11 | ## with [member show_location_name] to show the text of the location in the 12 | ## center of the screen. 13 | @export var location_name: String 14 | ## If this value is set to true, text displaying [member location_name] will 15 | ## appear in the center of the screen when the level loads. 16 | @export var show_location_name: bool = false 17 | 18 | 19 | func _ready() -> void: 20 | super() 21 | 22 | if not show_location_name: 23 | return 24 | 25 | _show_location_name() 26 | 27 | var previous_scene_id = ResourceManager.get_global_data("previous_scene_id") 28 | if previous_scene_id: 29 | initialize_level(previous_scene_id) 30 | 31 | 32 | ## Shows the text of [member location_name] in the center of the screen. 33 | func _show_location_name() -> void: 34 | SignalManager.location_entered.emit(location_name) 35 | 36 | 37 | ## This is the logic that will be called with the previous scenes id from 38 | ## [member Glbl.LEVELS] so you can have run logic based on the previous 39 | ## level/scene. Keep in mind that this runs after [method ready]. 40 | func initialize_level(previous_scene_id: Global.LEVELS) -> void: 41 | _player_container.move_player_to_spawn_position(previous_scene_id) 42 | 43 | 44 | func _heal_player() -> void: 45 | SignalManager.emit_signal("player_healed") 46 | -------------------------------------------------------------------------------- /levels/example/main_menu_example.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | 3 | 4 | enum MENUS { 5 | BASE, 6 | LOAD, 7 | OPTION, 8 | } 9 | 10 | const BASE_START_SELECTION = "Start" 11 | const BASE_LOAD_SELECTION = "Load game" 12 | const BASE_OPTIONS_SELECTION = "Options" 13 | const BASE_QUIT_SELECTION = "Exit" 14 | const LOAD_SLOT_1_SELECTION = "1" 15 | const LOAD_SLOT_2_SELECTION = "2" 16 | const LOAD_SLOT_3_SELECTION = "3" 17 | const LOAD_BACK_SELECTION = "Back" 18 | 19 | @export var starting_level_id: Global.LEVELS = Global.LEVELS.TEST 20 | 21 | var _current_menu_id: int = MENUS.BASE 22 | var _finished_selection: bool = false 23 | 24 | @onready var _menu_timer: Timer = $MenuTimer 25 | @onready var _selection_containers: Dictionary = { 26 | MENUS.BASE: $Menus/MarginContainer/BaseMenuSelectionContainer, 27 | MENUS.LOAD: $Menus/MarginContainer/LoadMenuSelectionContainer 28 | } 29 | 30 | 31 | func _ready() -> void: 32 | _menu_timer.wait_time = SelectionContainer.TRANS_DURATION + 0.2 33 | _connect_signals() 34 | _show_menu() 35 | 36 | 37 | func _switch_menu(new_menu_id: int) -> void: 38 | _finished_selection = false 39 | if _current_menu_id == new_menu_id: 40 | return 41 | 42 | var current_selection_container: SelectionContainer = _selection_containers[_current_menu_id] 43 | var new_selection_container: SelectionContainer = _selection_containers[new_menu_id] 44 | current_selection_container.menu_available = false 45 | await current_selection_container.transition_finished 46 | 47 | _current_menu_id = new_menu_id 48 | new_selection_container.menu_available = true 49 | 50 | 51 | func _show_menu() -> void: 52 | _switch_menu(MENUS.BASE) 53 | 54 | 55 | func _on_base_option_confirmed(selection_name: String) -> void: 56 | match selection_name: 57 | BASE_START_SELECTION: 58 | SignalManager.change_scene_requested.emit(starting_level_id) 59 | BASE_LOAD_SELECTION: 60 | _switch_menu(MENUS.LOAD) 61 | BASE_QUIT_SELECTION: 62 | get_tree().quit() 63 | _: 64 | return 65 | 66 | _finished_selection = true 67 | 68 | 69 | func _on_load_selection_confirmed(selection_name: String) -> void: 70 | match selection_name: 71 | LOAD_SLOT_1_SELECTION: SignalManager.game_save_load_requested.emit(1) 72 | LOAD_SLOT_2_SELECTION: SignalManager.game_save_load_requested.emit(2) 73 | LOAD_SLOT_3_SELECTION: SignalManager.game_save_load_requested.emit(3) 74 | LOAD_BACK_SELECTION: _switch_menu(MENUS.BASE) 75 | _: return 76 | 77 | _finished_selection = true 78 | 79 | 80 | func _connect_signals() -> void: 81 | var base_selection_container: SelectionContainer = _selection_containers[MENUS.BASE] 82 | base_selection_container.selection_confirmed.connect(_on_base_option_confirmed) 83 | var load_selection_container: SelectionContainer = _selection_containers[MENUS.LOAD] 84 | load_selection_container.selection_confirmed.connect(_on_load_selection_confirmed) 85 | -------------------------------------------------------------------------------- /levels/example/tool_color_rect.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends ColorRect 3 | 4 | ## I only created this node 5 | -------------------------------------------------------------------------------- /levels/intro/made_with_tools.gd: -------------------------------------------------------------------------------- 1 | class_name MadeWithToolsCredit 2 | extends Control 3 | 4 | 5 | signal animation_finished() 6 | 7 | @onready var _animation_player: AnimationPlayer = $AnimationPlayer 8 | 9 | 10 | func play_animation() -> void: 11 | _animation_player.play("godot_cmy_join") 12 | await _animation_player.animation_finished 13 | animation_finished.emit() 14 | -------------------------------------------------------------------------------- /levels/launcher.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3] 2 | 3 | [ext_resource type="Script" path="res://levels/launcher.gd" id="1_8lxr4"] 4 | [ext_resource type="Script" path="res://tools/tool_color_rect.gd" id="2_2nvc5"] 5 | [ext_resource type="PackedScene" uid="uid://ckw4dv0xxi1wh" path="res://ui/menu_selection_container.tscn" id="2_sqnwx"] 6 | [ext_resource type="PackedScene" uid="uid://d32vr0askpuce" path="res://ui/menu_selection.tscn" id="3_p5070"] 7 | 8 | [node name="Launcher" type="Control"] 9 | layout_mode = 3 10 | anchors_preset = 15 11 | anchor_right = 1.0 12 | anchor_bottom = 1.0 13 | grow_horizontal = 2 14 | grow_vertical = 2 15 | script = ExtResource("1_8lxr4") 16 | background_color = Color(0.227451, 0.14902, 0.243137, 1) 17 | 18 | [node name="BackgroundColorRect" type="ColorRect" parent="."] 19 | layout_mode = 1 20 | anchors_preset = 15 21 | anchor_right = 1.0 22 | anchor_bottom = 1.0 23 | grow_horizontal = 2 24 | grow_vertical = 2 25 | color = Color(0.227451, 0.14902, 0.243137, 1) 26 | script = ExtResource("2_2nvc5") 27 | 28 | [node name="CenterContainer" type="CenterContainer" parent="."] 29 | layout_mode = 1 30 | anchors_preset = 15 31 | anchor_right = 1.0 32 | anchor_bottom = 1.0 33 | grow_horizontal = 2 34 | grow_vertical = 2 35 | 36 | [node name="MenuSelectionContainer" parent="CenterContainer" instance=ExtResource("2_sqnwx")] 37 | layout_mode = 2 38 | theme_override_constants/separation = -8 39 | title = "Game Launcher" 40 | title_font_size = 32 41 | 42 | [node name="FirstSelection" parent="CenterContainer/MenuSelectionContainer" instance=ExtResource("3_p5070")] 43 | unique_name_in_owner = true 44 | layout_mode = 2 45 | selection_name = "Begin fullscreen" 46 | 47 | [node name="SecondSelection" parent="CenterContainer/MenuSelectionContainer" instance=ExtResource("3_p5070")] 48 | unique_name_in_owner = true 49 | layout_mode = 2 50 | selection_name = "Begin windowed" 51 | 52 | [node name="ExitSelection" parent="CenterContainer/MenuSelectionContainer" instance=ExtResource("3_p5070")] 53 | unique_name_in_owner = true 54 | layout_mode = 2 55 | selection_name = "Exit" 56 | -------------------------------------------------------------------------------- /levels/level_3d.gd: -------------------------------------------------------------------------------- 1 | class_name Level3D 2 | extends Node3D 3 | 4 | ## A Node which can be used to create inherited scenes as levels for your game 5 | ## 6 | ## You should be able to make most of the generic functionality you need by 7 | ## using the editor for every level/scene that involves gameplay. You should 8 | ## extend this script for each level/scene to have the logic of your 9 | ## level/scene. 10 | 11 | 12 | @export_category("Level info") 13 | ## This value is the id of the level/scene from [member Glbl.LEVELS]. It can be 14 | ## used for other Nodes to know what level/scene this is. I may take this out. 15 | ## Doesn't seem that useful. 16 | @export var level_id: Global.LEVELS = Global.LEVELS.TEST 17 | ## If this value is set to true, the scene will begin with the default shaders 18 | ## set in [GameMngr] active. If it is set to false, no shaders will be shown 19 | ## and you'll have to set them up yourself in the ready function. 20 | @export var use_default_shaders: bool = true 21 | 22 | 23 | @export_category("Sound") 24 | ## This is the starting ambience that will be played on load. The ID's are from 25 | ## [member Glbl.Ambiences] so you will need to have edited that script first. 26 | @export var starting_ambience_id: Global.AMBIENCES = Global.AMBIENCES.NONE 27 | ## This is the id of the music that will be played on load. The ID's are from 28 | ## [member Glbl.MUSIC] so you will need to have edited that script first. 29 | @export var starting_music_id: Global.MUSIC = Global.MUSIC.NONE 30 | ## How loud the starting ambience will be. This is helpful if you have sound 31 | ## files that have irritatingly large differences in volume. 32 | @export_range(-40, 40, 0.5) var starting_ambience_db = 0.0 33 | ## How loud the starting music will be. This is helpful if you have sound 34 | ## files that have irritatingly large differences in volume. 35 | @export_range(-40, 40, 0.5) var starting_music_db = 0.0 36 | 37 | var _current_music_id: int 38 | var _current_ambience_id: int 39 | 40 | @onready var _player_container: PlayerContainer = $PlayerContainer 41 | @onready var _geometry_container: Node3D = $GeometryContainer 42 | @onready var _area_container: AreaContainer = $AreaContainer 43 | @onready var _camera_manager: CameraManager3D = $CameraManager3D 44 | 45 | 46 | func _ready() -> void: 47 | _connect_signals() 48 | process_mode = Node.PROCESS_MODE_PAUSABLE 49 | 50 | SoundManager.play_ambience(starting_ambience_id) 51 | SoundManager.play_music(starting_music_id) 52 | 53 | if use_default_shaders: 54 | SignalManager.pp_default_shaders_enabled_changed.emit(true) 55 | else: 56 | SignalManager.pp_all_disabled.emit() 57 | 58 | 59 | func _connect_signals() -> void: 60 | SignalManager.instance_requested.connect(_on_instance_requested) 61 | var player = _player_container.get_player() 62 | for safety_net in _area_container.get_safety_nets(): 63 | safety_net.player_entered.connect(_player_container.teleport_player_to_safety) 64 | 65 | 66 | func _on_instance_requested(instance_scene: PackedScene, location: Vector3) -> void: 67 | var new_instance = instance_scene.instantiate() 68 | add_child(new_instance) 69 | new_instance.global_transform.origin = location 70 | -------------------------------------------------------------------------------- /levels/level_3d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://e4biybi2mnck"] 2 | 3 | [ext_resource type="Script" path="res://levels/level_3d.gd" id="1"] 4 | [ext_resource type="Script" path="res://levels/player_container.gd" id="4"] 5 | [ext_resource type="Script" path="res://levels/area_container.gd" id="5"] 6 | [ext_resource type="PackedScene" uid="uid://buq1gorj12oov" path="res://management/camera_manager_3d.tscn" id="5_fnww5"] 7 | [ext_resource type="Script" path="res://levels/npc_container.gd" id="6"] 8 | [ext_resource type="Script" path="res://management/game_state_saver.gd" id="7"] 9 | 10 | [node name="Level3D" type="Node3D"] 11 | script = ExtResource("1") 12 | starting_ambience_db = -5.0 13 | 14 | [node name="GameStateSaver" type="Node" parent="."] 15 | script = ExtResource("7") 16 | 17 | [node name="PlayerContainer" type="Node3D" parent="."] 18 | script = ExtResource("4") 19 | 20 | [node name="Camera3D" type="Camera3D" parent="PlayerContainer"] 21 | transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, 0) 22 | current = true 23 | 24 | [node name="PlayerSpawnPositionContainer" type="Node3D" parent="PlayerContainer"] 25 | 26 | [node name="GeometryContainer" type="Node3D" parent="."] 27 | 28 | [node name="CameraContainer" type="Node3D" parent="."] 29 | 30 | [node name="AreaContainer" type="Node3D" parent="."] 31 | script = ExtResource("5") 32 | 33 | [node name="SafetyNetContainer" type="Node3D" parent="AreaContainer"] 34 | 35 | [node name="ExitContainer" type="Node3D" parent="."] 36 | 37 | [node name="NPCContainer" type="Node3D" parent="."] 38 | script = ExtResource("6") 39 | 40 | [node name="Patrols" type="Node3D" parent="NPCContainer"] 41 | 42 | [node name="EnemySpawnPortals" type="Node3D" parent="NPCContainer"] 43 | 44 | [node name="CameraManager3D" parent="." instance=ExtResource("5_fnww5")] 45 | 46 | [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] 47 | transform = Transform3D(0.793642, 0.28199, -0.539087, 0.073095, 0.835479, 0.544639, 0.603979, -0.471653, 0.642459, 0, 10, 0) 48 | light_energy = 0.5 49 | directional_shadow_mode = 0 50 | -------------------------------------------------------------------------------- /levels/npc_container.gd: -------------------------------------------------------------------------------- 1 | class_name NPCContainer 2 | extends Node3D 3 | 4 | 5 | func _ready() -> void: 6 | SignalManager.enemy_spawned.connect(_on_enemy_spawned) 7 | 8 | 9 | # Replace node with enemy class 10 | func _on_enemy_spawned(spawned_enemy: Node) -> void: 11 | add_child(spawned_enemy) 12 | -------------------------------------------------------------------------------- /levels/patrol_path.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://levels/patrol_path.gd" type="Script" id=1] 4 | 5 | [node name="PatrolPath" type="Node3D"] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /levels/player_container.gd: -------------------------------------------------------------------------------- 1 | class_name PlayerContainer 2 | extends Node3D 3 | 4 | 5 | @onready var _player 6 | @onready var _player_spawn_position_container := $PlayerSpawnPositionContainer 7 | 8 | var _player_spawn_positions: Dictionary 9 | 10 | 11 | func _ready() -> void: 12 | SignalManager.enemy_spawned.connect(_on_enemy_spawned) 13 | 14 | _add_player_spawn_positions() 15 | 16 | 17 | # Replace Node with whatever class you have for your player. 18 | func get_player() -> Node: 19 | return _player 20 | 21 | 22 | func move_player_to_spawn_position(level_id: int) -> void: 23 | if not _player: 24 | return 25 | 26 | var spawn_position: PlayerSpawnPosition = _player_spawn_positions.get(level_id) 27 | 28 | if not spawn_position: 29 | spawn_position = _player_spawn_position_container.get_child(0) 30 | 31 | _player.global_transform.origin = spawn_position.global_transform.origin 32 | 33 | 34 | func teleport_player_to_safety() -> void: 35 | pass 36 | 37 | 38 | func _add_player_spawn_positions() -> void: 39 | for child in _player_spawn_position_container.get_children(): 40 | if child is PlayerSpawnPosition: 41 | _player_spawn_positions[child.from_level_id] = child 42 | 43 | 44 | func _on_enemy_spawned(spawned_enemy: Node3D) -> void: 45 | if spawned_enemy.has_method("set_player"): 46 | spawned_enemy.set_player(_player) 47 | -------------------------------------------------------------------------------- /levels/player_spawn_position.gd: -------------------------------------------------------------------------------- 1 | class_name PlayerSpawnPosition 2 | extends Marker3D 3 | 4 | 5 | @export var from_level_id: Global.LEVELS = Global.LEVELS.TEST # (Global.LEVELS) 6 | #@export var spawn_camera_position: PlayerTopDown.DIRECTIONS = PlayerTopDown.DIRECTIONS.BOTTOM 7 | -------------------------------------------------------------------------------- /levels/tool_color_rect.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends ColorRect 3 | 4 | ## I only created this node so that the color change of the BackgroundColorRect 5 | ## in [Launcher] would change visually as [member Launcher.background_color] 6 | ## would change in the editor. 7 | -------------------------------------------------------------------------------- /management/camera_manager_3d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://buq1gorj12oov"] 2 | 3 | [ext_resource type="Script" path="res://management/camera_manager_3d.gd" id="1_whjns"] 4 | [ext_resource type="PackedScene" uid="uid://rd6ckqavshaf" path="res://tools/screenshake.tscn" id="2_r1kcf"] 5 | 6 | [node name="CameraManager3D" type="Node"] 7 | process_mode = 3 8 | script = ExtResource("1_whjns") 9 | 10 | [node name="TransCamera" type="Camera3D" parent="."] 11 | 12 | [node name="Screenshake" parent="." instance=ExtResource("2_r1kcf")] 13 | -------------------------------------------------------------------------------- /management/camera_manger_2d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://vv1vxgmuqbbu"] 2 | 3 | [ext_resource type="Script" path="res://management/camera_manger_2d.gd" id="1_sx88o"] 4 | [ext_resource type="Script" path="res://tools/screenshake.gd" id="2_mya10"] 5 | 6 | [node name="CameraManger2D" type="Node"] 7 | process_mode = 3 8 | script = ExtResource("1_sx88o") 9 | 10 | [node name="TransCamera" type="Camera2D" parent="."] 11 | enabled = false 12 | 13 | [node name="Screenshake" type="Node" parent="."] 14 | script = ExtResource("2_mya10") 15 | -------------------------------------------------------------------------------- /management/data_structures/game_data.gd: -------------------------------------------------------------------------------- 1 | class_name GameData 2 | extends Resource 3 | 4 | ## A Resource containing all of the data that needs to persist between scene 5 | ## changes. 6 | ## 7 | ## This resource is primarily used in HR PSX in conjunction with the 8 | ## [GameStateSaver] node. [ResourceMngr] should handle everything this needs 9 | ## to do so I wouldn't mess with it too much unless you need it to do something 10 | ## beyond its normal functionality. 11 | 12 | 13 | @export var _meta_data: Dictionary 14 | @export var _global_data: Dictionary 15 | @export var _scene_data: Dictionary 16 | 17 | 18 | func get_meta_data(name: String): 19 | if _meta_data.has(name): 20 | return _meta_data[name] 21 | return null 22 | 23 | 24 | func get_global_data(name: String): 25 | if _global_data.has(name): 26 | return _global_data[name] 27 | return null 28 | 29 | 30 | func get_scene_data(name: String): 31 | if _scene_data.has(name): 32 | return _scene_data[name] 33 | return null 34 | 35 | 36 | func add_meta_data(name: String, new_meta_data) -> void: 37 | _meta_data[name] = new_meta_data 38 | 39 | 40 | func add_global_data(name: String, new_global_data) -> void: 41 | _global_data[name] = new_global_data 42 | 43 | 44 | func add_scene_data(name: String, new_scene_data) -> void: 45 | _scene_data[name] = new_scene_data 46 | -------------------------------------------------------------------------------- /management/data_structures/resource_data/ambience_data.gd: -------------------------------------------------------------------------------- 1 | class_name AmbienceData 2 | extends ResourceData 3 | 4 | ## A resource that intended to be used in [member ResourceMngr.ambience_data]. 5 | 6 | 7 | @export_category("ID") 8 | 9 | @export var ambience_id: Global.AMBIENCES 10 | -------------------------------------------------------------------------------- /management/data_structures/resource_data/dialogue_data.gd: -------------------------------------------------------------------------------- 1 | class_name DialogueData 2 | extends ResourceData 3 | 4 | 5 | @export var dialogue_data: Global.DIALOGUES 6 | -------------------------------------------------------------------------------- /management/data_structures/resource_data/instance_data.gd: -------------------------------------------------------------------------------- 1 | class_name InstanceData 2 | extends ResourceData 3 | 4 | 5 | @export var instance_id: Global.INSTANCES = Global.INSTANCES.ENEMY_DEVILMAN 6 | -------------------------------------------------------------------------------- /management/data_structures/resource_data/level_data.gd: -------------------------------------------------------------------------------- 1 | class_name LevelData 2 | extends ResourceData 3 | 4 | ## A resource that intended to be used in [member ResourceMngr.level_data]. 5 | 6 | 7 | @export_category("ID") 8 | 9 | @export var level_id: Global.LEVELS 10 | -------------------------------------------------------------------------------- /management/data_structures/resource_data/music_data.gd: -------------------------------------------------------------------------------- 1 | class_name MusicData 2 | extends ResourceData 3 | 4 | ## A resource that intended to be used in [member ResourceMngr.music_data]. 5 | 6 | 7 | @export_category("ID") 8 | 9 | @export var music_id: Global.MUSIC 10 | -------------------------------------------------------------------------------- /management/data_structures/resource_data/resource_data.gd: -------------------------------------------------------------------------------- 1 | class_name ResourceData 2 | extends Resource 3 | 4 | ## A resource that intended to be extended and used to fill arrays in 5 | ## [ResourceMngr]. 6 | 7 | 8 | @export_category("File") 9 | @export_file var data_path = "" 10 | -------------------------------------------------------------------------------- /management/data_structures/resource_data/sfx_data.gd: -------------------------------------------------------------------------------- 1 | class_name SFXData 2 | extends ResourceData 3 | 4 | ## A resource that intended to be used in [member ResourceMngr.sfx_data]. 5 | 6 | 7 | @export_category("ID") 8 | 9 | @export var sfx_id: Global.SFX 10 | -------------------------------------------------------------------------------- /management/data_structures/resource_load_data.gd: -------------------------------------------------------------------------------- 1 | class_name ResourceLoadManager 2 | extends Node 3 | 4 | 5 | const _OK_LOADING_STATUSES = [ResourceLoader.THREAD_LOAD_IN_PROGRESS, ResourceLoader.THREAD_LOAD_LOADED] 6 | 7 | var _loading_resource: bool = false 8 | var _loading_resource_paths: Array[String] 9 | var _loaded_resource_paths: Array[String] 10 | 11 | 12 | func _process(_delta: float) -> void: 13 | for path in _loading_resource_paths: 14 | var status: ResourceLoader.ThreadLoadStatus = ResourceLoader.load_threaded_get_status(path) 15 | assert(_OK_LOADING_STATUSES.has(status), "There was an error while loading %s." % path) 16 | if status == ResourceLoader.THREAD_LOAD_LOADED: 17 | resource_loaded.emit() 18 | _move_resource_to_loaded_array() 19 | 20 | var status: ResourceLoader.ThreadLoadStatus = ResourceLoader.load_threaded_get_status(_loading_level_path) 21 | 22 | assert(_OK_LOADING_STATUSES.has(status), "There was an error while loading %s." % _loading_level_path) 23 | 24 | if status == ResourceLoader.THREAD_LOAD_LOADED: 25 | level_loaded.emit() 26 | set_loading_resource(false) 27 | 28 | 29 | func load_resource(path_to_resource: String) -> void: 30 | _loading_resource_paths.append(path_to_resource) 31 | ResourceLoader.load_threaded_request(path_to_resource) 32 | set_loading_resource(true) 33 | 34 | 35 | func set_loading_resource(new_loading_resource: bool) -> void: 36 | _loading_resource = new_loading_resource 37 | set_process(_loading_resource) 38 | -------------------------------------------------------------------------------- /management/data_structures/save_metadata.gd: -------------------------------------------------------------------------------- 1 | class_name SaveMetadata 2 | extends Resource 3 | 4 | @export var save_1_hash: String = "fa54s6df15a 61a56d" 5 | @export var save_2_hash: String = ":fjdaf d4f65a1 56w51f " 6 | @export var save_3_hash: String = "fidijefudhalfdaj; kfgld;af" 7 | 8 | 9 | func get_hash(save_slot: int) -> String: 10 | assert(save_slot <= 3, "tried to get hash for save slot that doesn't exist") 11 | 12 | var hash: String = "" 13 | match save_slot: 14 | 1: hash = save_1_hash 15 | 2: hash = save_2_hash 16 | 3: hash = save_3_hash 17 | 18 | return hash 19 | 20 | 21 | func set_hash(save_slot: int, new_hash: String) -> void: 22 | assert(save_slot <= 3, "tried to set hash for save slot that doesn't exist") 23 | 24 | match save_slot: 25 | 1: save_1_hash = new_hash 26 | 2: save_2_hash = new_hash 27 | 3: save_3_hash = new_hash 28 | -------------------------------------------------------------------------------- /management/debug_menu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://btlaebgb52ikw"] 2 | 3 | [ext_resource type="Script" path="res://management/debug_menu.gd" id="1_36c63"] 4 | 5 | [node name="DebugMenu" type="CanvasLayer"] 6 | script = ExtResource("1_36c63") 7 | 8 | [node name="Debug" type="Control" parent="."] 9 | layout_mode = 3 10 | anchors_preset = 15 11 | anchor_right = 1.0 12 | anchor_bottom = 1.0 13 | grow_horizontal = 2 14 | grow_vertical = 2 15 | 16 | [node name="ColorRect" type="ColorRect" parent="Debug"] 17 | layout_mode = 1 18 | anchors_preset = 7 19 | anchor_left = 0.5 20 | anchor_top = 1.0 21 | anchor_right = 0.5 22 | anchor_bottom = 1.0 23 | offset_left = -175.0 24 | offset_top = -175.0 25 | offset_right = 175.0 26 | grow_horizontal = 2 27 | grow_vertical = 0 28 | color = Color(0, 0, 0, 1) 29 | 30 | [node name="VBoxContainer" type="VBoxContainer" parent="Debug/ColorRect"] 31 | layout_mode = 1 32 | anchors_preset = 15 33 | anchor_right = 1.0 34 | anchor_bottom = 1.0 35 | grow_horizontal = 2 36 | grow_vertical = 2 37 | 38 | [node name="OptionsContainer" type="GridContainer" parent="Debug/ColorRect/VBoxContainer"] 39 | layout_mode = 2 40 | theme_override_constants/v_separation = -2 41 | -------------------------------------------------------------------------------- /management/fade_rect.gd: -------------------------------------------------------------------------------- 1 | class_name FadeRect 2 | extends ColorRect 3 | 4 | ## A [ColorRect] that is used to fade the screen to whatever color you want. 5 | ## 6 | ## This node is predominatly used in HR PSX to fade the screen to black between 7 | ## scene changes. 8 | 9 | 10 | ## This signal is emited when the animation from [method fade_in] or 11 | ## [method fade_out] is finished. 12 | signal fade_finished() 13 | 14 | ## The color that will be faded to in [method fade_out] or faded from in 15 | ## [method fade_in]. 16 | @export var default_fade_color: Color = Color.BLACK 17 | 18 | @onready var _animation_player: AnimationPlayer = $TransAnimationPlayer 19 | 20 | 21 | func _ready() -> void: 22 | _animation_player.animation_finished.connect(_on_trans_animation_player_animation_finished) 23 | 24 | 25 | func fade_in(fade_seconds: float = 1.0) -> void: 26 | _set_animation_speed(fade_seconds) 27 | _animation_player.play("fade_in") 28 | 29 | 30 | func fade_out(fade_seconds: float = 0.5, color: Color = default_fade_color) -> void: 31 | modulate = color 32 | _set_animation_speed(fade_seconds) 33 | _animation_player.play("fade_out") 34 | 35 | 36 | func _set_animation_speed(seconds: float) -> void: 37 | var clamped_seconds = clampf(seconds, 0.05, 20) 38 | _animation_player.speed_scale /= clamped_seconds 39 | 40 | 41 | func _on_trans_animation_player_animation_finished(_animation_name: String) -> void: 42 | _animation_player.speed_scale = 1 43 | fade_finished.emit() 44 | -------------------------------------------------------------------------------- /management/fade_rect.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=3 uid="uid://1ogrcx2r81q6"] 2 | 3 | [ext_resource type="Script" path="res://management/fade_rect.gd" id="1_gbcb6"] 4 | 5 | [sub_resource type="Animation" id="Animation_u54hn"] 6 | length = 0.001 7 | tracks/0/type = "value" 8 | tracks/0/imported = false 9 | tracks/0/enabled = true 10 | tracks/0/path = NodePath(".:color") 11 | tracks/0/interp = 1 12 | tracks/0/loop_wrap = true 13 | tracks/0/keys = { 14 | "times": PackedFloat32Array(0), 15 | "transitions": PackedFloat32Array(1), 16 | "update": 0, 17 | "values": [Color(0, 0, 0, 0)] 18 | } 19 | tracks/1/type = "value" 20 | tracks/1/imported = false 21 | tracks/1/enabled = true 22 | tracks/1/path = NodePath(".:visible") 23 | tracks/1/interp = 1 24 | tracks/1/loop_wrap = true 25 | tracks/1/keys = { 26 | "times": PackedFloat32Array(0), 27 | "transitions": PackedFloat32Array(1), 28 | "update": 1, 29 | "values": [false] 30 | } 31 | 32 | [sub_resource type="Animation" id="Animation_agu0l"] 33 | resource_name = "fade_in" 34 | tracks/0/type = "value" 35 | tracks/0/imported = false 36 | tracks/0/enabled = true 37 | tracks/0/path = NodePath(".:color") 38 | tracks/0/interp = 1 39 | tracks/0/loop_wrap = true 40 | tracks/0/keys = { 41 | "times": PackedFloat32Array(0, 1), 42 | "transitions": PackedFloat32Array(-2, -2), 43 | "update": 0, 44 | "values": [Color(1, 1, 1, 1), Color(0, 0, 0, 0)] 45 | } 46 | tracks/1/type = "value" 47 | tracks/1/imported = false 48 | tracks/1/enabled = true 49 | tracks/1/path = NodePath(".:visible") 50 | tracks/1/interp = 1 51 | tracks/1/loop_wrap = true 52 | tracks/1/keys = { 53 | "times": PackedFloat32Array(0, 1), 54 | "transitions": PackedFloat32Array(1, 1), 55 | "update": 1, 56 | "values": [true, false] 57 | } 58 | 59 | [sub_resource type="Animation" id="Animation_wwqpp"] 60 | resource_name = "fade_out" 61 | tracks/0/type = "value" 62 | tracks/0/imported = false 63 | tracks/0/enabled = true 64 | tracks/0/path = NodePath(".:color") 65 | tracks/0/interp = 1 66 | tracks/0/loop_wrap = true 67 | tracks/0/keys = { 68 | "times": PackedFloat32Array(0, 0.5), 69 | "transitions": PackedFloat32Array(-2, -2), 70 | "update": 0, 71 | "values": [Color(0, 0, 0, 0), Color(1, 1, 1, 1)] 72 | } 73 | tracks/1/type = "value" 74 | tracks/1/imported = false 75 | tracks/1/enabled = true 76 | tracks/1/path = NodePath(".:visible") 77 | tracks/1/interp = 1 78 | tracks/1/loop_wrap = true 79 | tracks/1/keys = { 80 | "times": PackedFloat32Array(0), 81 | "transitions": PackedFloat32Array(1), 82 | "update": 1, 83 | "values": [true] 84 | } 85 | 86 | [sub_resource type="AnimationLibrary" id="AnimationLibrary_8eyso"] 87 | _data = { 88 | "RESET": SubResource("Animation_u54hn"), 89 | "fade_in": SubResource("Animation_agu0l"), 90 | "fade_out": SubResource("Animation_wwqpp") 91 | } 92 | 93 | [node name="FadeRect" type="ColorRect"] 94 | visible = false 95 | custom_minimum_size = Vector2(1280, 720) 96 | anchors_preset = 15 97 | anchor_right = 1.0 98 | anchor_bottom = 1.0 99 | grow_horizontal = 2 100 | grow_vertical = 2 101 | size_flags_horizontal = 3 102 | size_flags_vertical = 3 103 | color = Color(0, 0, 0, 0) 104 | script = ExtResource("1_gbcb6") 105 | 106 | [node name="TransAnimationPlayer" type="AnimationPlayer" parent="."] 107 | libraries = { 108 | "": SubResource("AnimationLibrary_8eyso") 109 | } 110 | -------------------------------------------------------------------------------- /management/game_manager_no_subviewport.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=3 uid="uid://bwf27kjache61"] 2 | 3 | [ext_resource type="Script" path="res://management/game_manager.gd" id="1_wk2hf"] 4 | [ext_resource type="PackedScene" uid="uid://difhaik1vv3o" path="res://levels/example/launcher_example.tscn" id="3_qtdgq"] 5 | [ext_resource type="PackedScene" uid="uid://1ogrcx2r81q6" path="res://management/fade_rect.tscn" id="4_4jt33"] 6 | [ext_resource type="PackedScene" uid="uid://c8mx7gh16hp2h" path="res://management/screen_space_shader_manager.tscn" id="5_kwkbv"] 7 | 8 | [node name="GameManager" type="Control"] 9 | process_mode = 3 10 | layout_mode = 3 11 | anchors_preset = 15 12 | anchor_right = 1.0 13 | anchor_bottom = 1.0 14 | grow_horizontal = 2 15 | grow_vertical = 2 16 | script = ExtResource("1_wk2hf") 17 | 18 | [node name="FadeRect" parent="." instance=ExtResource("4_4jt33")] 19 | unique_name_in_owner = true 20 | layout_mode = 1 21 | 22 | [node name="LevelContainer" type="Node" parent="."] 23 | unique_name_in_owner = true 24 | process_mode = 1 25 | 26 | [node name="LauncherExample" parent="LevelContainer" instance=ExtResource("3_qtdgq")] 27 | background_color = Color(0, 0, 0, 1) 28 | 29 | [node name="ScreenSpaceShaderManager" parent="LevelContainer" instance=ExtResource("5_kwkbv")] 30 | -------------------------------------------------------------------------------- /management/game_manager_with_subviewport.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://ch2o46s520ygi"] 2 | 3 | [ext_resource type="Script" path="res://management/game_manager.gd" id="1_m3ejh"] 4 | [ext_resource type="Shader" path="res://shaders/psx_dither.gdshader" id="2_66ot5"] 5 | [ext_resource type="PackedScene" uid="uid://difhaik1vv3o" path="res://levels/example/launcher_example.tscn" id="3_mfsm3"] 6 | [ext_resource type="PackedScene" uid="uid://1ogrcx2r81q6" path="res://management/fade_rect.tscn" id="4_8yejn"] 7 | [ext_resource type="PackedScene" uid="uid://c8mx7gh16hp2h" path="res://management/screen_space_shader_manager.tscn" id="5_rq5lt"] 8 | 9 | [sub_resource type="ShaderMaterial" id="ShaderMaterial_oesds"] 10 | shader = ExtResource("2_66ot5") 11 | shader_parameter/color_depth = 5 12 | shader_parameter/dithering = false 13 | shader_parameter/resolution_scale = 4 14 | 15 | [node name="GameManager" type="Control"] 16 | process_mode = 3 17 | layout_mode = 3 18 | anchors_preset = 15 19 | anchor_right = 1.0 20 | anchor_bottom = 1.0 21 | grow_horizontal = 2 22 | grow_vertical = 2 23 | script = ExtResource("1_m3ejh") 24 | default_shaders = Array[int]([2, 3]) 25 | 26 | [node name="FadeRect" parent="." instance=ExtResource("4_8yejn")] 27 | unique_name_in_owner = true 28 | layout_mode = 1 29 | 30 | [node name="PPDitherContainer" type="SubViewportContainer" parent="."] 31 | material = SubResource("ShaderMaterial_oesds") 32 | layout_mode = 1 33 | anchors_preset = 15 34 | anchor_right = 1.0 35 | anchor_bottom = 1.0 36 | grow_horizontal = 2 37 | grow_vertical = 2 38 | 39 | [node name="SubViewport" type="SubViewport" parent="PPDitherContainer"] 40 | handle_input_locally = false 41 | size = Vector2i(1280, 720) 42 | size_2d_override = Vector2i(320, 180) 43 | size_2d_override_stretch = true 44 | render_target_update_mode = 4 45 | 46 | [node name="LevelContainer" type="Node" parent="PPDitherContainer/SubViewport"] 47 | unique_name_in_owner = true 48 | process_mode = 1 49 | 50 | [node name="LauncherExample" parent="PPDitherContainer/SubViewport/LevelContainer" instance=ExtResource("3_mfsm3")] 51 | background_color = Color(0, 0, 0, 1) 52 | 53 | [node name="ScreenSpaceShaderManager" parent="." instance=ExtResource("5_rq5lt")] 54 | layout_mode = 1 55 | -------------------------------------------------------------------------------- /management/game_state_saver.gd: -------------------------------------------------------------------------------- 1 | class_name GameStateSaver 2 | extends Node 3 | 4 | ## This node saves any data you choose from it's parent that needs to persist 5 | ## between scenes. 6 | ## 7 | ## This is intended to be used in conjunction with the autoload singleton 8 | ## [ResourceMngr] in HR PSX. Add it as a child to any node and edit 9 | ## [member save_properties] in the editor. Each entry in save_properties should 10 | ## be a member of this node's parent. When this node's parent is freed, all the 11 | ## members in save_properties will be saved to [GameData] through [ResourceMngr] 12 | ## and when this node's parent enters into the scene tree again, all of it's 13 | ## members data contained in save_properties will be restored. This script is 14 | ## heavily based on a similar node by Jason Lothamer. 15 | ## 16 | ## @tutorial(Jason's tutorial): https://www.youtube.com/watch?v=_gBpk5nKyXU 17 | 18 | 19 | const _GAME_STATE_KEY_NODE_PATH = "game_state_saver_node_path" 20 | 21 | ## An array of properties of this node's parent that will be saved when the 22 | ## parent is freed and restored when it is loaded again. 23 | @export var save_properties: Array[String] 24 | @export var dynamic_instance: bool 25 | ## If this value is set to true, the parent's data will be saved as global data 26 | ## through [ResourceMngr] instead of scene data. 27 | @export var global: bool 28 | ## If this value is set to true, there will be a breakpoint each time this node 29 | ## loads or saves data from its parent. 30 | @export var debug: bool 31 | 32 | @onready var _parent: Node = get_parent() 33 | 34 | 35 | func _ready() -> void: 36 | add_to_group(Global.GAME_STATE_SAVER_GROUP) 37 | 38 | 39 | func _exit_tree() -> void: 40 | if dynamic_instance: 41 | return 42 | 43 | var key: String 44 | if debug: 45 | breakpoint 46 | if global: 47 | key = _parent.name 48 | else: 49 | key = get_path() 50 | 51 | SignalManager.state_saver_freed.emit(global, key, get_save_data()) 52 | 53 | 54 | func get_parent_path() -> NodePath: 55 | return _parent.get_path() 56 | 57 | 58 | func get_save_data() -> Dictionary: 59 | var node_data: Dictionary = {} 60 | 61 | node_data[_GAME_STATE_KEY_NODE_PATH] = _parent.get_path() 62 | 63 | for prop_name in save_properties: 64 | node_data[prop_name] = _parent.get(prop_name) 65 | 66 | return node_data 67 | 68 | 69 | func load_data(loaded_data: Dictionary) -> void: 70 | if loaded_data.has("freed") and loaded_data.freed: 71 | _parent.queue_free() 72 | return 73 | for prop_name in loaded_data: 74 | _parent.set(prop_name, loaded_data[prop_name]) 75 | -------------------------------------------------------------------------------- /management/global.gd: -------------------------------------------------------------------------------- 1 | class_name Glbl 2 | extends Node 3 | 4 | ## This node, along with [SignalMngr], is intended to be used as an autoload 5 | ## singleton to facilitate communication between the rest of the nodes in HR 6 | ## PSX, it does this by containing enums. 7 | ## 8 | ## You should edit this script in your game to have the appropriate ACTORS, 9 | ## MUSIC, AMBIENCES, SFX, and LEVELS. Don't extend this into another script, 10 | ## because you cannot change the enums once they've been declared. I had to use 11 | ## a shortened class name because the class name cannot be the same as it's 12 | ## autoload name. 13 | 14 | 15 | enum ACTORS { 16 | 17 | } 18 | 19 | enum LEVELS { 20 | MAIN_MENU, 21 | INTRO, 22 | TEST, 23 | TEST2, 24 | } 25 | 26 | enum SOUND_TYPES { 27 | MUSIC, 28 | AMBIENCE, 29 | SFX, 30 | } 31 | 32 | enum MUSIC { 33 | NONE, 34 | ITS_SAX, 35 | ITS_SAX_8_BIT, 36 | CARNELIA, 37 | SHIFTING_LOSS, 38 | } 39 | 40 | enum AMBIENCES { 41 | NONE, 42 | FOGHORN, 43 | LOST, 44 | } 45 | 46 | enum SFX { 47 | BLIP, 48 | NJB, 49 | } 50 | 51 | enum WINDOW_MODES { 52 | FULLSCREEN, 53 | BORDERLESS, 54 | WINDOWED_MED, 55 | WINDOWED_SMALL, 56 | } 57 | 58 | enum SHADERS { 59 | BLUR, 60 | COLOR_PRECISION, 61 | CRT, 62 | GRAIN, 63 | PSX_DITHER, 64 | SHARPNESS, 65 | VHS, 66 | GLITCH, 67 | } 68 | 69 | enum DAMAGE_TYPES { 70 | CRUSHING, 71 | SLASHING, 72 | PIERCING, 73 | BURNING, 74 | FREEZING, 75 | CAUSTIC, 76 | DISINTEGRATING, 77 | MIST, 78 | } 79 | 80 | const GAME_STATE_SAVER_GROUP = "game_state_saver" 81 | -------------------------------------------------------------------------------- /management/save_game_manager.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=3 uid="uid://n8jvj3d5d0eh"] 2 | 3 | [node name="SaveGameManager" type="Node"] 4 | process_mode = 3 5 | -------------------------------------------------------------------------------- /management/save_metadata.gd: -------------------------------------------------------------------------------- 1 | class_name SaveMetadata 2 | extends Resource 3 | 4 | @export var save_1_hash: String = "fa54s6df15a 61a56d" 5 | @export var save_2_hash: String = ":fjdaf d4f65a1 56w51f " 6 | @export var save_3_hash: String = "fidijefudhalfdaj; kfgld;af" 7 | 8 | 9 | func get_hash(save_slot: int) -> String: 10 | assert(save_slot <= 3, "tried to get hash for save slot that doesn't exist") 11 | 12 | var hash: String = "" 13 | match save_slot: 14 | 1: hash = save_1_hash 15 | 2: hash = save_2_hash 16 | 3: hash = save_3_hash 17 | 18 | return hash 19 | 20 | 21 | func set_hash(save_slot: int, new_hash: String) -> void: 22 | assert(save_slot <= 3, "tried to set hash for save slot that doesn't exist") 23 | 24 | match save_slot: 25 | 1: save_1_hash = new_hash 26 | 2: save_2_hash = new_hash 27 | 3: save_3_hash = new_hash 28 | -------------------------------------------------------------------------------- /management/signal_manager.gd: -------------------------------------------------------------------------------- 1 | class_name SignalMngr 2 | extends Node 3 | 4 | ## This is intended to be used as an autoload singleton in conjuction with the 5 | ## rest of HR PSX. 6 | ## 7 | ## Use the signals here as an easy way to interact with the various scripts 8 | ## included in HR PSX in the intended way. 9 | 10 | 11 | # Saves 12 | signal game_save_requested(save_slot: int) 13 | signal game_save_load_requested(save_slot: int) 14 | signal game_save_load_finished() 15 | 16 | # Resource Manager 17 | signal music_load_requested(music_id: Global.MUSIC, path_to_resource: String) 18 | signal ambience_load_requested(ambience_id: Global.AMBIENCES, path_to_resource: String) 19 | signal sfx_load_requested(sfx_id: Global.SFX, path_to_resource: String) 20 | signal screenshot_requested() 21 | 22 | # Post processing 23 | signal pp_default_shaders_changed(new_default_shaders: Array) 24 | signal pp_default_shaders_enabled_changed(new_default_pp_enabled: bool) 25 | signal pp_enabled_changed(shader: Global.SHADERS, enabled: bool) 26 | signal pp_all_disabled() 27 | 28 | # Scene transitions 29 | signal change_scene_requested(level_id: Global.LEVELS) 30 | signal fade_out_requested() 31 | signal fade_in_requested() 32 | signal fade_out_finished() 33 | signal fade_in_finished() 34 | 35 | # Gameplay 36 | signal pause_allowed_changed(pause_allowed: bool) 37 | signal paused_changed(paused: bool) 38 | signal set_delayed(target: Object, property_name: String, property_value, wait_seconds: float) 39 | signal time_scale_change_requested(time_scale: float, duration: float) 40 | signal location_entered(location_name: String) 41 | 42 | # CameraManager 43 | signal camera_cut_requested(to_camera: Camera3D) 44 | signal camera_return_cut_requested() 45 | signal camera_transition_requested(to_camera: Camera3D, duration: float) 46 | signal camera_return_transition_requested(duration: float) 47 | signal camera_transition_finished() 48 | signal screenshake_requested(speed: float, strength: float, decay_rate: float) 49 | signal screenshake_stop_requested() 50 | 51 | # Instances 52 | signal instance_requested(scene: PackedScene, location: Vector3) 53 | signal instance_spawned(instance: Node) 54 | signal enemy_spawned(enemy) 55 | signal state_saver_freed(name: String, data: Dictionary) 56 | 57 | # Player 58 | signal player_died 59 | signal player_health_changed(current_health: int, max_health: int) 60 | 61 | # Display 62 | signal set_window_mode(new_window_mode: Global.WINDOW_MODES) 63 | -------------------------------------------------------------------------------- /management/sound_manager.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://cwcdqiyx5wdyq"] 2 | 3 | [ext_resource type="Script" path="res://management/sound_manager.gd" id="1_mwa27"] 4 | 5 | [node name="SoundManager" type="Node"] 6 | process_mode = 3 7 | script = ExtResource("1_mwa27") 8 | 9 | [node name="SFXPlayers" type="Node" parent="."] 10 | 11 | [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="SFXPlayers"] 12 | 13 | [node name="AudioStreamPlayer2" type="AudioStreamPlayer" parent="SFXPlayers"] 14 | 15 | [node name="AudioStreamPlayer3" type="AudioStreamPlayer" parent="SFXPlayers"] 16 | 17 | [node name="AudioStreamPlayer4" type="AudioStreamPlayer" parent="SFXPlayers"] 18 | 19 | [node name="AudioStreamPlayer5" type="AudioStreamPlayer" parent="SFXPlayers"] 20 | 21 | [node name="AudioStreamPlayer6" type="AudioStreamPlayer" parent="SFXPlayers"] 22 | 23 | [node name="AudioStreamPlayer7" type="AudioStreamPlayer" parent="SFXPlayers"] 24 | 25 | [node name="AudioStreamPlayer8" type="AudioStreamPlayer" parent="SFXPlayers"] 26 | 27 | [node name="AudioStreamPlayer9" type="AudioStreamPlayer" parent="SFXPlayers"] 28 | 29 | [node name="AudioStreamPlayer10" type="AudioStreamPlayer" parent="SFXPlayers"] 30 | 31 | [node name="AudioStreamPlayer11" type="AudioStreamPlayer" parent="SFXPlayers"] 32 | 33 | [node name="AudioStreamPlayer12" type="AudioStreamPlayer" parent="SFXPlayers"] 34 | 35 | [node name="MusicPlayers" type="Node" parent="."] 36 | 37 | [node name="AudioStreamPlayer13" type="AudioStreamPlayer" parent="MusicPlayers"] 38 | 39 | [node name="AudioStreamPlayer14" type="AudioStreamPlayer" parent="MusicPlayers"] 40 | 41 | [node name="AmbiencePlayers" type="Node" parent="."] 42 | 43 | [node name="AudioStreamPlayer15" type="AudioStreamPlayer" parent="AmbiencePlayers"] 44 | 45 | [node name="AudioStreamPlayer16" type="AudioStreamPlayer" parent="AmbiencePlayers"] 46 | -------------------------------------------------------------------------------- /materials/textures/Tiles012_1K_Color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/materials/textures/Tiles012_1K_Color.jpg -------------------------------------------------------------------------------- /materials/textures/Tiles012_1K_Color.jpg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bufvgxxobu8o6" 6 | path.s3tc="res://.godot/imported/Tiles012_1K_Color.jpg-531498e2490be0f3d1ac6345a844c23c.s3tc.ctex" 7 | metadata={ 8 | "imported_formats": ["s3tc_bptc"], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://materials/textures/Tiles012_1K_Color.jpg" 15 | dest_files=["res://.godot/imported/Tiles012_1K_Color.jpg-531498e2490be0f3d1ac6345a844c23c.s3tc.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=true 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=0 36 | -------------------------------------------------------------------------------- /materials/textures/decorative.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/materials/textures/decorative.png -------------------------------------------------------------------------------- /materials/textures/decorative.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://sk2w5b38nhgt" 6 | path.s3tc="res://.godot/imported/decorative.png-58f07883c9c2143dae59c57ab5b0405c.s3tc.ctex" 7 | metadata={ 8 | "imported_formats": ["s3tc_bptc"], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://materials/textures/decorative.png" 15 | dest_files=["res://.godot/imported/decorative.png-58f07883c9c2143dae59c57ab5b0405c.s3tc.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=true 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=0 36 | -------------------------------------------------------------------------------- /materials/textures/demon_blob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/materials/textures/demon_blob.png -------------------------------------------------------------------------------- /materials/textures/demon_blob.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://ci1c01c7aq526" 6 | path.s3tc="res://.godot/imported/demon_blob.png-e04425a10e3febf389f8303deb2374a9.s3tc.ctex" 7 | metadata={ 8 | "imported_formats": ["s3tc_bptc"], 9 | "vram_texture": true 10 | } 11 | generator_parameters={} 12 | 13 | [deps] 14 | 15 | source_file="res://materials/textures/demon_blob.png" 16 | dest_files=["res://.godot/imported/demon_blob.png-e04425a10e3febf389f8303deb2374a9.s3tc.ctex"] 17 | 18 | [params] 19 | 20 | compress/mode=2 21 | compress/high_quality=false 22 | compress/lossy_quality=0.7 23 | compress/hdr_compression=1 24 | compress/normal_map=0 25 | compress/channel_pack=0 26 | mipmaps/generate=true 27 | mipmaps/limit=-1 28 | roughness/mode=0 29 | roughness/src_normal="" 30 | process/fix_alpha_border=true 31 | process/premult_alpha=false 32 | process/normal_map_invert_y=false 33 | process/hdr_as_srgb=false 34 | process/hdr_clamp_exposure=false 35 | process/size_limit=0 36 | detect_3d/compress_to=0 37 | -------------------------------------------------------------------------------- /materials/textures/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/materials/textures/tile.png -------------------------------------------------------------------------------- /materials/textures/tile.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://ixemw6m2rw56" 6 | path.s3tc="res://.godot/imported/tile.png-a417d4c87d6212c902c8de00e6e24ed8.s3tc.ctex" 7 | metadata={ 8 | "imported_formats": ["s3tc_bptc"], 9 | "vram_texture": true 10 | } 11 | 12 | [deps] 13 | 14 | source_file="res://materials/textures/tile.png" 15 | dest_files=["res://.godot/imported/tile.png-a417d4c87d6212c902c8de00e6e24ed8.s3tc.ctex"] 16 | 17 | [params] 18 | 19 | compress/mode=2 20 | compress/high_quality=false 21 | compress/lossy_quality=0.7 22 | compress/hdr_compression=1 23 | compress/normal_map=0 24 | compress/channel_pack=0 25 | mipmaps/generate=true 26 | mipmaps/limit=-1 27 | roughness/mode=0 28 | roughness/src_normal="" 29 | process/fix_alpha_border=true 30 | process/premult_alpha=false 31 | process/normal_map_invert_y=false 32 | process/hdr_as_srgb=false 33 | process/hdr_clamp_exposure=false 34 | process/size_limit=0 35 | detect_3d/compress_to=0 36 | -------------------------------------------------------------------------------- /materials/textures/warning_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/materials/textures/warning_background.png -------------------------------------------------------------------------------- /materials/textures/warning_background.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://susf0ur67j3h" 6 | path="res://.godot/imported/warning_background.png-3ee9a895c67019953bef737a73de81c0.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://materials/textures/warning_background.png" 14 | dest_files=["res://.godot/imported/warning_background.png-3ee9a895c67019953bef737a73de81c0.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /readme_screenshots/Screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/readme_screenshots/Screenshot1.png -------------------------------------------------------------------------------- /readme_screenshots/Screenshot1.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dspsv6s2fsrjm" 6 | path="res://.godot/imported/Screenshot1.png-7fdfdc30a87fb5e9dc3eb636ee8f3f47.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://readme_screenshots/Screenshot1.png" 14 | dest_files=["res://.godot/imported/Screenshot1.png-7fdfdc30a87fb5e9dc3eb636ee8f3f47.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /readme_screenshots/Screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/readme_screenshots/Screenshot2.png -------------------------------------------------------------------------------- /readme_screenshots/Screenshot2.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://2dd473afc4uw" 6 | path="res://.godot/imported/Screenshot2.png-1299958e1f2bb19e446d144a2fa5fdd7.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://readme_screenshots/Screenshot2.png" 14 | dest_files=["res://.godot/imported/Screenshot2.png-1299958e1f2bb19e446d144a2fa5fdd7.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /readme_screenshots/Screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/readme_screenshots/Screenshot3.png -------------------------------------------------------------------------------- /readme_screenshots/Screenshot3.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://ddv7jxwhwvdvu" 6 | path="res://.godot/imported/Screenshot3.png-a6301d81308ac18117609cea39e37bd0.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://readme_screenshots/Screenshot3.png" 14 | dest_files=["res://.godot/imported/Screenshot3.png-a6301d81308ac18117609cea39e37bd0.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /readme_screenshots/screenshot4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/readme_screenshots/screenshot4.png -------------------------------------------------------------------------------- /readme_screenshots/screenshot4.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://6k5r2pcqau2n" 6 | path="res://.godot/imported/screenshot4.png-d6b6c9ee655284e22d0b5fe9f9c738d4.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://readme_screenshots/screenshot4.png" 14 | dest_files=["res://.godot/imported/screenshot4.png-d6b6c9ee655284e22d0b5fe9f9c738d4.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /script_templates/launcher/launcher_template.gd: -------------------------------------------------------------------------------- 1 | # meta_description: Contains all of the functions you should override 2 | # meta_default: true 3 | 4 | 5 | @tool 6 | extends _BASE_ 7 | 8 | 9 | ## This is the function that will be called if the top selection is chosen. 10 | func handle_first_selection() -> void: 11 | pass 12 | 13 | 14 | ## This is the function that will be called if the middle selection is chosen. 15 | func handle_second_selection() -> void: 16 | pass 17 | -------------------------------------------------------------------------------- /script_templates/level/level_template.gd: -------------------------------------------------------------------------------- 1 | # meta_description: Contains all of the functions you should override 2 | # meta_default: true 3 | 4 | 5 | extends _BASE_ 6 | 7 | 8 | func _ready() -> void: 9 | super() 10 | # Add the rest of what you need to the ready function after the super() call. 11 | 12 | 13 | ## This is the logic that will be called with the previous scenes id from 14 | ## [member Glbl.LEVELS] so you can have run logic based on the previous 15 | ## level/scene. Keep in mind that this runs after [method ready]. 16 | func initialize_level(previous_scene_id: Global.LEVELS) -> void: 17 | _player_container.move_player_to_spawn_position(previous_scene_id) 18 | -------------------------------------------------------------------------------- /shaders/B&W.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "demofox" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3, WEBGL 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/XdXSzX 6 | 7 | shader_type canvas_item; 8 | 9 | uniform float contrast :hint_range(0.0, 3.0) = 1.0; 10 | uniform float brightness :hint_range(-1.0, 1.0) = 0.0; 11 | 12 | 13 | void fragment(){ 14 | vec2 uv = FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy; 15 | 16 | vec3 pixelColor = texture(SCREEN_TEXTURE, uv).xyz; 17 | 18 | // Grayscale 19 | float pixelGrey = dot(pixelColor, vec3(0.2126, 0.7152, 0.0722)); 20 | pixelColor = vec3(pixelGrey); 21 | 22 | // Contrast 23 | pixelColor.rgb = ((pixelColor.rgb - 0.5) * max(contrast, 0.0)) + 0.5; 24 | 25 | // Brightness 26 | pixelColor.rgb += brightness; 27 | 28 | COLOR = vec4(pixelColor, 1.0); 29 | } -------------------------------------------------------------------------------- /shaders/BetterCC.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "Wunkolo" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3, WEBGL 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/tllfRf 6 | 7 | shader_type canvas_item; 8 | 9 | uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, filter_linear_mipmap; 10 | uniform vec4 Shadows :source_color = vec4(0.0, 0.0, 0.0, 1.0); 11 | uniform vec4 Midtones :source_color = vec4(0.0, 0.0, 0.0, 1.0); 12 | uniform vec4 Hilights :source_color = vec4(0.0, 0.0, 0.0, 1.0); 13 | 14 | vec3 InvLerp( vec3 A, vec3 B, vec3 t){ 15 | return (t - A) / (B - A); 16 | } 17 | 18 | vec3 ColorGrade( in vec3 InColor ){ 19 | // Calculate the three offseted colors up-front 20 | vec3 OffShadows = InColor + Shadows.xyz; 21 | vec3 OffMidtones = InColor + Midtones.xyz; 22 | vec3 OffHilights = InColor + Hilights.xyz; 23 | 24 | // Linearly interpolate between the 3 new colors, piece-wise 25 | return mix( 26 | // We pick which of the two control points to interpolate from based on which side of 27 | // 0.5 the input color channel lands on 28 | mix(OffShadows, OffMidtones, InvLerp(vec3(0.0), vec3(0.5), InColor)), // < 0.5 29 | mix(OffMidtones, OffHilights, InvLerp(vec3(0.5), vec3(1.0), InColor)), // >= 0.5 30 | greaterThanEqual(InColor, vec3(0.5)) 31 | ); 32 | } 33 | 34 | void fragment(){ 35 | vec2 uv = FRAGCOORD.xy / vec2(1.0 / SCREEN_PIXEL_SIZE.xy); 36 | COLOR.a = 1.0; 37 | COLOR.rgb = texture(SCREEN_TEXTURE, uv).rgb; 38 | COLOR.rgb = ColorGrade(COLOR.rgb); 39 | 40 | //COLOR.rgb = pow(COLOR.rgb, vec3(2.2)); 41 | } -------------------------------------------------------------------------------- /shaders/Blur.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "jcant0n" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3, WEBGL 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/XssSDs# 6 | 7 | shader_type canvas_item; 8 | uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, filter_linear; 9 | uniform float amount :hint_range(0.0, 1.5) = 1.0; 10 | 11 | vec2 Circle(float Start, float Points, float Point) { 12 | float Rad = (3.141592 * 3.0 * (1.0 / Points)) * (Point + Start); 13 | return vec2(sin(Rad), cos(Rad)); 14 | } 15 | 16 | void fragment(){ 17 | vec2 uv = FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy; 18 | vec2 PixelOffset = amount / (1.0 / SCREEN_PIXEL_SIZE).xy; 19 | 20 | float Start = 2.0 / 14.0; 21 | vec2 Scale = 0.66 * 4.0 * 2.0 * PixelOffset.xy; 22 | 23 | vec3 N0 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 0.0) * Scale).rgb; 24 | vec3 N1 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 1.0) * Scale).rgb; 25 | vec3 N2 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 2.0) * Scale).rgb; 26 | vec3 N3 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 3.0) * Scale).rgb; 27 | vec3 N4 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 4.0) * Scale).rgb; 28 | vec3 N5 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 5.0) * Scale).rgb; 29 | vec3 N6 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 6.0) * Scale).rgb; 30 | vec3 N7 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 7.0) * Scale).rgb; 31 | vec3 N8 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 8.0) * Scale).rgb; 32 | vec3 N9 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 9.0) * Scale).rgb; 33 | vec3 N10 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 10.0) * Scale).rgb; 34 | vec3 N11 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 11.0) * Scale).rgb; 35 | vec3 N12 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 12.0) * Scale).rgb; 36 | vec3 N13 = texture(SCREEN_TEXTURE, uv + Circle(Start, 14.0, 13.0) * Scale).rgb; 37 | vec3 N14 = texture(SCREEN_TEXTURE, uv).rgb; 38 | 39 | float W = 1.0 / 15.0; 40 | 41 | vec3 color = vec3(0,0,0); 42 | 43 | color.rgb = 44 | (N0 * W) + 45 | (N1 * W) + 46 | (N2 * W) + 47 | (N3 * W) + 48 | (N4 * W) + 49 | (N5 * W) + 50 | (N6 * W) + 51 | (N7 * W) + 52 | (N8 * W) + 53 | (N9 * W) + 54 | (N10 * W) + 55 | (N11 * W) + 56 | (N12 * W) + 57 | (N13 * W) + 58 | (N14 * W); 59 | 60 | COLOR = vec4(color.rgb,1.0); 61 | } 62 | 63 | -------------------------------------------------------------------------------- /shaders/ColorPrecission.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "abelcamarena" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3, WEBGL 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/tsKGDm 6 | 7 | // Looking for ditheirng? I reccomend using this shader instead : 8 | // https://github.com/WittyCognomen/godot-psx-shaders/blob/master/shaders/psx_dither_post.shader 9 | // https://github.com/WittyCognomen/godot-psx-shaders/tree/master/shaders/dithers 10 | 11 | shader_type canvas_item; 12 | 13 | uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, filter_linear; 14 | uniform float SCREEN_WIDTH = 320.; // Lower num - bigger pixels (this will be the screen width) 15 | uniform float COLOR_FACTOR :hint_range(0., 10.) = 4.; // Higher num - higher colors quality 16 | 17 | void fragment(){ 18 | // Reduce pixels 19 | vec2 size = SCREEN_WIDTH * SCREEN_PIXEL_SIZE.xy/SCREEN_PIXEL_SIZE.x; 20 | vec2 coor = floor( UV * size) ; 21 | vec2 uv = FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy; 22 | 23 | // Get source color 24 | vec3 col = texture(SCREEN_TEXTURE, uv).xyz; 25 | 26 | // Reduce colors 27 | col = floor(col * COLOR_FACTOR) / COLOR_FACTOR; 28 | 29 | // Output to screen 30 | COLOR = vec4(col,1.); 31 | } -------------------------------------------------------------------------------- /shaders/Glitch.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "keijiro" FROM GITHUB 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : https://github.com/keijiro/KinoGlitch#license 4 | //COMATIBLE WITH : GLES2, GLES3, WEBGL 5 | //GITHUB LINK : https://github.com/keijiro/KinoGlitch 6 | 7 | shader_type canvas_item; 8 | 9 | uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, filter_linear; 10 | uniform float _ScanLineJitter : hint_range(.2, 1) = .25; // (displacement, threshold) 11 | uniform float _VerticalJump : hint_range(0, 1) = .01; // (amount, time) 12 | uniform float _HorizontalShake : hint_range(0, 1) = 0; 13 | uniform float _ColorDrift : hint_range(0, 1) = .02; // (amount, time) 14 | 15 | 16 | float nrand(float x, float y){ 17 | return fract(sin(dot(vec2(x, y), vec2(12.9898, 78.233))) * 43758.5453); 18 | } 19 | 20 | void fragment(){ 21 | float sl_thresh = dot(vec2(1.0 - _ScanLineJitter * 1.2), vec2(1.0 - _ScanLineJitter * 1.2)); 22 | float sl_disp = 0.002 + pow(_ScanLineJitter, 3) * 0.05; 23 | vec2 sl = vec2(sl_disp, sl_thresh); 24 | 25 | float _verticalJumpTime = TIME * _VerticalJump * 11.3; 26 | vec2 vj = vec2(_VerticalJump, _verticalJumpTime); 27 | 28 | float hs = _HorizontalShake * 0.2; 29 | 30 | vec2 cd = vec2(_ColorDrift * 0.04f, TIME * 606.11f); 31 | 32 | float u = FRAGCOORD.x / (1.0 / SCREEN_PIXEL_SIZE).x; 33 | float v = FRAGCOORD.y / (1.0 / SCREEN_PIXEL_SIZE).y; 34 | 35 | // Scan line jitter 36 | float jitter = nrand(v, TIME) * 2.0 - 1.0; 37 | jitter *= step(sl.y, abs(jitter)) * sl.x; 38 | 39 | // Vertical jump 40 | float jump = mix(v, fract(v + vj.y), vj.x); 41 | 42 | // Horizontal shake 43 | float shake = (nrand(TIME, 2) - 0.5) * hs; 44 | 45 | // Color drift 46 | float drift = sin(jump + cd.y) * cd.x; 47 | 48 | vec4 final1 = texture(SCREEN_TEXTURE, fract(vec2(u + jitter + shake, jump))); 49 | vec4 final2 = texture(SCREEN_TEXTURE, fract(vec2(u + jitter + shake + drift, jump))); 50 | 51 | vec4 render = vec4(final1.r, final2.g, final1.b, 1); 52 | 53 | COLOR = render; 54 | } -------------------------------------------------------------------------------- /shaders/JpegCompression.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "paniq" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3, WEBGL 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/MdcGzj 6 | 7 | shader_type canvas_item; 8 | 9 | uniform float color_depth :hint_range(0.0, 255.0) = 100.0; 10 | uniform float color_number :hint_range(0.0, 50.0) = 20.0; 11 | 12 | const mat3 rgb2ycbcr = mat3( 13 | vec3(0.299, -0.168736, 0.5), 14 | vec3(0.587, -0.331264, -0.418688), 15 | vec3(0.114, 0.5, -0.081312) 16 | ); 17 | const mat3 ycbcr2rgb = mat3( 18 | vec3(1.0, 1.0, 1.0), 19 | vec3(0.0, -0.344136, 1.772), 20 | vec3(1.402, -0.714136, 0.0) 21 | ); 22 | 23 | // simulating 8:4:4 compression ratio (16bit) 24 | vec3 compress_ycbcr_844 (vec3 rgb) { 25 | vec3 ycbcr = rgb2ycbcr * rgb; 26 | ycbcr.r = floor(ycbcr.r * color_depth + 0.5) / color_depth; 27 | ycbcr.gb += 0.5; 28 | ycbcr.gb = floor(ycbcr.gb * color_number + 0.5) / color_number; 29 | ycbcr.gb -= 0.5; 30 | return ycbcr2rgb * ycbcr; 31 | } 32 | 33 | 34 | void fragment(){ 35 | vec2 uv = FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy; 36 | vec3 rgb = texture(SCREEN_TEXTURE, uv).rgb; 37 | rgb = compress_ycbcr_844(rgb); 38 | 39 | COLOR = vec4(rgb,1.0); 40 | } -------------------------------------------------------------------------------- /shaders/LensDistortion.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "jcant0n" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3, WEBGL 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/4sSSzz 6 | 7 | shader_type canvas_item; 8 | 9 | uniform float strength :hint_range(-0.035, 0.035) = 0.0; 10 | 11 | void fragment(){ 12 | vec2 Resolution = 1.0 / SCREEN_PIXEL_SIZE; 13 | vec2 uv = FRAGCOORD.xy / Resolution.xy; 14 | float aspectRatio = Resolution.x / Resolution.y; 15 | 16 | 17 | vec2 intensity = vec2(strength * aspectRatio); 18 | 19 | vec2 coords = uv; 20 | coords = (coords - 0.5) * 2.0; 21 | 22 | vec2 realCoordOffs; 23 | realCoordOffs.x = (1.0 - coords.y * coords.y) * intensity.y * (coords.x); 24 | realCoordOffs.y = (1.0 - coords.x * coords.x) * intensity.x * (coords.y); 25 | 26 | vec4 color = texture(SCREEN_TEXTURE, uv - realCoordOffs); 27 | 28 | COLOR = vec4(color); 29 | } -------------------------------------------------------------------------------- /shaders/NTSCBasic.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "keijiro" FROM GITHUB 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : MIT 4 | //COMATIBLE WITH : GLES2, GLES3 5 | //GITHUB LINK : https://github.com/keijiro/KinoTube/ 6 | 7 | shader_type canvas_item; 8 | 9 | const float PI = 3.14159265359; 10 | 11 | uniform float _bleeding :hint_range(0, 1) = 0.5; 12 | uniform float _fringing :hint_range(0, 1) = 0.5; 13 | uniform float _scanline :hint_range(0, 1) = 0.5; 14 | uniform bool linearColorSpace = true; 15 | 16 | vec3 LinearToGammaSpace (vec3 linRGB){ 17 | linRGB = max(linRGB, vec3(0, 0, 0)); 18 | return max(1.055 * pow(linRGB, vec3(0.416666667)) - 0.055, 0); 19 | } 20 | vec3 GammaToLinearSpace (vec3 sRGB){ 21 | return sRGB * (sRGB * (sRGB * 0.305306011 + 0.682171111) + 0.012522878); 22 | } 23 | 24 | vec3 RGB2YIQ(vec3 rgb){ 25 | rgb = clamp(rgb, 0, 1); 26 | if (!linearColorSpace){ 27 | rgb = LinearToGammaSpace(rgb); 28 | } 29 | return mat3(vec3(0.299, 0.587, 0.114), 30 | vec3(0.596, -0.274, -0.322), 31 | vec3(0.211, -0.523, 0.313)) * rgb; 32 | } 33 | vec3 YIQ2RGB(vec3 yiq){ 34 | vec3 rgb = mat3(vec3(1, 0.956, 0.621), 35 | vec3(1, -0.272, -0.647), 36 | vec3(1, -1.106, 1.703)) * yiq; 37 | 38 | rgb = clamp(rgb, 0, 1); 39 | if (!linearColorSpace){ 40 | rgb = GammaToLinearSpace(rgb); 41 | } 42 | 43 | return rgb; 44 | } 45 | 46 | vec3 SampleYIQ(vec2 uv, float du, sampler2D _MainTex){ 47 | uv.x += du; 48 | return RGB2YIQ(texture(_MainTex, uv).rgb); 49 | } 50 | 51 | void fragment(){ 52 | float bleedWidth = 0.04 * _bleeding; // width of bleeding 53 | float bleedStep = 2.5 / (1.0 / SCREEN_PIXEL_SIZE).x; // max interval of taps 54 | float bleedTaps = ceil(bleedWidth / bleedStep); 55 | float bleedDelta = bleedWidth / bleedTaps; 56 | float fringeWidth = 0.0025 * _fringing; // width of fringing 57 | 58 | vec2 uv = FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy; 59 | vec3 yiq = SampleYIQ(uv, 0, SCREEN_TEXTURE); 60 | 61 | // Bleeding 62 | for (float i = 0.0; i < bleedTaps; i++) 63 | { 64 | yiq.y += SampleYIQ(uv, - bleedTaps * i, SCREEN_TEXTURE).y; 65 | yiq.z += SampleYIQ(uv, + bleedTaps * i, SCREEN_TEXTURE).z; 66 | } 67 | yiq.yz /= bleedTaps + 1.0; 68 | 69 | // Fringing 70 | float y1 = SampleYIQ(uv, - fringeWidth, SCREEN_TEXTURE).x; 71 | float y2 = SampleYIQ(uv, + fringeWidth, SCREEN_TEXTURE).x; 72 | yiq.yz += y2 - y1; 73 | 74 | // Scanline 75 | float scan = sin(uv.y * 500.0 * PI); 76 | scan = mix(1.0, (scan + 1.0) / 2.0, _scanline); 77 | 78 | COLOR = vec4(YIQ2RGB(yiq * scan), 1); 79 | } -------------------------------------------------------------------------------- /shaders/Sharpness.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "Nihilistic_Furry" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3, WEBGL 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/wsK3Wt 6 | 7 | shader_type canvas_item; 8 | 9 | uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_linear; 10 | uniform float sharpen_amount :hint_range(0,4) = 1.0; 11 | 12 | vec4 sharpenMask (sampler2D st, vec2 fc, vec2 sps){ 13 | // Sharpen detection matrix [0,1,0],[1,-4,1],[0,1,0] 14 | // Colors 15 | vec4 up = texture (st, (fc + vec2 (0, 1))/sps); 16 | vec4 left = texture (st, (fc + vec2 (-1, 0))/sps); 17 | vec4 center = texture (st, fc/sps); 18 | vec4 right = texture (st, (fc + vec2 (1, 0))/sps); 19 | vec4 down = texture (st, (fc + vec2 (0, -1))/sps); 20 | 21 | // Return edge detection 22 | return (1.0 + 4.0*sharpen_amount)*center -sharpen_amount*(up + left + right + down); 23 | } 24 | 25 | void fragment(){ 26 | // Detect edges and output to screen 27 | COLOR = sharpenMask (SCREEN_TEXTURE, FRAGCOORD.xy, 1.0 / SCREEN_PIXEL_SIZE); 28 | } -------------------------------------------------------------------------------- /shaders/SimpleGlitch.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "Gaktan" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/Ms3XWH# 6 | 7 | shader_type canvas_item; 8 | 9 | uniform float range = 0.03; 10 | uniform float noiseQuality :hint_range(0,250) = 250.0; 11 | uniform float noiseIntensity :hint_range(0, 0.05) = 0.005; 12 | uniform float offsetIntensity = 0.01; 13 | uniform float colorOffsetIntensity :hint_range(0,1.5) = 0.3; 14 | 15 | float rand(vec2 co){ 16 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 17 | } 18 | 19 | float verticalBar(float pos, float uvY, float offset){ 20 | float edge0 = (pos - range); 21 | float edge1 = (pos + range); 22 | 23 | float x = smoothstep(edge0, pos, uvY) * offset; 24 | x -= smoothstep(pos, edge1, uvY) * offset; 25 | return x; 26 | } 27 | 28 | void fragment(){ 29 | vec2 uv = FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy; 30 | 31 | for (float i = 0.0; i < 0.71; i += 0.1313){ 32 | float d = mod(TIME - tan(TIME * 0.24 * i), 0); 33 | float o = sin(1.0 - tan(TIME * 0.24 * i)); 34 | o *= offsetIntensity; 35 | uv.x += verticalBar(d, uv.y, o); 36 | } 37 | 38 | float uvY = uv.y; 39 | uvY *= noiseQuality; 40 | uvY = float(int(uvY)) * (1.0 / noiseQuality); 41 | float noise = rand(vec2(TIME * 0.00001, uvY)); 42 | uv.x += noise * noiseIntensity; 43 | 44 | vec2 offsetR = vec2(0.006 * sin(TIME), 0.0) * colorOffsetIntensity; 45 | vec2 offsetG = vec2(0.0073 * (cos(TIME * 0.97)), 0.0) * colorOffsetIntensity; 46 | 47 | float r = texture(SCREEN_TEXTURE, uv + offsetR).r; 48 | float g = texture(SCREEN_TEXTURE, uv + offsetG).g; 49 | float b = texture(SCREEN_TEXTURE, uv).b; 50 | 51 | vec4 tex = vec4(r, g, b, 1.0); 52 | COLOR = tex; 53 | } -------------------------------------------------------------------------------- /shaders/SimpleGrain.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "juniorxsound" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3, WEBGL 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/ldScWw 6 | 7 | shader_type canvas_item; 8 | 9 | uniform float amount :hint_range(0.0, 0.4) = 0.1; 10 | 11 | float grain (vec2 st, float time){ 12 | return fract(sin(dot(st.xy, vec2(17.0,180.)))* 2500. + time); 13 | } 14 | 15 | void fragment(){ 16 | //Coords 17 | vec2 uv = FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy; 18 | 19 | //Produce some noise based on the coords 20 | vec3 grainPlate = vec3(grain(uv, TIME)); 21 | 22 | //Get the image 23 | vec4 img = texture(SCREEN_TEXTURE, uv); 24 | 25 | //Mix the two signals together 26 | vec3 mixer = mix(img.rgb, grainPlate, amount); 27 | 28 | 29 | COLOR = vec4(mixer,1.0); 30 | } -------------------------------------------------------------------------------- /shaders/VHS.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "FMS_Cat" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/XtBXDt 6 | 7 | shader_type canvas_item; 8 | 9 | uniform sampler2D SCREEN_TEXTURE: hint_screen_texture, filter_linear; 10 | uniform float tape_wave_amount :hint_range (0, .04) = 0.003; 11 | uniform float tape_crease_amount :hint_range (0, 15) = 2.5; 12 | uniform float color_displacement :hint_range (0, 5) = 1; 13 | uniform float lines_velocity :hint_range (0, 5) = 0.1; 14 | 15 | //const float PI = 3.14159265; 16 | 17 | vec3 tex2D( sampler2D _tex, vec2 _p ){ 18 | vec3 col = texture( _tex, _p ).xyz; 19 | if ( 0.5 < abs( _p.x - 0.5 ) ) { 20 | col = vec3( 0.1 ); 21 | } 22 | return col; 23 | } 24 | 25 | float hash( vec2 _v ){ 26 | return fract( sin( dot( _v, vec2( 89.44, 19.36 ) ) ) * 22189.22 ); 27 | } 28 | 29 | float iHash( vec2 _v, vec2 _r ){ 30 | float h00 = hash( vec2( floor( _v * _r + vec2( 0.0, 0.0 ) ) / _r ) ); 31 | float h10 = hash( vec2( floor( _v * _r + vec2( 1.0, 0.0 ) ) / _r ) ); 32 | float h01 = hash( vec2( floor( _v * _r + vec2( 0.0, 1.0 ) ) / _r ) ); 33 | float h11 = hash( vec2( floor( _v * _r + vec2( 1.0, 1.0 ) ) / _r ) ); 34 | vec2 ip = vec2( smoothstep( vec2( 0.0, 0.0 ), vec2( 1.0, 1.0 ), mod( _v*_r, 1. ) ) ); 35 | return ( h00 * ( 1. - ip.x ) + h10 * ip.x ) * ( 1. - ip.y ) + ( h01 * ( 1. - ip.x ) + h11 * ip.x ) * ip.y; 36 | } 37 | 38 | float noise( vec2 _v ){ 39 | float sum = 0.; 40 | for( float i=1.0; i<9.0; i++ ){ 41 | sum += iHash( _v + vec2( i ), vec2( 2. * pow( 2., float( i ) ) ) ) / pow( 2., float( i ) ); 42 | } 43 | return sum; 44 | } 45 | 46 | void fragment(){ 47 | vec2 uv = FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy; 48 | vec2 uvn = uv; 49 | vec3 col = vec3( 0.0 ); 50 | 51 | // tape wave 52 | uvn.x += ( noise( vec2( uvn.y, TIME ) ) - 0.5 )* 0.001; 53 | uvn.x += ( noise( vec2( uvn.y * 100.0, TIME * 10.0 ) ) - 0.1 ) * tape_wave_amount; 54 | //uvn.x += ( noise( vec2( uvn.y * 100.0, TIME * 10.0 ) ) - 0.5 ) * tape_wave_amount; 55 | 56 | // tape crease 57 | float tcPhase = clamp( ( sin( uvn.y * 8.0 - TIME * PI * 1.2 ) - 0.92 ) * noise( vec2( TIME ) ), 0.0, 0.01 ) * tape_crease_amount; 58 | float tcNoise = max( noise( vec2( uvn.y * 100.0, TIME * 10.0 ) ) - 0.5, 0.0 ); 59 | uvn.x = uvn.x - tcNoise * tcPhase; 60 | 61 | // switching noise 62 | float snPhase = smoothstep( 0.03, 0.0, uvn.y ); 63 | uvn.y += snPhase * 0.3; 64 | uvn.x += snPhase * ( ( noise( vec2( uv.y * 100.0, TIME * 10.0 ) ) - 0.5 ) * 0.2 ); 65 | 66 | col = tex2D( SCREEN_TEXTURE, uvn ); 67 | col *= 1.0 - tcPhase; 68 | col = mix( 69 | col, 70 | col.yzx, 71 | snPhase 72 | ); 73 | 74 | // bloom 75 | for( float x = -4.0; x < 2.5; x += 1.0 ){ 76 | col.xyz += vec3( 77 | tex2D( SCREEN_TEXTURE, uvn + vec2( x - 0.0, 0.0 ) * 0.007 ).x, 78 | tex2D( SCREEN_TEXTURE, uvn + vec2( x - color_displacement, 0.0 ) * 0.007 ).y, 79 | tex2D( SCREEN_TEXTURE, uvn + vec2( x - color_displacement * 2.0, 0.0 ) * 0.007 ).z 80 | ) * 0.05; 81 | } 82 | col *= 0.6; 83 | 84 | // ac beat 85 | col *= 1.0 + clamp( noise( vec2( 0.0, uv.y + TIME * lines_velocity ) ) * 0.6 - 0.25, 0.0, 0.1 ); 86 | 87 | COLOR = vec4( col, 1.0 ); 88 | } -------------------------------------------------------------------------------- /shaders/VHSPause.gdshader: -------------------------------------------------------------------------------- 1 | //SHADER ORIGINALY CREADED BY "caaaaaaarter" FROM SHADERTOY 2 | //PORTED AND MODIFYED TO GODOT BY AHOPNESS (@ahopness) 3 | //LICENSE : CC0 4 | //COMATIBLE WITH : GLES2, GLES3 5 | //SHADERTOY LINK : https://www.shadertoy.com/view/4lB3Dc 6 | 7 | shader_type canvas_item; 8 | 9 | uniform float shake_amount_x : hint_range(1, 500) = 250.0; 10 | uniform float shake_amount_y : hint_range(1, 500) = 40.0; 11 | uniform float white_hlines : hint_range(0, 50) = 50; 12 | uniform float white_vlines : hint_range(0,80) = 80; 13 | 14 | float rand(vec2 co){ 15 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 16 | } 17 | 18 | void fragment(){ 19 | vec4 texColor = vec4(0); 20 | // get position to sample 21 | vec2 samplePosition = FRAGCOORD.xy / (1.0 / SCREEN_PIXEL_SIZE).xy; 22 | 23 | float whiteNoise = 9999.0; 24 | 25 | // Jitter each line left and right 26 | samplePosition.x = samplePosition.x+(rand(vec2(TIME,UV.y))-0.5)/shake_amount_x; 27 | // Jitter the whole picture up and down 28 | samplePosition.y = samplePosition.y+(rand(vec2(TIME))-0.5)/shake_amount_y; 29 | // Slightly add color noise to each line 30 | texColor = texColor + (vec4(-0.5)+vec4(rand(vec2(UV.y,TIME)),rand(vec2(UV.y,TIME+1.0)),rand(vec2(UV.y,TIME+2.0)),0))*0.1; 31 | 32 | // Either sample the texture, or just make the pixel white (to get the staticy-bit at the bottom) 33 | whiteNoise = rand(vec2(floor(samplePosition.y*white_vlines),floor(samplePosition.x*white_hlines))+vec2(TIME,0)); 34 | if (whiteNoise > 11.5-30.0*samplePosition.y || whiteNoise < 1.5-5.0*samplePosition.y) { 35 | // Sample the texture. 36 | //samplePosition.y = 1.0-samplePosition.y; //Fix for upside-down texture 37 | texColor = texColor + texture(SCREEN_TEXTURE,samplePosition); 38 | }else{ 39 | // Use white. (I'm adding here so the color noise still applies) 40 | texColor = vec4(1); 41 | } 42 | COLOR = texColor; 43 | } -------------------------------------------------------------------------------- /shaders/gpsx/2d/gpsx_2d.gdshader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | render_mode blend_disabled; 3 | 4 | uniform float alpha_cutoff : hint_range(0.0, 1.0, 0.01) = 0.1; 5 | uniform bool dithering = true; 6 | uniform bool banding = true; 7 | 8 | const float neareight = 255.0; 9 | 10 | // Note: Based off emulator PS1 GPU code 11 | // It's widespread enough you need to only look at your favorite one's 12 | vec3 dither(vec3 col, uvec2 fc) 13 | { 14 | const int mat[16] = int[16] ( 15 | -4, 0, -3, 1, 16 | 2, -2, 3, -1, 17 | -3, 1, -4, 0, 18 | 3, -1, 2, -2 19 | ); 20 | 21 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 22 | return vec3(uncol) / neareight; 23 | } 24 | 25 | const float finaldepth = 32.0; 26 | 27 | // I have no need for anything under/around 15-bit 28 | // If higher, only 24-bit, and at that point just disable 29 | // it lol 30 | vec3 band_color(vec3 lol) 31 | { 32 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 33 | ivec3 ires = res >> 3; 34 | ivec3 dres = res & ivec3(7); 35 | res = (ires << 3) | dres; 36 | return vec3(res) / neareight; 37 | } 38 | void fragment(){ 39 | if (COLOR.a < alpha_cutoff) 40 | discard; 41 | if(dithering) 42 | COLOR.rgb = dither(COLOR.rgb, uvec2(FRAGCOORD.xy)); 43 | if(banding) 44 | COLOR.rgb = band_color(COLOR.rgb); 45 | } 46 | -------------------------------------------------------------------------------- /shaders/gpsx/2d/gpsx_2d_add.gdshader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | render_mode blend_add; 3 | 4 | uniform float alpha_cutoff : hint_range(0.0, 1.0, 0.01) = 0.1; 5 | uniform bool dithering = true; 6 | uniform bool banding = true; 7 | 8 | const float neareight = 255.0; 9 | 10 | // Note: Based off emulator PS1 GPU code 11 | // It's widespread enough you need to only look at your favorite one's 12 | vec3 dither(vec3 col, uvec2 fc) 13 | { 14 | const int mat[16] = int[16] ( 15 | -4, 0, -3, 1, 16 | 2, -2, 3, -1, 17 | -3, 1, -4, 0, 18 | 3, -1, 2, -2 19 | ); 20 | 21 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 22 | return vec3(uncol) / neareight; 23 | } 24 | 25 | const float finaldepth = 32.0; 26 | 27 | // I have no need for anything under/around 15-bit 28 | // If higher, only 24-bit, and at that point just disable 29 | // it lol 30 | vec3 band_color(vec3 lol) 31 | { 32 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 33 | ivec3 ires = res >> 3; 34 | ivec3 dres = res & ivec3(7); 35 | res = (ires << 3) | dres; 36 | return vec3(res) / neareight; 37 | } 38 | 39 | void fragment(){ 40 | if (COLOR.a < alpha_cutoff) 41 | discard; 42 | if(dithering) 43 | COLOR.rgb = dither(COLOR.rgb, uvec2(FRAGCOORD.xy)); 44 | if(banding) 45 | COLOR.rgb = band_color(COLOR.rgb); 46 | COLOR.a = 1.0; 47 | } 48 | -------------------------------------------------------------------------------- /shaders/gpsx/2d/gpsx_2d_avg.gdshader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | 3 | uniform float alpha_cutoff : hint_range(0.0, 1.0, 0.01) = 0.1; 4 | uniform bool dithering = true; 5 | uniform bool banding = true; 6 | 7 | const float neareight = 255.0; 8 | 9 | // Note: Based off emulator PS1 GPU code 10 | // It's widespread enough you need to only look at your favorite one's 11 | vec3 dither(vec3 col, uvec2 fc) 12 | { 13 | const int mat[16] = int[16] ( 14 | -4, 0, -3, 1, 15 | 2, -2, 3, -1, 16 | -3, 1, -4, 0, 17 | 3, -1, 2, -2 18 | ); 19 | 20 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 21 | return vec3(uncol) / neareight; 22 | } 23 | 24 | const float finaldepth = 32.0; 25 | 26 | // I have no need for anything under/around 15-bit 27 | // If higher, only 24-bit, and at that point just disable 28 | // it lol 29 | vec3 band_color(vec3 lol) 30 | { 31 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 32 | ivec3 ires = res >> 3; 33 | ivec3 dres = res & ivec3(7); 34 | res = (ires << 3) | dres; 35 | return vec3(res) / neareight; 36 | } 37 | 38 | void fragment(){ 39 | if (COLOR.a < alpha_cutoff) 40 | discard; 41 | if(dithering) 42 | COLOR.rgb = dither(COLOR.rgb, uvec2(FRAGCOORD.xy)); 43 | if(banding) 44 | COLOR.rgb = band_color(COLOR.rgb); 45 | COLOR.a = 0.5; 46 | } 47 | -------------------------------------------------------------------------------- /shaders/gpsx/2d/gpsx_2d_qadd.gdshader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | render_mode blend_add; 3 | 4 | uniform float alpha_cutoff : hint_range(0.0, 1.0, 0.01) = 0.1; 5 | uniform bool dithering = true; 6 | uniform bool banding = true; 7 | 8 | const float neareight = 255.0; 9 | 10 | // Note: Based off emulator PS1 GPU code 11 | // It's widespread enough you need to only look at your favorite one's 12 | vec3 dither(vec3 col, uvec2 fc) 13 | { 14 | const int mat[16] = int[16] ( 15 | -4, 0, -3, 1, 16 | 2, -2, 3, -1, 17 | -3, 1, -4, 0, 18 | 3, -1, 2, -2 19 | ); 20 | 21 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 22 | return vec3(uncol) / neareight; 23 | } 24 | 25 | const float finaldepth = 32.0; 26 | 27 | // I have no need for anything under/around 15-bit 28 | // If higher, only 24-bit, and at that point just disable 29 | // it lol 30 | vec3 band_color(vec3 lol) 31 | { 32 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 33 | ivec3 ires = res >> 3; 34 | ivec3 dres = res & ivec3(7); 35 | res = (ires << 3) | dres; 36 | return vec3(res) / neareight; 37 | } 38 | 39 | void fragment(){ 40 | if (COLOR.a < alpha_cutoff) 41 | discard; 42 | if(dithering) 43 | COLOR.rgb = dither(COLOR.rgb, uvec2(FRAGCOORD.xy)); 44 | if(banding) 45 | COLOR.rgb = band_color(COLOR.rgb); 46 | COLOR.a = 0.25; 47 | } 48 | -------------------------------------------------------------------------------- /shaders/gpsx/2d/gpsx_2d_sub.gdshader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | render_mode blend_sub; 3 | 4 | uniform float alpha_cutoff : hint_range(0.0, 1.0, 0.01) = 0.1; 5 | uniform bool dithering = true; 6 | uniform bool banding = true; 7 | 8 | const float neareight = 255.0; 9 | 10 | // Note: Based off emulator PS1 GPU code 11 | // It's widespread enough you need to only look at your favorite one's 12 | vec3 dither(vec3 col, uvec2 fc) 13 | { 14 | const int mat[16] = int[16] ( 15 | -4, 0, -3, 1, 16 | 2, -2, 3, -1, 17 | -3, 1, -4, 0, 18 | 3, -1, 2, -2 19 | ); 20 | 21 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 22 | return vec3(uncol) / neareight; 23 | } 24 | 25 | const float finaldepth = 32.0; 26 | 27 | // I have no need for anything under/around 15-bit 28 | // If higher, only 24-bit, and at that point just disable 29 | // it lol 30 | vec3 band_color(vec3 lol) 31 | { 32 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 33 | ivec3 ires = res >> 3; 34 | ivec3 dres = res & ivec3(7); 35 | res = (ires << 3) | dres; 36 | return vec3(res) / neareight; 37 | } 38 | 39 | void fragment(){ 40 | if (COLOR.a < alpha_cutoff) 41 | discard; 42 | if(dithering) 43 | COLOR.rgb = dither(COLOR.rgb, uvec2(FRAGCOORD.xy)); 44 | if(banding) 45 | COLOR.rgb = band_color(COLOR.rgb); 46 | COLOR.a = 1.0; 47 | } 48 | -------------------------------------------------------------------------------- /shaders/gpsx/3d/gpsx_3d.gdshader: -------------------------------------------------------------------------------- 1 | shader_type spatial; 2 | render_mode skip_vertex_transform, unshaded, cull_disabled; 3 | 4 | instance uniform vec4 mixer : source_color = vec4(1.0); 5 | 6 | group_uniforms Texture; 7 | uniform sampler2D tex : source_color, hint_default_white, filter_nearest; 8 | uniform float alpha_cutoff : hint_range(0.0, 1.0, 0.01) = 0.1; 9 | group_uniforms; 10 | 11 | group_uniforms Accuracy; 12 | uniform float psx_fixed_point_precision = 48.16; 13 | uniform bool dithering = false; 14 | uniform bool banding = false; 15 | group_uniforms; 16 | 17 | group_uniforms Fog; 18 | uniform bool fog = false; 19 | uniform vec3 fog_color : source_color = vec3(0.0); 20 | uniform float dist_fade_min = 0.0; 21 | uniform float dist_fade_max = 10.0; 22 | group_uniforms; 23 | 24 | varying float dist; 25 | 26 | void vertex() 27 | { 28 | // Vertex snapping 29 | // based on https://github.com/BroMandarin/unity_lwrp_psx_shader/blob/master/PS1.shader 30 | float vertex_snap_step = psx_fixed_point_precision * 2.0; 31 | vec4 snap_to_pixel = PROJECTION_MATRIX * MODELVIEW_MATRIX * vec4(VERTEX, 1.0); 32 | vec4 clip_vertex = snap_to_pixel; 33 | clip_vertex.xyz = snap_to_pixel.xyz / snap_to_pixel.w; 34 | clip_vertex.x = floor(vertex_snap_step * clip_vertex.x) / vertex_snap_step; 35 | clip_vertex.y = floor(vertex_snap_step * clip_vertex.y) / vertex_snap_step; 36 | clip_vertex.xyz *= snap_to_pixel.w; 37 | POSITION = clip_vertex; 38 | POSITION /= abs(POSITION.w); 39 | NORMAL = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz; 40 | 41 | if (fog) 42 | dist = clamp(smoothstep(dist_fade_min, dist_fade_max, length(clip_vertex)), 0.0, 1.0); 43 | } 44 | 45 | const float neareight = 255.0; 46 | 47 | // Note: Based off emulator PS1 GPU code 48 | // It's widespread enough you need to only look at your favorite one's 49 | vec3 dither(vec3 col, uvec2 fc) 50 | { 51 | const int mat[16] = int[16] ( 52 | -4, 0, -3, 1, 53 | 2, -2, 3, -1, 54 | -3, 1, -4, 0, 55 | 3, -1, 2, -2 56 | ); 57 | 58 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 59 | return vec3(uncol) / neareight; 60 | } 61 | 62 | const float finaldepth = 32.0; 63 | 64 | // I have no need for anything under/around 15-bit 65 | // If higher, only 24-bit, and at that point just disable 66 | // it lol 67 | vec3 band_color(vec3 lol) 68 | { 69 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 70 | ivec3 ires = res >> 3; 71 | ivec3 dres = res & ivec3(7); 72 | res = (ires << 3) | dres; 73 | return vec3(res) / neareight; 74 | } 75 | 76 | void fragment(){ 77 | mediump vec4 col = (texture(tex, UV) * mixer) * COLOR; 78 | if (col.a < alpha_cutoff) 79 | discard; 80 | if (fog) 81 | col.rgb = mix(col.rgb, fog_color, dist); 82 | ALBEDO = col.rgb; 83 | if(dithering) 84 | ALBEDO = dither(ALBEDO, uvec2(FRAGCOORD.xy)); 85 | if(banding) 86 | ALBEDO = band_color(ALBEDO); 87 | } -------------------------------------------------------------------------------- /shaders/gpsx/3d/gpsx_3d_add.gdshader: -------------------------------------------------------------------------------- 1 | shader_type spatial; 2 | render_mode skip_vertex_transform, unshaded, blend_add; 3 | 4 | instance uniform vec4 mixer : source_color = vec4(1.0); 5 | 6 | group_uniforms Texture; 7 | uniform sampler2D tex : source_color, hint_default_white, filter_nearest; 8 | uniform float alpha_cutoff : hint_range(0.0, 1.0, 0.01) = 0.1; 9 | group_uniforms; 10 | 11 | group_uniforms Accuracy; 12 | uniform float psx_fixed_point_precision = 48.16; 13 | uniform bool dithering = true; 14 | uniform bool banding = true; 15 | group_uniforms; 16 | 17 | group_uniforms Fog; 18 | uniform bool fog = false; 19 | uniform vec3 fog_color : source_color = vec3(0.0); 20 | uniform float dist_fade_min = 0.0; 21 | uniform float dist_fade_max = 10.0; 22 | group_uniforms; 23 | 24 | varying flat float dist; 25 | void vertex() 26 | { 27 | // Vertex snapping 28 | // based on https://github.com/BroMandarin/unity_lwrp_psx_shader/blob/master/PS1.shader 29 | float vertex_snap_step = psx_fixed_point_precision * 2.0; 30 | vec4 snap_to_pixel = PROJECTION_MATRIX * MODELVIEW_MATRIX * vec4(VERTEX, 1.0); 31 | vec4 clip_vertex = snap_to_pixel; 32 | clip_vertex.xyz = snap_to_pixel.xyz / snap_to_pixel.w; 33 | clip_vertex.x = floor(vertex_snap_step * clip_vertex.x) / vertex_snap_step; 34 | clip_vertex.y = floor(vertex_snap_step * clip_vertex.y) / vertex_snap_step; 35 | clip_vertex.xyz *= snap_to_pixel.w; 36 | POSITION = clip_vertex; 37 | POSITION /= abs(POSITION.w); 38 | NORMAL = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz; 39 | 40 | if (fog) 41 | dist = length(clip_vertex); 42 | } 43 | 44 | const float neareight = 255.0; 45 | 46 | // Note: Based off emulator PS1 GPU code 47 | // It's widespread enough you need to only look at your favorite one's 48 | vec3 dither(vec3 col, uvec2 fc) 49 | { 50 | const int mat[16] = int[16] ( 51 | -4, 0, -3, 1, 52 | 2, -2, 3, -1, 53 | -3, 1, -4, 0, 54 | 3, -1, 2, -2 55 | ); 56 | 57 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 58 | return vec3(uncol) / neareight; 59 | } 60 | 61 | const float finaldepth = 32.0; 62 | 63 | // I have no need for anything under/around 15-bit 64 | // If higher, only 24-bit, and at that point just disable 65 | // it lol 66 | vec3 band_color(vec3 lol) 67 | { 68 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 69 | ivec3 ires = res >> 3; 70 | ivec3 dres = res & ivec3(7); 71 | res = (ires << 3) | dres; 72 | return vec3(res) / neareight; 73 | } 74 | 75 | void fragment(){ 76 | vec4 col = (texture(tex, UV) * mixer) * COLOR; 77 | if (col.a < alpha_cutoff) 78 | discard; 79 | if (fog) 80 | col.rgb = mix(col.rgb, fog_color, clamp(smoothstep(dist_fade_min, dist_fade_max, dist), 0.0, 1.0)); 81 | ALBEDO = col.rgb; 82 | if(dithering) 83 | ALBEDO = dither(ALBEDO, uvec2(FRAGCOORD.xy)); 84 | if(banding) 85 | ALBEDO = band_color(ALBEDO); 86 | } -------------------------------------------------------------------------------- /shaders/gpsx/3d/gpsx_3d_avg.gdshader: -------------------------------------------------------------------------------- 1 | shader_type spatial; 2 | render_mode skip_vertex_transform, unshaded, cull_disabled, blend_mix; 3 | 4 | instance uniform vec4 mixer : source_color = vec4(1.0); 5 | 6 | group_uniforms Texture; 7 | uniform sampler2D tex : source_color, hint_default_white, filter_nearest; 8 | uniform float alpha_cutoff : hint_range(0.0, 1.0, 0.01) = 0.1; 9 | group_uniforms; 10 | 11 | group_uniforms Accuracy; 12 | uniform float psx_fixed_point_precision = 48.16; 13 | uniform bool dithering = true; 14 | uniform bool banding = true; 15 | group_uniforms; 16 | 17 | group_uniforms Fog; 18 | uniform bool fog = false; 19 | uniform vec3 fog_color : source_color = vec3(0.0); 20 | uniform float dist_fade_min = 0.0; 21 | uniform float dist_fade_max = 10.0; 22 | group_uniforms; 23 | 24 | varying flat float dist; 25 | void vertex() 26 | { 27 | // Vertex snapping 28 | // based on https://github.com/BroMandarin/unity_lwrp_psx_shader/blob/master/PS1.shader 29 | float vertex_snap_step = psx_fixed_point_precision * 2.0; 30 | vec4 snap_to_pixel = PROJECTION_MATRIX * MODELVIEW_MATRIX * vec4(VERTEX, 1.0); 31 | vec4 clip_vertex = snap_to_pixel; 32 | clip_vertex.xyz = snap_to_pixel.xyz / snap_to_pixel.w; 33 | clip_vertex.x = floor(vertex_snap_step * clip_vertex.x) / vertex_snap_step; 34 | clip_vertex.y = floor(vertex_snap_step * clip_vertex.y) / vertex_snap_step; 35 | clip_vertex.xyz *= snap_to_pixel.w; 36 | POSITION = clip_vertex; 37 | POSITION /= abs(POSITION.w); 38 | NORMAL = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz; 39 | 40 | if (fog) 41 | dist = length(clip_vertex); 42 | } 43 | 44 | const float neareight = 255.0; 45 | 46 | // Note: Based off emulator PS1 GPU code 47 | // It's widespread enough you need to only look at your favorite one's 48 | vec3 dither(vec3 col, uvec2 fc) 49 | { 50 | const int mat[16] = int[16] ( 51 | -4, 0, -3, 1, 52 | 2, -2, 3, -1, 53 | -3, 1, -4, 0, 54 | 3, -1, 2, -2 55 | ); 56 | 57 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 58 | return vec3(uncol) / neareight; 59 | } 60 | 61 | const float finaldepth = 32.0; 62 | 63 | // I have no need for anything under/around 15-bit 64 | // If higher, only 24-bit, and at that point just disable 65 | // it lol 66 | vec3 band_color(vec3 lol) 67 | { 68 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 69 | ivec3 ires = res >> 3; 70 | ivec3 dres = res & ivec3(7); 71 | res = (ires << 3) | dres; 72 | return vec3(res) / neareight; 73 | } 74 | 75 | void fragment(){ 76 | vec4 col = (texture(tex, UV) * mixer) * COLOR; 77 | if (col.a < alpha_cutoff) 78 | discard; 79 | if (fog) 80 | col.rgb = mix(col.rgb, fog_color, clamp(smoothstep(dist_fade_min, dist_fade_max, dist), 0.0, 1.0)); 81 | ALBEDO = col.rgb; 82 | if(dithering) 83 | ALBEDO = dither(ALBEDO, uvec2(FRAGCOORD.xy)); 84 | if(banding) 85 | ALBEDO = band_color(ALBEDO); 86 | ALPHA = 0.5; 87 | } -------------------------------------------------------------------------------- /shaders/gpsx/3d/gpsx_3d_qadd.gdshader: -------------------------------------------------------------------------------- 1 | shader_type spatial; 2 | render_mode skip_vertex_transform, unshaded, blend_add; 3 | 4 | instance uniform vec4 mixer : source_color = vec4(1.0); 5 | 6 | group_uniforms Texture; 7 | uniform sampler2D tex : source_color, hint_default_white, filter_nearest; 8 | uniform float alpha_cutoff : hint_range(0.0, 1.0, 0.01) = 0.1; 9 | group_uniforms; 10 | 11 | group_uniforms Accuracy; 12 | uniform float psx_fixed_point_precision = 48.16; 13 | uniform bool dithering = true; 14 | uniform bool banding = true; 15 | group_uniforms; 16 | 17 | group_uniforms Fog; 18 | uniform bool fog = false; 19 | uniform vec3 fog_color : source_color = vec3(0.0); 20 | uniform float dist_fade_min = 0.0; 21 | uniform float dist_fade_max = 10.0; 22 | group_uniforms; 23 | 24 | varying flat float dist; 25 | void vertex() 26 | { 27 | // Vertex snapping 28 | // based on https://github.com/BroMandarin/unity_lwrp_psx_shader/blob/master/PS1.shader 29 | float vertex_snap_step = psx_fixed_point_precision * 2.0; 30 | vec4 snap_to_pixel = PROJECTION_MATRIX * MODELVIEW_MATRIX * vec4(VERTEX, 1.0); 31 | vec4 clip_vertex = snap_to_pixel; 32 | clip_vertex.xyz = snap_to_pixel.xyz / snap_to_pixel.w; 33 | clip_vertex.x = floor(vertex_snap_step * clip_vertex.x) / vertex_snap_step; 34 | clip_vertex.y = floor(vertex_snap_step * clip_vertex.y) / vertex_snap_step; 35 | clip_vertex.xyz *= snap_to_pixel.w; 36 | POSITION = clip_vertex; 37 | POSITION /= abs(POSITION.w); 38 | NORMAL = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz; 39 | 40 | if (fog) 41 | dist = length(clip_vertex); 42 | } 43 | 44 | const float neareight = 255.0; 45 | 46 | // Note: Based off emulator PS1 GPU code 47 | // It's widespread enough you need to only look at your favorite one's 48 | vec3 dither(vec3 col, uvec2 fc) 49 | { 50 | const int mat[16] = int[16] ( 51 | -4, 0, -3, 1, 52 | 2, -2, 3, -1, 53 | -3, 1, -4, 0, 54 | 3, -1, 2, -2 55 | ); 56 | 57 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 58 | return vec3(uncol) / neareight; 59 | } 60 | 61 | const float finaldepth = 32.0; 62 | 63 | // I have no need for anything under/around 15-bit 64 | // If higher, only 24-bit, and at that point just disable 65 | // it lol 66 | vec3 band_color(vec3 lol) 67 | { 68 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 69 | ivec3 ires = res >> 3; 70 | ivec3 dres = res & ivec3(7); 71 | res = (ires << 3) | dres; 72 | return vec3(res) / neareight; 73 | } 74 | 75 | void fragment(){ 76 | vec4 col = (texture(tex, UV) * mixer) * COLOR; 77 | if (col.a < alpha_cutoff) 78 | discard; 79 | if (fog) 80 | col.rgb = mix(col.rgb, fog_color, clamp(smoothstep(dist_fade_min, dist_fade_max, dist), 0.0, 1.0)); 81 | ALBEDO = col.rgb; 82 | if(dithering) 83 | ALBEDO = dither(ALBEDO, uvec2(FRAGCOORD.xy)); 84 | if(banding) 85 | ALBEDO = band_color(ALBEDO); 86 | ALPHA = 0.25; 87 | } -------------------------------------------------------------------------------- /shaders/gpsx/3d/gpsx_3d_sub.gdshader: -------------------------------------------------------------------------------- 1 | shader_type spatial; 2 | render_mode skip_vertex_transform, unshaded, blend_sub; 3 | 4 | instance uniform vec4 mixer : source_color = vec4(1.0); 5 | 6 | group_uniforms Texture; 7 | uniform sampler2D tex : source_color, hint_default_white, filter_nearest; 8 | uniform float alpha_cutoff : hint_range(0.0, 1.0, 0.01) = 0.1; 9 | group_uniforms; 10 | 11 | group_uniforms Accuracy; 12 | uniform float psx_fixed_point_precision = 48.16; 13 | uniform bool dithering = true; 14 | uniform bool banding = true; 15 | group_uniforms; 16 | 17 | group_uniforms Fog; 18 | uniform bool fog = false; 19 | uniform vec3 fog_color : source_color = vec3(0.0); 20 | uniform float dist_fade_min = 0.0; 21 | uniform float dist_fade_max = 10.0; 22 | group_uniforms; 23 | 24 | varying flat float dist; 25 | void vertex() 26 | { 27 | // Vertex snapping 28 | // based on https://github.com/BroMandarin/unity_lwrp_psx_shader/blob/master/PS1.shader 29 | float vertex_snap_step = psx_fixed_point_precision * 2.0; 30 | vec4 snap_to_pixel = PROJECTION_MATRIX * MODELVIEW_MATRIX * vec4(VERTEX, 1.0); 31 | vec4 clip_vertex = snap_to_pixel; 32 | clip_vertex.xyz = snap_to_pixel.xyz / snap_to_pixel.w; 33 | clip_vertex.x = floor(vertex_snap_step * clip_vertex.x) / vertex_snap_step; 34 | clip_vertex.y = floor(vertex_snap_step * clip_vertex.y) / vertex_snap_step; 35 | clip_vertex.xyz *= snap_to_pixel.w; 36 | POSITION = clip_vertex; 37 | POSITION /= abs(POSITION.w); 38 | NORMAL = (MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz; 39 | 40 | if (fog) 41 | dist = length(clip_vertex); 42 | } 43 | 44 | const float neareight = 255.0; 45 | 46 | // Note: Based off emulator PS1 GPU code 47 | // It's widespread enough you need to only look at your favorite one's 48 | vec3 dither(vec3 col, uvec2 fc) 49 | { 50 | const int mat[16] = int[16] ( 51 | -4, 0, -3, 1, 52 | 2, -2, 3, -1, 53 | -3, 1, -4, 0, 54 | 3, -1, 2, -2 55 | ); 56 | 57 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 58 | return vec3(uncol) / neareight; 59 | } 60 | 61 | const float finaldepth = 32.0; 62 | 63 | // I have no need for anything under/around 15-bit 64 | // If higher, only 24-bit, and at that point just disable 65 | // it lol 66 | vec3 band_color(vec3 lol) 67 | { 68 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 69 | ivec3 ires = res >> 3; 70 | ivec3 dres = res & ivec3(7); 71 | res = (ires << 3) | dres; 72 | return vec3(res) / neareight; 73 | } 74 | 75 | void fragment(){ 76 | vec4 col = (texture(tex, UV) * mixer) * COLOR; 77 | if (col.a < alpha_cutoff) 78 | discard; 79 | if (fog) 80 | col.rgb = mix(col.rgb, fog_color, clamp(smoothstep(dist_fade_min, dist_fade_max, dist), 0.0, 1.0)); 81 | ALBEDO = col.rgb; 82 | if(dithering) 83 | ALBEDO = dither(ALBEDO, uvec2(FRAGCOORD.xy)); 84 | if(banding) 85 | ALBEDO = band_color(ALBEDO); 86 | } -------------------------------------------------------------------------------- /shaders/gpsx/misc/gpsx_sky.gdshader: -------------------------------------------------------------------------------- 1 | shader_type sky; 2 | 3 | group_uniforms Texture; 4 | uniform sampler2D tex : source_color, hint_default_white, filter_nearest; 5 | uniform vec4 mixer : source_color = vec4(1.0); 6 | group_uniforms; 7 | 8 | group_uniforms Accuracy; 9 | uniform bool dithering = true; 10 | uniform bool banding = true; 11 | group_uniforms; 12 | 13 | const float neareight = 255.0; 14 | 15 | // Note: Based off emulator PS1 GPU code 16 | // It's widespread enough you need to only look at your favorite one's 17 | vec3 dither(vec3 col, uvec2 fc) 18 | { 19 | const int mat[16] = int[16] ( 20 | -4, 0, -3, 1, 21 | 2, -2, 3, -1, 22 | -3, 1, -4, 0, 23 | 3, -1, 2, -2 24 | ); 25 | 26 | ivec3 uncol = ivec3(col * neareight) + mat[(fc.y & uint(3)) * uint(4) + (fc.x & uint(3))]; 27 | return vec3(uncol) / neareight; 28 | } 29 | 30 | const float finaldepth = 32.0; 31 | 32 | // I have no need for anything under/around 15-bit 33 | // If higher, only 24-bit, and at that point just disable 34 | // it lol 35 | vec3 band_color(vec3 lol) 36 | { 37 | ivec3 res = ivec3(clamp(lol, 0.0, 1.0) * neareight) & ivec3(0xFF); 38 | ivec3 ires = res >> 3; 39 | ivec3 dres = res & ivec3(7); 40 | res = (ires << 3) | dres; 41 | return vec3(res) / neareight; 42 | } 43 | 44 | void sky() { 45 | COLOR = texture(tex, SKY_COORDS).rgb * mixer.rgb; 46 | if(dithering) 47 | COLOR = dither(COLOR, uvec2(FRAGCOORD.xy)); 48 | if(banding) 49 | COLOR = band_color(COLOR); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /shaders/grain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/shaders/grain.jpg -------------------------------------------------------------------------------- /shaders/grain.jpg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://wt2wa3awvhpj" 6 | path="res://.godot/imported/grain.jpg-20b8144c6e09476e2ec154fa2cc41f5c.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://shaders/grain.jpg" 14 | dest_files=["res://.godot/imported/grain.jpg-20b8144c6e09476e2ec154fa2cc41f5c.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /shaders/lcd_post_process.gdshader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | 3 | uniform bool enabled = true; 4 | uniform float lcd_opacity = 0.5; 5 | uniform int scanline_gap = 5; 6 | 7 | // http://theorangeduck.com/page/avoiding-shader-conditionals 8 | float when_eq(int x, int y) 9 | { 10 | return 1.0 - abs(sign(float(x) - float(y))); 11 | } 12 | 13 | vec4 lcdColor(int pos_x, int pos_y) 14 | { 15 | vec4 lcdColor = vec4(1); 16 | // Change every 1st, 2nd, and 3rd vertical strip to RGB respectively 17 | // if (px == 1) lcdColor.r = 1.0; 18 | // else if (px == 2) lcdColor.g = 1.0; 19 | // else lcdColor.b = 1.0; 20 | lcdColor.r = lcdColor.r * when_eq(pos_x, 0); 21 | lcdColor.g = lcdColor.g * when_eq(pos_x, 1); 22 | lcdColor.b = lcdColor.b * when_eq(pos_x, 2); 23 | 24 | // Darken every 3rd horizontal strip for scanline 25 | // if (int(mod(FRAGCOORD.y,3.0)) == 0) lcdColor.rgb = vec3(0); 26 | lcdColor.rgb = lcdColor.rgb * vec3(1.0 - when_eq(pos_y, 0)); 27 | 28 | return lcdColor; 29 | } 30 | 31 | void fragment() 32 | { 33 | vec4 tex = texture(TEXTURE,UV); 34 | int pos_x = int(mod(FRAGCOORD.x, 3.0)); 35 | int pos_y = int(mod(FRAGCOORD.y, float(scanline_gap))); 36 | vec4 lcd_overlay_tex = lcdColor(pos_x, pos_y); 37 | COLOR = enabled ? mix(tex, tex * lcd_overlay_tex, lcd_opacity) : tex; 38 | } -------------------------------------------------------------------------------- /shaders/psx_dither.gdshader: -------------------------------------------------------------------------------- 1 | //*** *** *** *** *** *** *** *** *** *** *** ***// 2 | //*** === PSX dither effect === ***// 3 | //*** === For Godot 4.0 === ***// 4 | //*** === By AestheticalZ === ***// 5 | //*** === https://github.com/AestheticalZ === ***// 6 | //*** *** *** *** *** *** *** *** *** *** *** ***// 7 | //*** === Licensed under the: === ***// 8 | //*** === MIT License === ***// 9 | //*** *** *** *** *** *** *** *** *** *** *** ***// 10 | //*** === Derived from CC0 work by: === ***// 11 | //*** === Mighty Duke === ***// 12 | //*** *** *** *** *** *** *** *** *** *** *** ***// 13 | 14 | /***************************************************************************** 15 | MIT License 16 | 17 | Copyright (c) 2022 AestheticalZ 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in all 27 | copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35 | SOFTWARE. 36 | *****************************************************************************/ 37 | 38 | shader_type canvas_item; 39 | 40 | uniform int color_depth : hint_range(1, 8) = 5; 41 | uniform bool dithering = true; 42 | uniform int resolution_scale = 4; 43 | 44 | int dithering_pattern(ivec2 fragcoord) { 45 | const int pattern[] = { 46 | -4, +0, -3, +1, 47 | +2, -2, +3, -1, 48 | -3, +1, -4, +0, 49 | +3, -1, +2, -2 50 | }; 51 | 52 | int x = fragcoord.x % 4; 53 | int y = fragcoord.y % 4; 54 | 55 | return pattern[y * 4 + x]; 56 | } 57 | 58 | void fragment() { 59 | ivec2 uv = ivec2(FRAGCOORD.xy / float(resolution_scale)); 60 | vec3 color = texelFetch(TEXTURE, uv * resolution_scale, 0).rgb; 61 | 62 | // Convert from 0 to 1 to 0 to 255. 63 | ivec3 c = ivec3(round(color * 255.0)); 64 | 65 | // Apply the dithering. 66 | if (dithering) { 67 | c += ivec3(dithering_pattern(uv)); 68 | } 69 | 70 | // Truncate from 8 bits to color_depth bits. 71 | c >>= (8 - color_depth); 72 | 73 | // Convert back to 0 to 1. 74 | COLOR.rgb = vec3(c) / float(1 << color_depth); 75 | } 76 | -------------------------------------------------------------------------------- /shaders/psx_fade.gdshader: -------------------------------------------------------------------------------- 1 | //*** *** *** *** *** *** *** *** *** *** *** ***// 2 | //*** === PSX fading effect === ***// 3 | //*** === For Godot 4.0 === ***// 4 | //*** === By AestheticalZ === ***// 5 | //*** === https://github.com/AestheticalZ === ***// 6 | //*** *** *** *** *** *** *** *** *** *** *** ***// 7 | //*** === Licensed under the: === ***// 8 | //*** === MIT License === ***// 9 | //*** *** *** *** *** *** *** *** *** *** *** ***// 10 | 11 | /***************************************************************************** 12 | MIT License 13 | 14 | Copyright (c) 2022 AestheticalZ 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy 17 | of this software and associated documentation files (the "Software"), to deal 18 | in the Software without restriction, including without limitation the rights 19 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | copies of the Software, and to permit persons to whom the Software is 21 | furnished to do so, subject to the following conditions: 22 | 23 | The above copyright notice and this permission notice shall be included in all 24 | copies or substantial portions of the Software. 25 | 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 | SOFTWARE. 33 | *****************************************************************************/ 34 | 35 | shader_type canvas_item; 36 | 37 | // 0 = No fade. 38 | // 255 = Totally dark. 39 | uniform int alpha: hint_range(0, 255); 40 | 41 | void fragment() { 42 | vec3 color = texelFetch(TEXTURE, ivec2(FRAGCOORD.xy), 0).rgb; 43 | 44 | // Convert 0 to 255 to 0 to 1. 45 | float decimal_alpha = float(alpha) / 255.0; 46 | 47 | // Quantize to make it a bit more "jumpy". 48 | decimal_alpha = floor(decimal_alpha * 32.0) / 32.0; 49 | 50 | // Equal to GL_FUNC_REVERSE_SUBTRACT. 51 | COLOR.rgb = color - vec3(decimal_alpha); 52 | } 53 | -------------------------------------------------------------------------------- /sounds/blip2shortest.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/sounds/blip2shortest.wav -------------------------------------------------------------------------------- /sounds/blip2shortest.wav.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="wav" 4 | type="AudioStreamWAV" 5 | uid="uid://c8q35gl145u1" 6 | path="res://.godot/imported/blip2shortest.wav-099f60b7d37b8efd54af6eefc5774fe6.sample" 7 | 8 | [deps] 9 | 10 | source_file="res://sounds/blip2shortest.wav" 11 | dest_files=["res://.godot/imported/blip2shortest.wav-099f60b7d37b8efd54af6eefc5774fe6.sample"] 12 | 13 | [params] 14 | 15 | force/8_bit=false 16 | force/mono=false 17 | force/max_rate=false 18 | force/max_rate_hz=44100 19 | edit/trim=false 20 | edit/normalize=false 21 | edit/loop_mode=0 22 | edit/loop_begin=0 23 | edit/loop_end=-1 24 | compress/mode=0 25 | -------------------------------------------------------------------------------- /sounds/carnelia.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/sounds/carnelia.ogg -------------------------------------------------------------------------------- /sounds/carnelia.ogg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="oggvorbisstr" 4 | type="AudioStreamOggVorbis" 5 | uid="uid://dbasssnr0iduc" 6 | path="res://.godot/imported/carnelia.ogg-5ba3241ba2072372e30327c02ee9cb36.oggvorbisstr" 7 | 8 | [deps] 9 | 10 | source_file="res://sounds/carnelia.ogg" 11 | dest_files=["res://.godot/imported/carnelia.ogg-5ba3241ba2072372e30327c02ee9cb36.oggvorbisstr"] 12 | 13 | [params] 14 | 15 | loop=true 16 | loop_offset=0.0 17 | bpm=0.0 18 | beat_count=0 19 | bar_beats=4 20 | -------------------------------------------------------------------------------- /sounds/fire_bit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/sounds/fire_bit.wav -------------------------------------------------------------------------------- /sounds/fire_bit.wav.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="wav" 4 | type="AudioStreamWAV" 5 | uid="uid://crrfycxq1rn0x" 6 | path="res://.godot/imported/fire_bit.wav-f618cf8f5a507fdc2adae84689b1e732.sample" 7 | 8 | [deps] 9 | 10 | source_file="res://sounds/fire_bit.wav" 11 | dest_files=["res://.godot/imported/fire_bit.wav-f618cf8f5a507fdc2adae84689b1e732.sample"] 12 | 13 | [params] 14 | 15 | force/8_bit=false 16 | force/mono=false 17 | force/max_rate=false 18 | force/max_rate_hz=44100 19 | edit/trim=false 20 | edit/normalize=false 21 | edit/loop_mode=0 22 | edit/loop_begin=0 23 | edit/loop_end=-1 24 | compress/mode=0 25 | -------------------------------------------------------------------------------- /sounds/foghorn_four_times.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/sounds/foghorn_four_times.ogg -------------------------------------------------------------------------------- /sounds/foghorn_four_times.ogg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="oggvorbisstr" 4 | type="AudioStreamOggVorbis" 5 | uid="uid://cly1knfd2yx68" 6 | path="res://.godot/imported/foghorn_four_times.ogg-ba112e30b2e2cf3bcf5299ffc9f9da97.oggvorbisstr" 7 | 8 | [deps] 9 | 10 | source_file="res://sounds/foghorn_four_times.ogg" 11 | dest_files=["res://.godot/imported/foghorn_four_times.ogg-ba112e30b2e2cf3bcf5299ffc9f9da97.oggvorbisstr"] 12 | 13 | [params] 14 | 15 | loop=true 16 | loop_offset=0.0 17 | bpm=0.0 18 | beat_count=0 19 | bar_beats=4 20 | -------------------------------------------------------------------------------- /sounds/its_sax.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/sounds/its_sax.ogg -------------------------------------------------------------------------------- /sounds/its_sax.ogg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="oggvorbisstr" 4 | type="AudioStreamOggVorbis" 5 | uid="uid://cou58urt1lmfo" 6 | path="res://.godot/imported/its_sax.ogg-ba3a8712cc61b73f8fc0c15f910340f9.oggvorbisstr" 7 | 8 | [deps] 9 | 10 | source_file="res://sounds/its_sax.ogg" 11 | dest_files=["res://.godot/imported/its_sax.ogg-ba3a8712cc61b73f8fc0c15f910340f9.oggvorbisstr"] 12 | 13 | [params] 14 | 15 | loop=true 16 | loop_offset=0.0 17 | bpm=0.0 18 | beat_count=0 19 | bar_beats=4 20 | -------------------------------------------------------------------------------- /sounds/its_sax8bit.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/sounds/its_sax8bit.ogg -------------------------------------------------------------------------------- /sounds/its_sax8bit.ogg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="oggvorbisstr" 4 | type="AudioStreamOggVorbis" 5 | uid="uid://cr1xrgn7v3fbq" 6 | path="res://.godot/imported/its_sax8bit.ogg-9947b004e4bd6ae71635fd26ee9dc51b.oggvorbisstr" 7 | 8 | [deps] 9 | 10 | source_file="res://sounds/its_sax8bit.ogg" 11 | dest_files=["res://.godot/imported/its_sax8bit.ogg-9947b004e4bd6ae71635fd26ee9dc51b.oggvorbisstr"] 12 | 13 | [params] 14 | 15 | loop=true 16 | loop_offset=0.0 17 | bpm=0.0 18 | beat_count=0 19 | bar_beats=4 20 | -------------------------------------------------------------------------------- /sounds/lost_ambienceLOOPING.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/sounds/lost_ambienceLOOPING.ogg -------------------------------------------------------------------------------- /sounds/lost_ambienceLOOPING.ogg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="oggvorbisstr" 4 | type="AudioStreamOggVorbis" 5 | uid="uid://by6c4xt0nee21" 6 | path="res://.godot/imported/lost_ambienceLOOPING.ogg-3c27d71c729798d4d8b14a78362c8cbf.oggvorbisstr" 7 | 8 | [deps] 9 | 10 | source_file="res://sounds/lost_ambienceLOOPING.ogg" 11 | dest_files=["res://.godot/imported/lost_ambienceLOOPING.ogg-3c27d71c729798d4d8b14a78362c8cbf.oggvorbisstr"] 12 | 13 | [params] 14 | 15 | loop=true 16 | loop_offset=0.0 17 | bpm=0.0 18 | beat_count=0 19 | bar_beats=4 20 | -------------------------------------------------------------------------------- /sounds/njb_intro.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/sounds/njb_intro.ogg -------------------------------------------------------------------------------- /sounds/njb_intro.ogg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="oggvorbisstr" 4 | type="AudioStreamOggVorbis" 5 | uid="uid://c75hl5eci0dnw" 6 | path="res://.godot/imported/njb_intro.ogg-871b6ce2551f2b3da9c5168f89419a72.oggvorbisstr" 7 | 8 | [deps] 9 | 10 | source_file="res://sounds/njb_intro.ogg" 11 | dest_files=["res://.godot/imported/njb_intro.ogg-871b6ce2551f2b3da9c5168f89419a72.oggvorbisstr"] 12 | 13 | [params] 14 | 15 | loop=false 16 | loop_offset=0 17 | bpm=0 18 | beat_count=0 19 | bar_beats=4 20 | -------------------------------------------------------------------------------- /sounds/shifting_loss.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/sounds/shifting_loss.ogg -------------------------------------------------------------------------------- /sounds/shifting_loss.ogg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="oggvorbisstr" 4 | type="AudioStreamOggVorbis" 5 | uid="uid://ywdnexon5f3e" 6 | path="res://.godot/imported/shifting_loss.ogg-fd7302cdfcbd031194abbba0e826cc85.oggvorbisstr" 7 | 8 | [deps] 9 | 10 | source_file="res://sounds/shifting_loss.ogg" 11 | dest_files=["res://.godot/imported/shifting_loss.ogg-fd7302cdfcbd031194abbba0e826cc85.oggvorbisstr"] 12 | 13 | [params] 14 | 15 | loop=true 16 | loop_offset=0.0 17 | bpm=0.0 18 | beat_count=0 19 | bar_beats=4 20 | -------------------------------------------------------------------------------- /tools/damage_source.gd: -------------------------------------------------------------------------------- 1 | class_name DamageSource 2 | extends Resource 3 | 4 | 5 | @export_range(0, 1000) var damage: int = 1 6 | @export var damage_types: Array[Global.DAMAGE_TYPES] 7 | 8 | 9 | func add_damage_types(new_damage_types: Array[Global.DAMAGE_TYPES]) -> void: 10 | for type in new_damage_types: 11 | if not damage_types.has(type): 12 | damage_types.append(type) 13 | 14 | 15 | func remove_damage_types(removed_damage_types: Array[Global.DAMAGE_TYPES]) -> void: 16 | for type in removed_damage_types: 17 | if damage_types.has(type): 18 | damage_types.erase(type) 19 | -------------------------------------------------------------------------------- /tools/entity_components/2d/damage_number_component_2d.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | 4 | @export var _damage_number_display_scene: PackedScene 5 | @export var _health_component: HealthComponent 6 | 7 | 8 | func _ready() -> void: 9 | if not _health_component or not _damage_number_display_scene: return 10 | 11 | _health_component.damage_taken.connect(_on_damage_taken) 12 | 13 | 14 | func _on_damage_taken(damage_amount: int) -> void: 15 | var foreground_container: Node2D = get_tree().get_first_node_in_group("foreground_container") 16 | if not foreground_container: return 17 | 18 | var damage_number_instance: DamageNumberDisplay2D = _damage_number_display_scene.instantiate() 19 | foreground_container.add_child(damage_number_instance) 20 | damage_number_instance.global_position = global_position 21 | damage_number_instance.damage_amount = damage_amount 22 | -------------------------------------------------------------------------------- /tools/entity_components/2d/damage_number_component_2d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://de8tj27whq7ri"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/2d/damage_number_component_2d.gd" id="1_4vs8a"] 4 | [ext_resource type="PackedScene" uid="uid://cic8rkr6d658s" path="res://tools/entity_components/2d/damage_number_display_2d.tscn" id="2_lt300"] 5 | 6 | [node name="DamageNumberComponent2D" type="Node2D"] 7 | script = ExtResource("1_4vs8a") 8 | _damage_number_display_scene = ExtResource("2_lt300") 9 | -------------------------------------------------------------------------------- /tools/entity_components/2d/damage_number_display_2d.gd: -------------------------------------------------------------------------------- 1 | class_name DamageNumberDisplay2D 2 | extends Control 3 | 4 | 5 | var damage_amount: int = 1 : set = set_damage_amount 6 | 7 | @onready var _label: Label = %NumberLabel 8 | 9 | 10 | func set_damage_amount(new_amount: int) -> void: 11 | damage_amount = new_amount 12 | _label.text = str(damage_amount) 13 | -------------------------------------------------------------------------------- /tools/entity_components/2d/death_component_2d.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | 4 | @export var _sprite_component: SpriteComponent 5 | 6 | @onready var _particles: GPUParticles2D = $GPUParticles2D 7 | @onready var _animation_player: AnimationPlayer = $AnimationPlayer 8 | 9 | 10 | func _ready() -> void: 11 | _sprite_component.animation_finished.connect(_on_animation_finished) 12 | var particles_texture: AtlasTexture = _particles.texture 13 | particles_texture.atlas = _sprite_component.body_texture 14 | 15 | 16 | func _perform_death_effect() -> void: 17 | if not owner or not owner is Node2D: 18 | return 19 | 20 | var parent_node: Node2D = owner 21 | var spawn_position: Vector2 = parent_node.global_position 22 | var entity_container: Node2D = get_tree().get_first_node_in_group("entity_container") 23 | get_parent().remove_child(self) 24 | entity_container.add_child(self) 25 | global_position = spawn_position 26 | _animation_player.play("die") 27 | 28 | 29 | func _on_animation_finished(animation_name: String) -> void: 30 | if not animation_name == "die": return 31 | 32 | _perform_death_effect() 33 | -------------------------------------------------------------------------------- /tools/entity_components/2d/death_component_2d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=10 format=3 uid="uid://boumuklbs2pdg"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://dv8cv6enwfbso" path="res://actors/top_down_2d/dark bastion/imp/sprite_sheet_imp_0_16x16.png" id="1_44x46"] 4 | [ext_resource type="Script" path="res://tools/entity_components/2d/death_component_2d.gd" id="1_iq7nu"] 5 | 6 | [sub_resource type="Curve" id="Curve_r4njh"] 7 | max_value = 1.5 8 | _data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.1, 1), 0.0, 0.0, 0, 0, Vector2(0.25, 1.1), 0.0, 0.0, 0, 0, Vector2(0.75, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] 9 | point_count = 5 10 | 11 | [sub_resource type="CurveTexture" id="CurveTexture_s6fch"] 12 | curve = SubResource("Curve_r4njh") 13 | 14 | [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_je5yf"] 15 | particle_flag_disable_z = true 16 | direction = Vector3(0, -1, 0) 17 | spread = 30.0 18 | gravity = Vector3(0, 400, 0) 19 | initial_velocity_min = 100.0 20 | initial_velocity_max = 175.0 21 | angular_velocity_min = -720.0 22 | angular_velocity_max = 720.0 23 | orbit_velocity_min = 0.0 24 | orbit_velocity_max = 0.0 25 | scale_curve = SubResource("CurveTexture_s6fch") 26 | 27 | [sub_resource type="AtlasTexture" id="AtlasTexture_6qaw7"] 28 | atlas = ExtResource("1_44x46") 29 | region = Rect2(48, 64, 16, 16) 30 | 31 | [sub_resource type="Animation" id="Animation_npfx3"] 32 | length = 0.001 33 | tracks/0/type = "value" 34 | tracks/0/imported = false 35 | tracks/0/enabled = true 36 | tracks/0/path = NodePath("GPUParticles2D:emitting") 37 | tracks/0/interp = 1 38 | tracks/0/loop_wrap = true 39 | tracks/0/keys = { 40 | "times": PackedFloat32Array(0), 41 | "transitions": PackedFloat32Array(1), 42 | "update": 1, 43 | "values": [false] 44 | } 45 | 46 | [sub_resource type="Animation" id="Animation_yx0kg"] 47 | resource_name = "die" 48 | tracks/0/type = "value" 49 | tracks/0/imported = false 50 | tracks/0/enabled = true 51 | tracks/0/path = NodePath("GPUParticles2D:emitting") 52 | tracks/0/interp = 1 53 | tracks/0/loop_wrap = true 54 | tracks/0/keys = { 55 | "times": PackedFloat32Array(0), 56 | "transitions": PackedFloat32Array(1), 57 | "update": 1, 58 | "values": [true] 59 | } 60 | tracks/1/type = "method" 61 | tracks/1/imported = false 62 | tracks/1/enabled = true 63 | tracks/1/path = NodePath(".") 64 | tracks/1/interp = 1 65 | tracks/1/loop_wrap = true 66 | tracks/1/keys = { 67 | "times": PackedFloat32Array(1), 68 | "transitions": PackedFloat32Array(1), 69 | "values": [{ 70 | "args": [], 71 | "method": &"queue_free" 72 | }] 73 | } 74 | 75 | [sub_resource type="AnimationLibrary" id="AnimationLibrary_spij3"] 76 | _data = { 77 | "RESET": SubResource("Animation_npfx3"), 78 | "die": SubResource("Animation_yx0kg") 79 | } 80 | 81 | [node name="DeathComponent2D" type="Node2D"] 82 | texture_filter = 1 83 | script = ExtResource("1_iq7nu") 84 | 85 | [node name="GPUParticles2D" type="GPUParticles2D" parent="."] 86 | emitting = false 87 | amount = 1 88 | process_material = SubResource("ParticleProcessMaterial_je5yf") 89 | texture = SubResource("AtlasTexture_6qaw7") 90 | lifetime = 0.65 91 | one_shot = true 92 | 93 | [node name="AnimationPlayer" type="AnimationPlayer" parent="."] 94 | libraries = { 95 | "": SubResource("AnimationLibrary_spij3") 96 | } 97 | -------------------------------------------------------------------------------- /tools/entity_components/2d/hitbox_component_2d.gd: -------------------------------------------------------------------------------- 1 | class_name HitboxComponent2D 2 | extends Area2D 3 | 4 | 5 | signal hit_limit_reached() 6 | 7 | enum HURTBOX_TYPES { 8 | FRIENDLY, 9 | ENEMY, 10 | BOTH, 11 | NEITHER, 12 | } 13 | 14 | @export var can_damage: HURTBOX_TYPES = HURTBOX_TYPES.NEITHER : set = set_can_damage 15 | @export var max_hits: int = 1 16 | @export var damage_source: DamageSource 17 | @export var _debug: bool = false 18 | 19 | var hits_given: int = 0 : set = set_hits_given 20 | var enabled: bool = true : set = set_enabled 21 | 22 | var _infinite_hits: bool = false 23 | 24 | 25 | func _ready() -> void: 26 | if max_hits == -1: 27 | _infinite_hits = true 28 | 29 | _initialize_collision() 30 | 31 | 32 | func give_hit() -> void: 33 | if _debug: breakpoint 34 | 35 | if not _infinite_hits: 36 | set_hits_given(hits_given + 1) 37 | 38 | 39 | func set_enabled(new_enabled: bool) -> void: 40 | enabled = new_enabled 41 | 42 | set_deferred("monitorable",enabled) 43 | monitoring = false 44 | 45 | if not enabled: 46 | collision_layer = 0 47 | 48 | 49 | func set_can_damage(new_can_damage: HURTBOX_TYPES) -> void: 50 | can_damage = new_can_damage 51 | _initialize_collision() 52 | 53 | 54 | func set_hits_given(new_hits_given: int) -> void: 55 | hits_given = new_hits_given 56 | 57 | if hits_given >= max_hits: 58 | set_enabled(false) 59 | hit_limit_reached.emit() 60 | 61 | 62 | func _initialize_collision() -> void: 63 | collision_mask = 0 64 | collision_layer = 0 65 | 66 | match can_damage: 67 | HURTBOX_TYPES.FRIENDLY: 68 | collision_layer = 16 69 | HURTBOX_TYPES.ENEMY: 70 | collision_layer = 8 71 | HURTBOX_TYPES.BOTH: 72 | collision_layer = 24 73 | -------------------------------------------------------------------------------- /tools/entity_components/2d/hitbox_component_2d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://jmuqqfhupx52"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/2d/hitbox_component_2d.gd" id="1_xubbk"] 4 | 5 | [node name="HitboxComponent2D" type="Area2D"] 6 | script = ExtResource("1_xubbk") 7 | -------------------------------------------------------------------------------- /tools/entity_components/2d/hurtbox_component_2d.gd: -------------------------------------------------------------------------------- 1 | class_name HurtboxComponent2D 2 | extends Area2D 3 | 4 | 5 | signal hit_taken 6 | 7 | enum HITBOX_SOURCES { 8 | FRIENDLY, 9 | ENEMY, 10 | BOTH, 11 | } 12 | 13 | @export var _health_component: HealthComponent 14 | @export var can_take_hits_from: HITBOX_SOURCES = HITBOX_SOURCES.FRIENDLY 15 | @export var _debug: bool = false 16 | 17 | var enabled: bool = true : set = set_enabled 18 | var _overlapping_hitbox_components: Array[HitboxComponent2D] 19 | 20 | 21 | func _ready() -> void: 22 | _initialize_collision() 23 | area_entered.connect(_on_area_entered) 24 | area_exited.connect(_on_area_exited) 25 | 26 | 27 | func take_hit(damage_source: DamageSource) -> void: 28 | hit_taken.emit() 29 | 30 | if not damage_source: return 31 | if not _health_component: return 32 | 33 | _health_component.take_damage(damage_source) 34 | 35 | 36 | func check_for_damage() -> void: 37 | if _debug: breakpoint 38 | 39 | var damage_done := false 40 | 41 | for hitbox in _overlapping_hitbox_components: 42 | hitbox.give_hit() 43 | take_hit(hitbox.damage_source) 44 | 45 | if damage_done: break 46 | 47 | 48 | func set_enabled(new_enabled: bool) -> void: 49 | enabled = new_enabled 50 | 51 | monitoring = enabled 52 | monitorable = false 53 | 54 | 55 | func _initialize_collision() -> void: 56 | collision_layer = 0 57 | 58 | match can_take_hits_from: 59 | HITBOX_SOURCES.FRIENDLY: 60 | collision_mask = 8 61 | HITBOX_SOURCES.ENEMY: 62 | collision_mask = 16 63 | HITBOX_SOURCES.BOTH: 64 | collision_mask = 24 65 | 66 | 67 | func _on_area_entered(area: Area2D) -> void: 68 | if _debug: breakpoint 69 | 70 | if not area is HitboxComponent2D: return 71 | 72 | var hitbox: HitboxComponent2D = area 73 | 74 | _overlapping_hitbox_components.append(hitbox) 75 | hitbox.give_hit() 76 | take_hit(hitbox.damage_source) 77 | 78 | 79 | func _on_area_exited(area: Area2D) -> void: 80 | if _debug: breakpoint 81 | 82 | if not area is HitboxComponent2D: return 83 | if not _overlapping_hitbox_components.has(area): return 84 | 85 | var hitbox: HitboxComponent2D = area 86 | _overlapping_hitbox_components.erase(hitbox) 87 | -------------------------------------------------------------------------------- /tools/entity_components/2d/hurtbox_component_2d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://43el6e2p3y7n"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/2d/hurtbox_component_2d.gd" id="1_e6xwk"] 4 | 5 | [node name="HurtboxComponent2D" type="Area2D"] 6 | script = ExtResource("1_e6xwk") 7 | -------------------------------------------------------------------------------- /tools/entity_components/2d/movement_component_2D.gd: -------------------------------------------------------------------------------- 1 | class_name MovementComponent2D 2 | extends Node 3 | 4 | enum MOVEMENT_TYPES { 5 | SIDE_SCROLLING, 6 | TOP_DOWN, 7 | } 8 | 9 | signal is_moving_changed(new_is_moving: bool) 10 | 11 | @export var _body: CharacterBody2D 12 | @export var _sprite_component: SpriteComponent 13 | @export_range(0.5, 100.0, 0.5) var max_speed: float = 100.0 14 | @export_range(0.1, 20.0, 0.1) var acceleration: float = 10.0 15 | @export_range(0.5, 20.0, 0.5) var friction: float = 15.0 16 | @export var ground_gravity: float = -0.1 17 | @export var air_gravity := -9.8 18 | @export var movement_type: MOVEMENT_TYPES = MOVEMENT_TYPES.SIDE_SCROLLING 19 | 20 | var velocity: Vector2 = Vector2.ZERO 21 | var speed_multiplier: float = 1.0 22 | 23 | var _stopped: bool = false 24 | var _is_moving: bool = false : set = _set_is_moving 25 | 26 | @onready var _stopped_timer: Timer = $StoppedTimer 27 | 28 | 29 | func _ready() -> void: 30 | assert(_body, "MovementComponent2D has no physics body.") 31 | 32 | 33 | func _physics_process(delta: float) -> void: 34 | if movement_type == MOVEMENT_TYPES.SIDE_SCROLLING: 35 | _apply_gravity(delta) 36 | 37 | _is_moving = velocity.length_squared() > 0.5 38 | 39 | 40 | func move(direction: Vector2, delta: float): 41 | var max_velocity: Vector2 = max_speed * direction 42 | velocity = _body.velocity.lerp(max_speed * direction, acceleration * delta) 43 | _body.velocity = velocity 44 | return _apply_velocity() 45 | 46 | 47 | func move_to(target_location: Vector2, delta: float): 48 | if not _body: 49 | return 50 | 51 | if _stopped: 52 | velocity = Vector2.ZERO 53 | else: 54 | velocity = _body.velocity 55 | 56 | var direction = _body.global_position.direction_to(target_location) 57 | var weight: float 58 | 59 | if _body.global_position.distance_squared_to(target_location) < 0.01: 60 | _sprite_component.moving = false 61 | weight = friction 62 | else: 63 | _sprite_component.moving = true 64 | weight = acceleration 65 | 66 | _body.velocity = lerp(_body.velocity, max_speed * direction * speed_multiplier, weight * delta) 67 | 68 | if _sprite_component: 69 | _sprite_component.face(velocity.normalized()) 70 | 71 | return _apply_velocity() 72 | 73 | 74 | func stop(length_seconds: float = 1.0) -> void: 75 | if not _body: 76 | return 77 | 78 | if _stopped: 79 | return 80 | 81 | _stopped = true 82 | _stopped_timer.start(length_seconds) 83 | 84 | await(_stopped_timer.timeout) 85 | 86 | _stopped = false 87 | 88 | 89 | func _apply_velocity(): 90 | _body.move_and_slide() 91 | # if movement_type == MOVEMENT_TYPES.SIDE_SCROLLING: 92 | # _body.move_and_slide() 93 | # elif movement_type == MOVEMENT_TYPES.COLLIDE: 94 | # return _body.move_and_collide(_body.velocity) 95 | 96 | 97 | func _apply_gravity(delta: float) -> void: 98 | var current_gravity = ground_gravity if _body.is_on_floor() else air_gravity 99 | _body.velocity.y += delta * current_gravity 100 | 101 | 102 | func _set_is_moving(new_is_moving) -> void: 103 | if _is_moving == new_is_moving: 104 | return 105 | 106 | _is_moving = new_is_moving 107 | is_moving_changed.emit(_is_moving) 108 | -------------------------------------------------------------------------------- /tools/entity_components/2d/movement_component_2D.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://dlwo41i4h04d6"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/2d/movement_component_2D.gd" id="1_xgahj"] 4 | 5 | [node name="MovementComponent2D" type="Node"] 6 | script = ExtResource("1_xgahj") 7 | 8 | [node name="StoppedTimer" type="Timer" parent="."] 9 | one_shot = true 10 | -------------------------------------------------------------------------------- /tools/entity_components/2d/navigation_component_2d.gd: -------------------------------------------------------------------------------- 1 | class_name NavigationComponent2D 2 | extends Node2D 3 | 4 | 5 | signal target_node_lost() 6 | signal target_location_reached() 7 | 8 | @export var _movement_component: MovementComponent2D 9 | 10 | var disabled: bool = false 11 | var target_node: Node2D : set = set_target_node 12 | var target_location: Vector2 : set = set_target_location 13 | 14 | var _next_location: Vector2 15 | 16 | @onready var _navigation_agent: NavigationAgent2D = $NavigationAgent2D 17 | 18 | 19 | func _ready() -> void: 20 | assert(_movement_component, "Navigation component has no movement component set.") 21 | 22 | 23 | func _physics_process(delta: float) -> void: 24 | if disabled: 25 | return 26 | 27 | if target_location: 28 | _next_location = get_path_to_target_location() 29 | _movement_component.move_to(_next_location, delta) 30 | 31 | if target_node: 32 | _update_target_node_location() 33 | 34 | if _navigation_agent.is_target_reached(): 35 | target_location_reached.emit() 36 | 37 | 38 | func set_target_location(new_location: Vector2) -> void: 39 | target_location = new_location 40 | 41 | _navigation_agent.target_position = target_location 42 | 43 | 44 | func set_target_node(new_target_node: Node2D) -> void: 45 | target_node = new_target_node 46 | 47 | if target_node: 48 | _update_target_node_location() 49 | 50 | 51 | func get_path_to_target_location() -> Vector2: 52 | _navigation_agent.set_velocity(_movement_component.velocity) 53 | return _navigation_agent.get_next_path_position() 54 | 55 | 56 | func disable() -> void: 57 | disabled = true 58 | 59 | 60 | func enable() -> void: 61 | disabled = false 62 | 63 | 64 | func _update_target_node_location() -> void: 65 | set_target_location(target_node.global_position) 66 | -------------------------------------------------------------------------------- /tools/entity_components/2d/navigation_component_2d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://baq3kixetclfi"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/2d/navigation_component_2d.gd" id="1_jr0g0"] 4 | 5 | [node name="NavigationComponent2D" type="Node2D"] 6 | script = ExtResource("1_jr0g0") 7 | 8 | [node name="NavigationAgent2D" type="NavigationAgent2D" parent="."] 9 | avoidance_enabled = true 10 | -------------------------------------------------------------------------------- /tools/entity_components/2d/player_detector_2d.gd: -------------------------------------------------------------------------------- 1 | class_name PlayerDetector2D 2 | extends Area2D 3 | 4 | 5 | signal player_entered() 6 | signal player_exited() 7 | 8 | @export var one_shot: bool = false 9 | @export var start_enabled: bool = true 10 | 11 | var enabled: bool : set = set_enabled 12 | 13 | 14 | func _ready() -> void: 15 | body_entered.connect(_on_body_entered) 16 | body_exited.connect(_on_body_exited) 17 | 18 | set_enabled(start_enabled) 19 | collision_mask = 1 20 | 21 | 22 | func set_enabled(new_enabled: bool) -> void: 23 | enabled = new_enabled 24 | 25 | set_deferred("monitoring", enabled) 26 | 27 | 28 | func _player_entered() -> void: 29 | emit_signal("player_entered") 30 | 31 | 32 | func _player_exited() -> void: 33 | emit_signal("player_exited") 34 | if one_shot: 35 | set_deferred("monitoring", false) 36 | queue_free() 37 | 38 | 39 | func _on_body_entered(body: Node) -> void: 40 | if body.is_in_group("player"): 41 | _player_entered() 42 | 43 | 44 | func _on_body_exited(body: Node) -> void: 45 | if body.is_in_group("player"): 46 | _player_exited() 47 | -------------------------------------------------------------------------------- /tools/entity_components/2d/sprite_component.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name SpriteComponent 3 | extends Node2D 4 | 5 | 6 | enum ANIMATIONS {IDLE, WALK, ATTACK, TAKE_HIT, DIE, SKITTER} 7 | 8 | signal flashing_finished() 9 | signal facing_changed() 10 | signal animation_finished(animation_name: String) 11 | 12 | const FLASH_LENGTH: float = 0.05 13 | const FLASH_COLOR: Color = Color(10, 10, 10) 14 | 15 | @export var body_texture: Texture : set = set_body_texture, get = get_body_texture 16 | @export var _move_animation: ANIMATIONS = ANIMATIONS.WALK 17 | 18 | var moving: bool = false: set = set_moving 19 | 20 | var _animation_tweens: Array 21 | 22 | @onready var _body_sprite: Sprite2D = $BodySprite 23 | @onready var _flash_timer: Timer = $FlashTimer 24 | @onready var _animation_player: AnimationPlayer = $AnimationPlayer 25 | 26 | 27 | func _ready(): 28 | if body_texture: 29 | _body_sprite.texture = body_texture 30 | 31 | _animation_player.animation_finished.connect(_on_animation_finished) 32 | 33 | 34 | func flash(flash_time: float) -> void: 35 | while flash_time > FLASH_LENGTH * 2: 36 | _individual_flash() 37 | _flash_timer.start(FLASH_LENGTH) 38 | flash_time -= FLASH_LENGTH 39 | await(_flash_timer.timeout) 40 | 41 | _flash_timer.start(FLASH_LENGTH * 2) 42 | _body_sprite.modulate = FLASH_COLOR 43 | await(_flash_timer.timeout) 44 | _body_sprite.modulate = Color.WHITE 45 | flashing_finished.emit() 46 | 47 | 48 | func face(direction: Vector2) -> void: 49 | var move_sign: float = signf(direction.x) 50 | if move_sign == 0: 51 | return 52 | scale.x = move_sign 53 | 54 | 55 | func set_moving(new_moving: bool) -> void: 56 | if moving == new_moving: 57 | return 58 | 59 | moving = new_moving 60 | 61 | if moving: 62 | play_animation(_move_animation) 63 | return 64 | 65 | play_animation(ANIMATIONS.IDLE) 66 | 67 | 68 | func play_animation(animation: ANIMATIONS) -> void: 69 | var animation_name: String = ANIMATIONS.keys()[animation] 70 | animation_name = animation_name.to_lower() 71 | _animation_player.play(animation_name) 72 | 73 | 74 | func _individual_flash() -> void: 75 | if _body_sprite.modulate == Color.WHITE: 76 | _body_sprite.modulate = FLASH_COLOR 77 | return 78 | 79 | _body_sprite.modulate = Color.WHITE 80 | 81 | 82 | func set_body_texture(new_texture: Texture) -> void: 83 | body_texture = new_texture 84 | 85 | if _body_sprite: 86 | _body_sprite.texture = body_texture 87 | 88 | 89 | func get_body_texture() -> Texture2D: 90 | return body_texture.duplicate() 91 | 92 | 93 | func _on_animation_finished(animation_name: String) -> void: 94 | animation_finished.emit(animation_name) 95 | -------------------------------------------------------------------------------- /tools/entity_components/3d/hitbox_component_3d.gd: -------------------------------------------------------------------------------- 1 | class_name HitboxComponent3D 2 | extends Area3D 3 | 4 | 5 | signal hit_limit_reached() 6 | 7 | enum HURTBOX_TYPES { 8 | FRIENDLY, 9 | ENEMY, 10 | BOTH 11 | } 12 | 13 | @export var can_damage: HURTBOX_TYPES = HURTBOX_TYPES.FRIENDLY 14 | @export var max_hits: int = 1 15 | @export var damage_source: DamageSource 16 | 17 | var hits_given: int = 0 : set = set_hits_given 18 | var enabled: bool = true : set = set_enabled 19 | 20 | var _infinite_hits: bool = false 21 | 22 | 23 | func _ready() -> void: 24 | if max_hits == -1: 25 | _infinite_hits = true 26 | 27 | _initialize_collision() 28 | 29 | 30 | func give_hit() -> void: 31 | if not _infinite_hits: 32 | set_hits_given(hits_given + 1) 33 | 34 | 35 | func set_enabled(new_enabled: bool) -> void: 36 | enabled = new_enabled 37 | 38 | monitorable = enabled 39 | monitoring = false 40 | 41 | if not enabled: 42 | collision_layer = 0 43 | 44 | 45 | func set_hits_given(new_hits_given: int) -> void: 46 | hits_given = new_hits_given 47 | 48 | if hits_given >= max_hits: 49 | set_enabled(false) 50 | hit_limit_reached.emit() 51 | 52 | 53 | func _initialize_collision() -> void: 54 | collision_mask = 0 55 | 56 | match can_damage: 57 | HURTBOX_TYPES.FRIENDLY: 58 | collision_layer = 16 59 | HURTBOX_TYPES.ENEMY: 60 | collision_layer = 8 61 | HURTBOX_TYPES.BOTH: 62 | collision_layer = 24 63 | -------------------------------------------------------------------------------- /tools/entity_components/3d/hitbox_component_3d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bc12cqsdtqbc6"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/3d/hitbox_component_3d.gd" id="1_lf82x"] 4 | 5 | [node name="HitboxComponent" type="Area3D"] 6 | collision_layer = 0 7 | collision_mask = 0 8 | monitoring = false 9 | script = ExtResource("1_lf82x") 10 | -------------------------------------------------------------------------------- /tools/entity_components/3d/hurtbox_component_3d.gd: -------------------------------------------------------------------------------- 1 | class_name HurtboxComponent3D 2 | extends Area3D 3 | 4 | 5 | signal hit_taken 6 | 7 | enum HITBOX_SOURCES { 8 | FRIENDLY, 9 | ENEMY, 10 | BOTH, 11 | } 12 | 13 | @export var health_component_path: NodePath 14 | @export var can_take_hits_from: HITBOX_SOURCES = HITBOX_SOURCES.FRIENDLY 15 | 16 | var enabled: bool = true : set = set_enabled 17 | 18 | var _health_component: HealthComponent 19 | 20 | 21 | func _ready() -> void: 22 | if health_component_path: 23 | _health_component = get_node(health_component_path) 24 | 25 | _initialize_collision() 26 | area_entered.connect(_on_area_entered) 27 | 28 | 29 | func take_hit(damage_source: DamageSource) -> void: 30 | _health_component.take_damage(damage_source) 31 | hit_taken.emit() 32 | 33 | 34 | func check_for_damage() -> void: 35 | var possible_damage_areas = get_overlapping_areas() 36 | var damage_done := false 37 | 38 | for area in possible_damage_areas: 39 | if not area is HitboxComponent3D: 40 | continue 41 | var hitbox: HitboxComponent3D = area 42 | hitbox.give_hit() 43 | take_hit(hitbox.damage_source) 44 | 45 | if damage_done: 46 | break 47 | 48 | 49 | func set_enabled(new_enabled: bool) -> void: 50 | enabled = new_enabled 51 | 52 | monitoring = enabled 53 | monitorable = false 54 | 55 | 56 | func _initialize_collision() -> void: 57 | collision_layer = 0 58 | 59 | match can_take_hits_from: 60 | HITBOX_SOURCES.FRIENDLY: 61 | collision_mask = 8 62 | HITBOX_SOURCES.ENEMY: 63 | collision_mask = 16 64 | HITBOX_SOURCES.BOTH: 65 | collision_mask = 24 66 | 67 | 68 | func _on_area_entered(area: Area3D) -> void: 69 | if not area is HitboxComponent3D: 70 | return 71 | 72 | var hitbox: HitboxComponent3D = area 73 | hitbox.give_hit() 74 | take_hit(hitbox.damage_source) 75 | -------------------------------------------------------------------------------- /tools/entity_components/3d/hurtbox_component_3d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/3d/hurtbox_component_3d.gd" id="1_y4kle"] 4 | 5 | [node name="HurtboxComponent3D" type="Area3D"] 6 | collision_layer = 0 7 | collision_mask = 0 8 | monitorable = false 9 | script = ExtResource("1_y4kle") 10 | health_component_path = null 11 | can_take_hits_from = null 12 | -------------------------------------------------------------------------------- /tools/entity_components/3d/mesh_component.gd: -------------------------------------------------------------------------------- 1 | class_name MeshComponent 2 | extends Node3D 3 | 4 | 5 | signal flashing_finished() 6 | 7 | const FLASH_LENGTH: float = 0.05 8 | 9 | @export_range(2.0, 10.0, 0.25) var angular_acceleration: float = 4.0 10 | 11 | @onready var _flash_timer: Timer = $FlashTimer 12 | 13 | 14 | func turn(velocity: Vector3, delta: float) -> void: 15 | if _get_horizontal_speed(velocity) > 0.1: 16 | rotation.y = lerp_angle(rotation.y, atan2(-velocity.x, -velocity.z), delta * angular_acceleration) 17 | 18 | 19 | func flash(flash_time: float, bright_flash: bool = true) -> void: 20 | while flash_time > FLASH_LENGTH * 2: 21 | visible = not visible 22 | _flash_timer.start(FLASH_LENGTH) 23 | flash_time -= FLASH_LENGTH 24 | await(_flash_timer.timeout) 25 | 26 | _flash_timer.start(FLASH_LENGTH * 2) 27 | visible = false 28 | await(_flash_timer.timeout) 29 | visible = true 30 | flashing_finished.emit() 31 | 32 | 33 | func rotate_degrees(rotation_degree_vector: Vector3, delta: float) -> void: 34 | rotation += rotation_degree_vector * delta 35 | 36 | 37 | func _get_horizontal_speed(velocity: Vector3) -> float: 38 | return Vector2(velocity.x, velocity.z).length() 39 | -------------------------------------------------------------------------------- /tools/entity_components/3d/mesh_component.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/3d/mesh_component.gd" id="1_lc1pa"] 4 | 5 | 6 | [node name="MeshComponent" type="Node3D"] 7 | script = ExtResource("1_lc1pa") 8 | 9 | [node name="FlashTimer" type="Timer" parent="."] 10 | wait_time = 0.1 11 | one_shot = true 12 | -------------------------------------------------------------------------------- /tools/entity_components/3d/movement_component_3D.gd: -------------------------------------------------------------------------------- 1 | class_name MovementComponent3D 2 | extends Node 3 | 4 | enum MOVEMENT_TYPES { 5 | SLIDE, 6 | COLLIDE, 7 | } 8 | 9 | @export var _body: CharacterBody3D 10 | @export var _mesh_component: MeshComponent 11 | @export_range(0.5, 50.0, 0.5) var max_speed: float = 2.0 12 | @export_range(0.1, 20.0, 0.1) var acceleration: float = 1.0 13 | @export_range(0.5, 20.0, 0.5) var friction: float = 10.0 14 | @export var ground_gravity: float = -0.1 15 | @export var air_gravity := -9.8 16 | @export var movement_type: MOVEMENT_TYPES = MOVEMENT_TYPES.SLIDE 17 | 18 | var velocity: Vector3 = Vector3.ZERO 19 | var speed_multiplier: float = 1.0 20 | 21 | var _stopped: bool = false 22 | 23 | @onready var _stopped_timer: Timer = $StoppedTimer 24 | 25 | 26 | func _ready() -> void: 27 | assert(_body, "MovementComponent3D has no physics body set.") 28 | 29 | 30 | func _physics_process(delta: float) -> void: 31 | _apply_gravity(delta) 32 | 33 | 34 | func move(direction: Vector3, delta: float): 35 | var max_velocity: Vector3 = max_speed * direction 36 | velocity = _body.velocity.lerp(max_speed * direction * delta, acceleration * delta) 37 | _body.velocity = velocity 38 | return _apply_velocity() 39 | 40 | 41 | func move_to(target_location: Vector3, delta: float): 42 | if not _body: 43 | return 44 | 45 | if _stopped: 46 | velocity = Vector3.ZERO 47 | else: 48 | velocity = _body.velocity 49 | 50 | var direction = _body.global_position.direction_to(target_location) 51 | var weight: float 52 | 53 | if _body.global_position.distance_squared_to(target_location) < 0.01: 54 | weight = friction 55 | else: 56 | weight = acceleration 57 | 58 | _body.velocity = lerp(_body.velocity, max_speed * direction * speed_multiplier, weight * delta) 59 | 60 | if _mesh_component: 61 | _mesh_component.turn(velocity, delta) 62 | 63 | return _apply_velocity() 64 | 65 | 66 | func stop(length_seconds: float = 1.0) -> void: 67 | if not _body: 68 | return 69 | 70 | if _stopped: 71 | return 72 | 73 | _stopped = true 74 | _stopped_timer.start(length_seconds) 75 | 76 | await(_stopped_timer.timeout) 77 | 78 | _stopped = false 79 | 80 | 81 | func _apply_velocity(): 82 | if movement_type == MOVEMENT_TYPES.SLIDE: 83 | _body.move_and_slide() 84 | elif movement_type == MOVEMENT_TYPES.COLLIDE: 85 | return _body.move_and_collide(_body.velocity) 86 | 87 | 88 | func _apply_gravity(delta: float) -> void: 89 | var current_gravity = ground_gravity if _body.is_on_floor() else air_gravity 90 | _body.velocity.y += delta * current_gravity 91 | -------------------------------------------------------------------------------- /tools/entity_components/3d/movement_component_3D.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://drycb11qlgwnx"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/3d/movement_component_3d.gd" id="1_38qtk"] 4 | 5 | [node name="MovementComponent" type="Node"] 6 | script = ExtResource("1_38qtk") 7 | 8 | [node name="StoppedTimer" type="Timer" parent="."] 9 | one_shot = true 10 | -------------------------------------------------------------------------------- /tools/entity_components/3d/movement_component_3d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://drycb11qlgwnx"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/3d/movement_component_3d.gd" id="1_38qtk"] 4 | 5 | [node name="MovementComponent" type="Node"] 6 | script = ExtResource("1_38qtk") 7 | 8 | [node name="StoppedTimer" type="Timer" parent="."] 9 | one_shot = true 10 | -------------------------------------------------------------------------------- /tools/entity_components/3d/navigation_component_3d.gd: -------------------------------------------------------------------------------- 1 | class_name NavigationComponent3D 2 | extends Node3D 3 | 4 | 5 | signal target_node_lost() 6 | signal target_location_reached() 7 | 8 | @export var movement_component_path: NodePath 9 | 10 | var target_node: Node3D : set = set_target_node 11 | var target_location: Vector3 : set = set_target_location 12 | 13 | var _movement_component: MovementComponent3D 14 | var _next_location: Vector3 15 | 16 | @onready var _navigation_agent: NavigationAgent3D = $NavigationAgent3D 17 | 18 | 19 | func _ready() -> void: 20 | assert(movement_component_path, "Navigation component has no movement component set.") 21 | _movement_component = get_node(movement_component_path) 22 | 23 | 24 | func _physics_process(delta: float) -> void: 25 | if target_location: 26 | _next_location = get_path_to_target_location() 27 | _movement_component.move(_next_location, delta) 28 | 29 | if target_node: 30 | _update_target_node_location() 31 | 32 | if _navigation_agent.is_target_reached(): 33 | target_location_reached.emit() 34 | 35 | 36 | func set_target_location(new_location: Vector3) -> void: 37 | target_location = new_location 38 | 39 | _navigation_agent.target_position = target_location 40 | 41 | 42 | func set_target_node(new_target_node: Node3D) -> void: 43 | target_node = new_target_node 44 | 45 | if target_node: 46 | _update_target_node_location() 47 | 48 | 49 | func get_path_to_target_location() -> Vector3: 50 | _navigation_agent.set_velocity(_movement_component.velocity) 51 | return _navigation_agent.get_next_path_position() 52 | 53 | 54 | func _update_target_node_location() -> void: 55 | set_target_location(target_node.global_position) 56 | -------------------------------------------------------------------------------- /tools/entity_components/3d/navigation_component_3d.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://c3bh7irnwesh"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/3d/navigation_component_3d.gd" id="1_lickq"] 4 | 5 | [node name="NavigationComponent3D" type="Node3D"] 6 | script = ExtResource("1_lickq") 7 | 8 | [node name="NavigationAgent3D" type="NavigationAgent3D" parent="."] 9 | -------------------------------------------------------------------------------- /tools/entity_components/3d/player_detector_3d.gd: -------------------------------------------------------------------------------- 1 | class_name PlayerDetector3D 2 | extends Area3D 3 | 4 | 5 | signal player_entered() 6 | signal player_exited() 7 | 8 | @export var one_shot: bool = false 9 | @export var start_enabled: bool = true 10 | 11 | var enabled: bool : set = set_enabled 12 | 13 | 14 | func _ready() -> void: 15 | body_entered.connect(_on_body_entered) 16 | body_exited.connect(_on_body_exited) 17 | 18 | set_enabled(start_enabled) 19 | collision_mask = 1 20 | 21 | 22 | func set_enabled(new_enabled: bool) -> void: 23 | enabled = new_enabled 24 | 25 | set_deferred("monitoring", enabled) 26 | 27 | 28 | func _player_entered() -> void: 29 | emit_signal("player_entered") 30 | 31 | 32 | func _player_exited() -> void: 33 | emit_signal("player_exited") 34 | if one_shot: 35 | set_deferred("monitoring", false) 36 | queue_free() 37 | 38 | 39 | func _on_body_entered(body: Node) -> void: 40 | if body.is_in_group("player"): 41 | _player_entered() 42 | 43 | 44 | func _on_body_exited(body: Node) -> void: 45 | if body.is_in_group("player"): 46 | _player_exited() 47 | -------------------------------------------------------------------------------- /tools/entity_components/experience_drop_component.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | 4 | @export var _xp_pickup: PackedScene 5 | @export var _health_component: HealthComponent 6 | @export_range(0, 1) var _drop_chance_on_death: float = 0.5 7 | 8 | 9 | func _ready(): 10 | _health_component.health_reached_zero.connect(_on_health_reached_zero) 11 | 12 | 13 | func drop_experience() -> void: 14 | if not owner: 15 | return 16 | 17 | var xp_instance = _xp_pickup.instantiate() 18 | var spawn_position = owner.global_position 19 | var entity_container: Node = get_tree().get_first_node_in_group("entity_container") 20 | 21 | if entity_container: 22 | entity_container.add_child(xp_instance) 23 | else: 24 | get_parent().add_child(xp_instance) 25 | 26 | xp_instance.global_position = spawn_position 27 | 28 | 29 | func _on_health_reached_zero() -> void: 30 | if not _xp_pickup: 31 | return 32 | 33 | if randf() > _drop_chance_on_death: 34 | return 35 | 36 | drop_experience() 37 | -------------------------------------------------------------------------------- /tools/entity_components/experience_drop_component.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://djrtixww2e2ul"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/experience_drop_component.gd" id="1_r5fdm"] 4 | [ext_resource type="PackedScene" uid="uid://01gg1ueq130l" path="res://levels/objects/pickup_experience/pickup_experience.tscn" id="2_dmn5c"] 5 | 6 | [node name="ExperienceDropComponent" type="Node"] 7 | script = ExtResource("1_r5fdm") 8 | _xp_pickup = ExtResource("2_dmn5c") 9 | -------------------------------------------------------------------------------- /tools/entity_components/general/damage_source.gd: -------------------------------------------------------------------------------- 1 | class_name DamageSource 2 | extends Resource 3 | 4 | 5 | @export_range(0, 1000) var damage: int = 1 6 | @export var damage_types: Array[Global.DAMAGE_TYPES] 7 | 8 | 9 | func add_damage_types(new_damage_types: Array[Global.DAMAGE_TYPES]) -> void: 10 | for type in new_damage_types: 11 | if not damage_types.has(type): 12 | damage_types.append(type) 13 | 14 | 15 | func remove_damage_types(removed_damage_types: Array[Global.DAMAGE_TYPES]) -> void: 16 | for type in removed_damage_types: 17 | if damage_types.has(type): 18 | damage_types.erase(type) 19 | -------------------------------------------------------------------------------- /tools/entity_components/general/health_component.gd: -------------------------------------------------------------------------------- 1 | class_name HealthComponent 2 | extends Node 3 | 4 | 5 | signal health_changed(health: int, max_health: int) 6 | signal damage_taken(damage_amount: int) 7 | signal heal_received(heal_amount: int) 8 | signal health_reached_zero() 9 | 10 | @export var max_health: int = 3 : set = set_max_health 11 | 12 | var current_health: int : set = set_current_health 13 | var invincible: bool = false 14 | 15 | var _previous_health: int 16 | 17 | 18 | func _ready() -> void: 19 | _initialize_health() 20 | 21 | 22 | func heal(heal_amount: int) -> void: 23 | heal_received.emit(heal_amount) 24 | 25 | set_current_health(current_health + heal_amount) 26 | 27 | 28 | func take_damage(damage_source: DamageSource) -> void: 29 | if invincible: 30 | return 31 | 32 | var damage_amount: int = damage_source.damage 33 | 34 | damage_taken.emit(damage_amount) 35 | set_current_health(current_health - damage_amount) 36 | 37 | 38 | ## returns a float between 0 and 1 representing the ratio of current_health to max_health 39 | func get_health_ratio() -> float: 40 | if current_health <= 0: 41 | return 0 42 | 43 | var health_ratio: float = minf( ( float(current_health) / max_health), 1) 44 | return health_ratio 45 | 46 | 47 | func set_current_health(new_current_health: int) -> void: 48 | current_health = _previous_health 49 | current_health = clamp(new_current_health, 0, max_health) 50 | 51 | health_changed.emit(current_health, max_health) 52 | 53 | _check_death.call_deferred() 54 | 55 | 56 | func set_max_health(new_max_health: int) -> void: 57 | max_health = new_max_health 58 | 59 | if current_health > max_health: 60 | current_health = max_health 61 | 62 | 63 | func update_health() -> void: 64 | set_current_health(current_health) 65 | 66 | 67 | func _check_death() -> void: 68 | if current_health <= 0: 69 | health_reached_zero.emit() 70 | 71 | 72 | func _initialize_health() -> void: 73 | current_health = max_health 74 | -------------------------------------------------------------------------------- /tools/entity_components/general/health_component.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://drphl5von85kh"] 2 | 3 | [ext_resource type="Script" path="res://tools/entity_components/general/health_component.gd" id="1_x2y2y"] 4 | 5 | [node name="HealthComponent" type="Node"] 6 | script = ExtResource("1_x2y2y") 7 | -------------------------------------------------------------------------------- /tools/entity_components/general/screenshake.gd: -------------------------------------------------------------------------------- 1 | class_name Screenshake 2 | extends Node 3 | 4 | var speed: float = 30.0 5 | var decay_rate: float = 5.0 6 | var strength: float = 0.0 7 | 8 | @onready var rand = RandomNumberGenerator.new() 9 | @onready var noise = FastNoiseLite.new() 10 | 11 | 12 | var camera: set = set_camera 13 | 14 | # Used to keep track of where we are in the noise 15 | # so that we can smoothly move through it 16 | var _noise_i: float = 0.0 17 | 18 | func _ready() -> void: 19 | rand.randomize() 20 | # Randomize the generated noise 21 | noise.seed = rand.randi() 22 | # Period affects how quickly the noise changes values 23 | SignalManager.screenshake_requested.connect(start_shake) 24 | SignalManager.screenshake_stop_requested.connect(stop_shake) 25 | 26 | 27 | func _process(delta: float) -> void: 28 | if not camera: 29 | return 30 | 31 | # Fade out the intensity over time 32 | strength = lerp(strength, 0.0, decay_rate * delta) 33 | 34 | # Shake by adjusting camera.offset so we can move the camera around the level via it's position 35 | var noise_offset: Vector2 = get_noise_offset(delta) 36 | 37 | if camera is Camera2D: 38 | var camera_2d: Camera2D = camera 39 | camera.offset = noise_offset 40 | 41 | elif camera is Camera3D: 42 | var camera_3d: Camera3D = camera 43 | camera.h_offset = noise_offset.x 44 | camera.v_offset = noise_offset.y 45 | 46 | 47 | func get_noise_offset(delta: float) -> Vector2: 48 | _noise_i += delta * speed 49 | # Set the x values of each call to 'get_noise_2d' to a different value 50 | # so that our x and y vectors will be reading from unrelated areas of noise 51 | return Vector2( 52 | noise.get_noise_2d(1, _noise_i) * strength, 53 | noise.get_noise_2d(100, _noise_i) * strength 54 | ) 55 | 56 | 57 | func start_shake(new_speed: float, new_strength: float, new_decay_rate: float) -> void: 58 | if speed == new_speed and strength == new_strength and decay_rate == new_decay_rate: 59 | return 60 | 61 | speed = new_speed 62 | strength = new_strength 63 | decay_rate = new_decay_rate 64 | 65 | 66 | func stop_shake() -> void: 67 | strength = 0 68 | 69 | 70 | func set_camera(new_camera: Node) -> void: 71 | if not new_camera is Camera2D and not new_camera is Camera3D: 72 | return 73 | 74 | camera = new_camera 75 | -------------------------------------------------------------------------------- /tools/follow_camera.gd: -------------------------------------------------------------------------------- 1 | extends Camera2D 2 | 3 | 4 | @export var linear_accleration: float = 4.0 5 | @export var angular_acceleration: float = 4.0 6 | @export var target_path: NodePath 7 | 8 | var target: Node2D 9 | 10 | 11 | func _ready(): 12 | if target_path == null: 13 | return 14 | 15 | target = get_node(target_path) 16 | 17 | 18 | func _process(delta): 19 | if not target: 20 | return 21 | 22 | rotation = lerp_angle(rotation, target.rotation, delta * angular_acceleration) 23 | position = lerp(global_position, target.global_position, delta * linear_accleration) 24 | -------------------------------------------------------------------------------- /tools/follow_camera.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://bbohku5ac83fk"] 2 | 3 | [ext_resource type="Script" path="res://tools/follow_camera.gd" id="1_20fox"] 4 | 5 | [node name="FollowCamera" type="Camera2D"] 6 | drag_horizontal_enabled = true 7 | drag_vertical_enabled = true 8 | drag_horizontal_offset = 0.2 9 | drag_vertical_offset = 0.1 10 | script = ExtResource("1_20fox") 11 | -------------------------------------------------------------------------------- /tools/instance_spawner.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://cd4pp7h4xvvcj"] 2 | 3 | [ext_resource type="Script" path="res://tools/instance_spawner.gd" id="1_7w1j3"] 4 | [ext_resource type="PackedScene" uid="uid://bw3f85cpb3gaa" path="res://levels/green_portal_effect.tscn" id="2_xfmjq"] 5 | 6 | [node name="InstanceSpawner" type="Node3D"] 7 | script = ExtResource("1_7w1j3") 8 | spawn_effect = ExtResource("2_xfmjq") 9 | 10 | [node name="SpawnTimer" type="Timer" parent="."] 11 | one_shot = true 12 | -------------------------------------------------------------------------------- /tools/map_data.gd: -------------------------------------------------------------------------------- 1 | class_name MapData 2 | extends Resource 3 | 4 | 5 | var paths: Array[PackedInt64Array] 6 | var nodes: Dictionary 7 | 8 | func set_paths(new_paths: Array[PackedInt64Array], points: Array[Vector2i]): 9 | paths = new_paths 10 | 11 | for path in paths: 12 | for id in path: 13 | nodes[id] = points[id] 14 | -------------------------------------------------------------------------------- /tools/map_event.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | 4 | const margin: float = 20 5 | 6 | var children: Array 7 | 8 | @onready var _sprite: Sprite2D = $Sprite2D 9 | 10 | 11 | func _ready() -> void: 12 | _apply_random_texture() 13 | 14 | 15 | func add_child_event(child) -> void: 16 | if not children.has(child): 17 | children.append(child) 18 | queue_redraw() 19 | 20 | 21 | func _draw() -> void: 22 | # draw_circle(Vector2.ZERO, 4, Color.WHITE_SMOKE) 23 | 24 | for child in children: 25 | var line: Vector2 = child.position - position 26 | var normal: Vector2 = line.normalized() 27 | line -= margin * normal 28 | var color = Color.DIM_GRAY 29 | draw_line(normal * margin, line, color, 2, true) 30 | 31 | 32 | func _apply_random_texture() -> void: 33 | var texture 34 | randomize() 35 | 36 | match randi_range(0, 5): 37 | 0: texture = load("res://materials/textures/Castle.png") 38 | 1: texture = load("res://materials/textures/Inferno.png") 39 | 2: texture = load("res://materials/textures/Necropolis.png") 40 | 3: texture = load("res://materials/textures/Rampart.png") 41 | 4: texture = load("res://materials/textures/Stronghold.png") 42 | 5: texture = load("res://materials/textures/Tower.png") 43 | _sprite.texture = texture 44 | -------------------------------------------------------------------------------- /tools/map_event.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://dxeswhr2wcvg3"] 2 | 3 | [ext_resource type="Script" path="res://tools/map_event.gd" id="1_yvbwn"] 4 | [ext_resource type="Texture2D" uid="uid://b3ddbp88u25vp" path="res://materials/textures/Castle.png" id="2_30ip7"] 5 | 6 | [node name="MapEvent" type="Node2D"] 7 | script = ExtResource("1_yvbwn") 8 | 9 | [node name="Sprite2D" type="Sprite2D" parent="."] 10 | z_index = 1 11 | scale = Vector2(0.75, 0.75) 12 | texture = ExtResource("2_30ip7") 13 | -------------------------------------------------------------------------------- /tools/map_generator.gd: -------------------------------------------------------------------------------- 1 | class_name MapGenerator 2 | extends Resource 3 | 4 | 5 | func generate(plane_length: int, node_count: int, path_count: int) -> MapData: 6 | randomize() 7 | 8 | # generate points on a grid 9 | var points: Array[Vector2i] 10 | var start_point:Vector2i = Vector2i(0, plane_length / 2) 11 | var end_point: Vector2i = Vector2i(plane_length, plane_length / 2) 12 | points.append(start_point) 13 | points.append(end_point) 14 | 15 | var center: Vector2i = Vector2i(plane_length / 2, plane_length / 2) 16 | 17 | for i in range(node_count): 18 | while true: 19 | var point: Vector2i = Vector2i(randi_range(0, plane_length - 1), randi_range(0, plane_length - 1)) 20 | 21 | var distance_from_center: int = (point - center).length_squared() 22 | var is_in_circle: bool = distance_from_center <= plane_length * plane_length / 4 23 | 24 | if not points.has(point) and is_in_circle: 25 | points.append(point) 26 | break 27 | 28 | # connect the points into a graph 29 | var pool: PackedVector2Array = PackedVector2Array(points) 30 | var triangles: PackedInt32Array = Geometry2D.triangulate_delaunay(pool) 31 | 32 | # find A* paths from start to finish 33 | var astar = AStar2D.new() 34 | for i in range(points.size()): 35 | astar.add_point(i, points[i]) 36 | 37 | for i in range(triangles.size() / 3): 38 | var p1: int = triangles[i * 3] 39 | var p2: int = triangles[i * 3 + 1] 40 | var p3: int = triangles[i * 3 + 2] 41 | 42 | astar.connect_points(p1, p2) 43 | astar.connect_points(p1, p3) 44 | astar.connect_points(p2, p3) 45 | 46 | var paths: Array[PackedInt64Array] 47 | 48 | for i in range(path_count): 49 | var id_path: PackedInt64Array = astar.get_id_path(0, 1) 50 | 51 | if id_path.size() == 0: 52 | break 53 | 54 | paths.append(id_path) 55 | 56 | # generate unique paths and remove nodes 57 | for j in range( randi_range(1, 2)) : 58 | var index = randi_range(0, id_path.size()- 2) 59 | 60 | var id: int = id_path[index] 61 | astar.set_point_disabled(id) 62 | 63 | var data: MapData = MapData.new() 64 | data.set_paths(paths, points) 65 | return data 66 | -------------------------------------------------------------------------------- /tools/player_detector.gd: -------------------------------------------------------------------------------- 1 | class_name PlayerDetector 2 | extends Area3D 3 | 4 | 5 | signal player_entered() 6 | signal player_exited() 7 | 8 | @export var one_shot: bool = false 9 | @export var start_enabled: bool = true 10 | 11 | var enabled: bool : set = set_enabled 12 | 13 | 14 | func _ready() -> void: 15 | body_entered.connect(_on_body_entered) 16 | body_exited.connect(_on_body_exited) 17 | 18 | set_enabled(start_enabled) 19 | collision_mask = 1 20 | 21 | 22 | func set_enabled(new_enabled: bool) -> void: 23 | enabled = new_enabled 24 | 25 | set_deferred("monitoring", enabled) 26 | 27 | 28 | func _player_entered() -> void: 29 | emit_signal("player_entered") 30 | 31 | 32 | func _player_exited() -> void: 33 | emit_signal("player_exited") 34 | if one_shot: 35 | set_deferred("monitoring", false) 36 | queue_free() 37 | 38 | 39 | func _on_body_entered(body: Node) -> void: 40 | if body.is_in_group("player"): 41 | _player_entered() 42 | 43 | 44 | func _on_body_exited(body: Node) -> void: 45 | if body.is_in_group("player"): 46 | _player_exited() 47 | -------------------------------------------------------------------------------- /tools/player_trigger.gd: -------------------------------------------------------------------------------- 1 | class_name PlayerTrigger 2 | extends PlayerDetector 3 | 4 | 5 | enum TRIGGER_TYPES { 6 | ENTER_EXIT, 7 | INTERACT, 8 | } 9 | 10 | @export var triggered_node_path: NodePath 11 | ## Enter exit will trigger on player entered and player exit. Interact will trigger if the player calls the interact function. 12 | @export var trigger_type: TRIGGER_TYPES = TRIGGER_TYPES.ENTER_EXIT 13 | ## The name of the function that will be called on the node in [member: 14 | @export var enter_function: String 15 | @export var exit_function: String 16 | 17 | 18 | func _ready() -> void: 19 | player_entered.connect(_on_player_entered) 20 | player_exited.connect(_on_player_exited) 21 | 22 | 23 | func _on_player_entered() -> void: 24 | if triggered_node_path == null: 25 | printerr("Triggered node path not defined in PlayerTrigger") 26 | 27 | var triggered_node: Node = get_node_or_null(triggered_node_path) 28 | 29 | if not triggered_node: 30 | printerr("Triggered node not found by PlayerTrigger") 31 | return 32 | 33 | if trigger_type == TRIGGER_TYPES.INTERACT: 34 | SignalManager.interactable_available.emit(self) 35 | return 36 | 37 | if not triggered_node.has_method(enter_function): 38 | printerr("Node triggered by PlayerTrigger does not have function %s" % enter_function) 39 | return 40 | 41 | triggered_node.call(enter_function) 42 | 43 | 44 | func _on_player_exited() -> void: 45 | if triggered_node_path == null: 46 | printerr("Triggered node path not defined in PlayerTrigger") 47 | 48 | var triggered_node: Node = get_node_or_null(triggered_node_path) 49 | 50 | if not triggered_node: 51 | printerr("Triggered node not found by PlayerTrigger") 52 | return 53 | 54 | if trigger_type == TRIGGER_TYPES.INTERACT: 55 | SignalManager.interactable_unavailable.emit(self) 56 | return 57 | 58 | if not triggered_node.has_method(exit_function): 59 | printerr("Node triggered by PlayerTrigger does not have function %s" % exit_function) 60 | return 61 | 62 | triggered_node.call(exit_function) 63 | -------------------------------------------------------------------------------- /tools/screenshake.gd: -------------------------------------------------------------------------------- 1 | class_name Screenshake 2 | extends Node 3 | 4 | var speed: float = 30 5 | var decay_rate: float = 5 6 | var strength: float = 0 7 | var camera: set = set_camera 8 | 9 | var _base_speed: float = 0 10 | var _base_strength: float = 0 11 | var _noise_i: float = 0.0 12 | 13 | @onready var _rand = RandomNumberGenerator.new() 14 | @onready var _noise = FastNoiseLite.new() 15 | 16 | func _ready() -> void: 17 | _rand.randomize() 18 | # Randomize the generated _noise 19 | _noise.seed = _rand.randi() 20 | # Period affects how quickly the _noise changes values 21 | SignalManager.screenshake_requested.connect(start_shake) 22 | SignalManager.screenshake_stop_requested.connect(stop_shake) 23 | 24 | 25 | func _process(delta: float) -> void: 26 | if not camera: 27 | return 28 | 29 | if strength <= 0.1: 30 | strength = _base_strength 31 | speed = _base_speed 32 | decay_rate = 0 33 | 34 | # Fade out the intensity over time 35 | strength = lerpf(strength, 0, decay_rate * delta) 36 | 37 | # Shake by adjusting camera.offset so we can move the camera around the level via it's position 38 | var noise_offset: Vector2 = get_noise_offset(delta) 39 | 40 | if camera is Camera2D: 41 | var camera_2d: Camera2D = camera 42 | camera.offset = noise_offset 43 | 44 | elif camera is Camera3D: 45 | var camera_3d: Camera3D = camera 46 | camera.h_offset = noise_offset.x 47 | camera.v_offset = noise_offset.y 48 | 49 | 50 | func get_noise_offset(delta: float) -> Vector2: 51 | _noise_i += delta * speed 52 | # Set the x values of each call to 'get_noise_2d' to a different value 53 | # so that our x and y vectors will be reading from unrelated areas of _noise 54 | return Vector2( 55 | _noise.get_noise_2d(1, _noise_i) * strength, 56 | _noise.get_noise_2d(100, _noise_i) * strength 57 | ) 58 | 59 | 60 | func start_shake(new_speed: float, new_strength: float, new_decay_rate: float) -> void: 61 | if speed == new_speed and strength == new_strength and decay_rate == new_decay_rate: 62 | return 63 | 64 | speed = new_speed 65 | strength = new_strength 66 | decay_rate = new_decay_rate 67 | 68 | if decay_rate == 0.0: 69 | _base_speed = speed 70 | _base_strength = strength 71 | 72 | set_process(true) 73 | 74 | 75 | func stop_shake() -> void: 76 | strength = 0 77 | 78 | 79 | func set_camera(new_camera: Node) -> void: 80 | if not new_camera is Camera2D and not new_camera is Camera3D: 81 | return 82 | 83 | camera = new_camera 84 | -------------------------------------------------------------------------------- /tools/screenshake.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://rd6ckqavshaf"] 2 | 3 | [ext_resource type="Script" path="res://tools/screenshake.gd" id="1_etk1j"] 4 | 5 | [node name="Screenshake" type="Node"] 6 | script = ExtResource("1_etk1j") 7 | 8 | [node name="FrequencyTimer" type="Timer" parent="."] 9 | 10 | [node name="DurationTimer" type="Timer" parent="."] 11 | one_shot = true 12 | -------------------------------------------------------------------------------- /tools/tool_color_rect.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | extends ColorRect 3 | 4 | ## I only created this node so that the color change of the BackgroundColorRect 5 | ## in [Launcher] would change visually as [member Launcher.background_color] 6 | ## would change in the editor. 7 | -------------------------------------------------------------------------------- /ui/example_theme.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Theme" load_steps=2 format=3 uid="uid://bgw1r7m1fxlg7"] 2 | 3 | [ext_resource type="FontFile" uid="uid://dl8tf0fow8l7o" path="res://ui/fonts/BIOSfontII.ttf" id="1_mvi7w"] 4 | 5 | [resource] 6 | default_font = ExtResource("1_mvi7w") 7 | default_font_size = 16 8 | Label/colors/font_color = Color(0.85098, 0.85098, 0.85098, 1) 9 | Label/colors/font_outline_color = Color(0, 0, 0, 1) 10 | Label/constants/outline_size = 4 11 | -------------------------------------------------------------------------------- /ui/fonts/BIOSfontII.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/ui/fonts/BIOSfontII.ttf -------------------------------------------------------------------------------- /ui/fonts/BIOSfontII.ttf.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="font_data_dynamic" 4 | type="FontFile" 5 | uid="uid://dl8tf0fow8l7o" 6 | path="res://.godot/imported/BIOSfontII.ttf-4b96f87be9b0c73757db8210c498656e.fontdata" 7 | 8 | [deps] 9 | 10 | source_file="res://ui/fonts/BIOSfontII.ttf" 11 | dest_files=["res://.godot/imported/BIOSfontII.ttf-4b96f87be9b0c73757db8210c498656e.fontdata"] 12 | 13 | [params] 14 | 15 | Rendering=null 16 | antialiasing=1 17 | generate_mipmaps=false 18 | multichannel_signed_distance_field=false 19 | msdf_pixel_range=8 20 | msdf_size=48 21 | allow_system_fallback=true 22 | force_autohinter=false 23 | hinting=1 24 | subpixel_positioning=1 25 | oversampling=0.0 26 | Fallbacks=null 27 | fallbacks=[] 28 | Compress=null 29 | compress=true 30 | preload=[] 31 | language_support={} 32 | script_support={} 33 | opentype_features={} 34 | -------------------------------------------------------------------------------- /ui/fonts/MMXSNES.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeroRobb/godot-psx-starter-project/4fbc70d4a4802aac90df2a62528b38609ef2feba/ui/fonts/MMXSNES.ttf -------------------------------------------------------------------------------- /ui/fonts/MMXSNES.ttf.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="font_data_dynamic" 4 | type="FontFile" 5 | uid="uid://dtylolct677sn" 6 | path="res://.godot/imported/MMXSNES.ttf-87663da39d692e2579252727c507f02d.fontdata" 7 | 8 | [deps] 9 | 10 | source_file="res://ui/fonts/MMXSNES.ttf" 11 | dest_files=["res://.godot/imported/MMXSNES.ttf-87663da39d692e2579252727c507f02d.fontdata"] 12 | 13 | [params] 14 | 15 | Rendering=null 16 | antialiasing=1 17 | generate_mipmaps=false 18 | multichannel_signed_distance_field=false 19 | msdf_pixel_range=8 20 | msdf_size=48 21 | allow_system_fallback=true 22 | force_autohinter=false 23 | hinting=1 24 | subpixel_positioning=1 25 | oversampling=0.0 26 | Fallbacks=null 27 | fallbacks=[] 28 | Compress=null 29 | compress=true 30 | preload=[] 31 | language_support={} 32 | script_support={} 33 | opentype_features={} 34 | -------------------------------------------------------------------------------- /ui/held_input_check.gd: -------------------------------------------------------------------------------- 1 | class_name HeldInputCheck 2 | extends Control 3 | 4 | ## This node emits a signal if an action is held for an amount of time. 5 | ## 6 | ## Set [member skip_action_name] and [member held_seconds] in the editor. This 7 | ## Node will show a progress bar while the action is held. After held_seconds, 8 | ## the progress bar will be filled and this node will emit [signal input_held]. 9 | ## Note that this uses [method physics_process] and not [method process] for 10 | ## a consistent delta value. 11 | 12 | 13 | signal input_held() 14 | 15 | ## This is the action name that this node will listen for. The action must 16 | ## already be set by code or from the top left of the editor: 17 | ## Project -> Project Settings -> InputMap. 18 | @export var skip_action_name: String = "ui_accept" 19 | ## This is how many seconds the above action must be held before this node 20 | ## emits the signal [signal input_held]. 21 | @export var held_seconds: float = 2 22 | 23 | @onready var _progress_bar: ProgressBar = $HBoxContainer/VBoxContainer/ProgressBar 24 | 25 | 26 | func _ready(): 27 | hide() 28 | _progress_bar.max_value = held_seconds 29 | 30 | 31 | func _physics_process(delta): 32 | if Input.is_action_just_pressed(skip_action_name): 33 | show() 34 | 35 | if Input.is_action_pressed(skip_action_name): 36 | _progress_bar.value += delta 37 | 38 | elif Input.is_action_just_released(skip_action_name): 39 | hide() 40 | _progress_bar.value = 0 41 | 42 | if _progress_bar.value == _progress_bar.max_value: 43 | input_held.emit() 44 | -------------------------------------------------------------------------------- /ui/held_input_check.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://bllcr31b4x2nu"] 2 | 3 | [ext_resource type="Script" path="res://ui/held_input_check.gd" id="1_m816t"] 4 | 5 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_m0s5l"] 6 | bg_color = Color(0.188235, 0.188235, 0.188235, 1) 7 | 8 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ir21x"] 9 | 10 | [node name="HeldInputCheck" type="MarginContainer"] 11 | visible = false 12 | anchors_preset = 15 13 | anchor_right = 1.0 14 | anchor_bottom = 1.0 15 | grow_horizontal = 2 16 | grow_vertical = 2 17 | theme_override_constants/margin_left = 8 18 | theme_override_constants/margin_top = 8 19 | theme_override_constants/margin_right = 8 20 | theme_override_constants/margin_bottom = 8 21 | script = ExtResource("1_m816t") 22 | 23 | [node name="HBoxContainer" type="HBoxContainer" parent="."] 24 | layout_mode = 2 25 | size_flags_horizontal = 8 26 | alignment = 2 27 | 28 | [node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"] 29 | layout_mode = 2 30 | alignment = 2 31 | 32 | [node name="Label" type="Label" parent="HBoxContainer/VBoxContainer"] 33 | layout_mode = 2 34 | text = "hold to skip" 35 | 36 | [node name="ProgressBar" type="ProgressBar" parent="HBoxContainer/VBoxContainer"] 37 | custom_minimum_size = Vector2(0, 10) 38 | layout_mode = 2 39 | theme_override_styles/background = SubResource("StyleBoxFlat_m0s5l") 40 | theme_override_styles/fill = SubResource("StyleBoxFlat_ir21x") 41 | max_value = 60.0 42 | step = 0.001 43 | show_percentage = false 44 | -------------------------------------------------------------------------------- /ui/menu_selection.gd: -------------------------------------------------------------------------------- 1 | @tool 2 | class_name MenuSelection 3 | extends HBoxContainer 4 | 5 | ## This Node is intended to be used in conjunction with 6 | ## [SelectionContainer] to create in game menus. 7 | ## 8 | ## This is intended to be a child of [SelectionContainer] with have siblings 9 | ## of it's own class and to have it's variables changed in the editor. 10 | 11 | 12 | signal moused_over(selection_index) 13 | signal slider_value_changed(new_value, id) 14 | 15 | ## This is what the player will see in game. If you want more text that is 16 | ## smaller or larger that this text, this node can have another label as a 17 | ## child. 18 | @export var selection_name: String = "" : set = set_selection_name 19 | ## This is whether this option will have a horizontal slider to manipulate, 20 | ## like a volume bar. 21 | @export var horizontal_input: bool = false 22 | ## This is the sound the horizontal slider will make when manipulated. 23 | @export var slider_sound_id: Global.SFX = Global.SFX.BLIP 24 | 25 | ## This value is if this node is currently selected by the player. 26 | var selected: bool = false : set = set_selected 27 | var selection_index: int 28 | 29 | var _slider: HSlider 30 | 31 | @onready var _label: Label = $SelectionLabel 32 | @onready var _texture_rect: TextureRect = $SelectedContainer/SelectedRect 33 | 34 | 35 | func _ready() -> void: 36 | mouse_entered.connect(_on_mouse_entered) 37 | set_selection_name(selection_name) 38 | 39 | for child in get_children(): 40 | if child.has_method("set_parent"): 41 | child.parent = self 42 | 43 | _slider = get_node_or_null("SliderContainer/HSlider") 44 | if _slider: 45 | _slider.value_changed.connect(_on_slider_value_changed) 46 | 47 | deselect() 48 | 49 | 50 | func select() -> void: 51 | set_selected(true) 52 | 53 | 54 | func deselect() -> void: 55 | set_selected(false) 56 | 57 | 58 | func action_left() -> void: 59 | if not horizontal_input or _slider == null: 60 | return 61 | 62 | SoundManager.play_sfx(slider_sound_id) 63 | _slider.value -= _slider.step 64 | 65 | 66 | func action_right() -> void: 67 | if not horizontal_input or _slider == null: 68 | return 69 | 70 | SoundManager.play_sfx(slider_sound_id) 71 | _slider.value += _slider.step 72 | 73 | 74 | func set_selection_name(new_name: String) -> void: 75 | selection_name = new_name 76 | if _label: 77 | _label.text = selection_name 78 | 79 | 80 | func set_selected(new_selected: bool) -> void: 81 | selected = new_selected 82 | if _texture_rect: 83 | _texture_rect.visible = selected 84 | 85 | 86 | func set_font_size(new_font_size: int) -> void: 87 | _label.add_theme_font_size_override("font_size", new_font_size) 88 | 89 | 90 | func _on_mouse_entered() -> void: 91 | moused_over.emit(selection_index) 92 | 93 | 94 | func _on_slider_value_changed(new_value: float) -> void: 95 | slider_value_changed.emit(new_value, selection_name) 96 | -------------------------------------------------------------------------------- /ui/menu_selection.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://d32vr0askpuce"] 2 | 3 | [ext_resource type="Script" path="res://ui/menu_selection.gd" id="1_ps0in"] 4 | [ext_resource type="Texture2D" uid="uid://ccg7oxvj0iowx" path="res://icon.svg" id="2_ncrnu"] 5 | 6 | [node name="MenuSelection" type="HBoxContainer"] 7 | offset_right = 40.0 8 | offset_bottom = 40.0 9 | script = ExtResource("1_ps0in") 10 | 11 | [node name="SelectedContainer" type="Control" parent="."] 12 | custom_minimum_size = Vector2(40, 40) 13 | layout_mode = 2 14 | 15 | [node name="SelectedRect" type="TextureRect" parent="SelectedContainer"] 16 | visible = false 17 | layout_mode = 0 18 | offset_right = 40.0 19 | offset_bottom = 40.0 20 | texture = ExtResource("2_ncrnu") 21 | expand_mode = 2 22 | stretch_mode = 5 23 | 24 | [node name="SelectionLabel" type="Label" parent="."] 25 | layout_mode = 2 26 | -------------------------------------------------------------------------------- /ui/menu_selection_container.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://ckw4dv0xxi1wh"] 2 | 3 | [ext_resource type="Script" path="res://ui/menu_selection_container.gd" id="1_3vmnq"] 4 | 5 | [node name="MenuSelectionContainer" type="VBoxContainer"] 6 | offset_right = 40.0 7 | offset_bottom = 40.0 8 | script = ExtResource("1_3vmnq") 9 | 10 | [node name="TitleLabel" type="Label" parent="."] 11 | layout_mode = 2 12 | text = "Menu Title" 13 | horizontal_alignment = 1 14 | --------------------------------------------------------------------------------