├── .gitignore ├── .gitmodules ├── GAME_CONCEPT.org ├── LICENSE ├── README.org ├── docs └── feedback-report │ ├── data.csv │ ├── img │ ├── 01.gamedev.png │ ├── 02.dev.png │ ├── 03.godot.png │ ├── 04.assignments.png │ └── 05.topics.png │ ├── process.py │ └── report-backers-feedback.org ├── game ├── addons │ └── level_design_dock │ │ ├── LevelDesignDock.gd │ │ ├── LevelDesignDock.tscn │ │ ├── PackedSceneButton.gd │ │ ├── PackedSceneButton.tscn │ │ ├── PackedSceneButtonGroup.tres │ │ ├── plugin.cfg │ │ └── plugin.gd ├── assets │ ├── UI │ │ ├── loading_wheel.png │ │ ├── loading_wheel.png.import │ │ ├── robi_portrait.png │ │ └── robi_portrait.png.import │ ├── characters │ │ └── robi │ │ │ ├── hook.png │ │ │ ├── hook.png.import │ │ │ ├── robi_shaded.png │ │ │ ├── robi_shaded.png.import │ │ │ ├── shadow.png │ │ │ └── shadow.png.import │ ├── environment │ │ ├── background │ │ │ ├── cloud_big_1.png │ │ │ ├── cloud_big_1.png.import │ │ │ ├── cloud_small_1.png │ │ │ ├── cloud_small_1.png.import │ │ │ ├── cloud_small_2.png │ │ │ ├── cloud_small_2.png.import │ │ │ ├── decoration │ │ │ │ ├── birch_tree_01.png │ │ │ │ ├── birch_tree_01.png.import │ │ │ │ ├── birch_tree_02.png │ │ │ │ ├── birch_tree_02.png.import │ │ │ │ ├── birch_tree_03.png │ │ │ │ ├── birch_tree_03.png.import │ │ │ │ ├── bush_01.png │ │ │ │ ├── bush_01.png.import │ │ │ │ ├── bush_02.png │ │ │ │ ├── bush_02.png.import │ │ │ │ ├── debris_01.png │ │ │ │ ├── debris_01.png.import │ │ │ │ ├── debris_02.png │ │ │ │ ├── debris_02.png.import │ │ │ │ ├── dirt_overlay.png │ │ │ │ ├── dirt_overlay.png.import │ │ │ │ ├── rock_01.png │ │ │ │ ├── rock_01.png.import │ │ │ │ ├── rock_02.png │ │ │ │ ├── rock_02.png.import │ │ │ │ ├── root_01.png │ │ │ │ └── root_01.png.import │ │ │ ├── mountains.png │ │ │ ├── mountains.png.import │ │ │ ├── sky.png │ │ │ └── sky.png.import │ │ ├── interactive │ │ │ ├── checkpoint01_bottomWingsB.png │ │ │ ├── checkpoint01_bottomWingsB.png.import │ │ │ ├── checkpoint02_bottomWingsT.png │ │ │ ├── checkpoint02_bottomWingsT.png.import │ │ │ ├── checkpoint03_bottom.png │ │ │ ├── checkpoint03_bottom.png.import │ │ │ ├── checkpoint04_gradient.png │ │ │ ├── checkpoint04_gradient.png.import │ │ │ ├── checkpoint05_headWingsB.png │ │ │ ├── checkpoint05_headWingsB.png.import │ │ │ ├── checkpoint06_headWingsT.png │ │ │ ├── checkpoint06_headWingsT.png.import │ │ │ ├── checkpoint07_head.png │ │ │ ├── checkpoint07_head.png.import │ │ │ ├── checkpoint08_crystal.png │ │ │ ├── checkpoint08_crystal.png.import │ │ │ ├── hookTarget01_glow.png │ │ │ ├── hookTarget01_glow.png.import │ │ │ ├── hookTarget02_ball.png │ │ │ ├── hookTarget02_ball.png.import │ │ │ ├── hookTarget03_wings.png │ │ │ ├── hookTarget03_wings.png.import │ │ │ ├── portal01_bottom.png │ │ │ ├── portal01_bottom.png.import │ │ │ ├── portal02_wings.png │ │ │ ├── portal02_wings.png.import │ │ │ ├── portal03_frame.png │ │ │ └── portal03_frame.png.import │ │ └── props │ │ │ ├── birch_tree_01.png │ │ │ ├── birch_tree_01.png.import │ │ │ ├── birch_tree_02.png │ │ │ ├── birch_tree_02.png.import │ │ │ ├── birch_tree_03.png │ │ │ ├── birch_tree_03.png.import │ │ │ ├── bush_01.png │ │ │ ├── bush_01.png.import │ │ │ ├── bush_02.png │ │ │ ├── bush_02.png.import │ │ │ ├── debris_01.png │ │ │ ├── debris_01.png.import │ │ │ ├── dirt_overlay.png │ │ │ ├── dirt_overlay.png.import │ │ │ ├── rock_01.png │ │ │ ├── rock_01.png.import │ │ │ ├── rock_02.png │ │ │ ├── rock_02.png.import │ │ │ ├── root_01.png │ │ │ └── root_01.png.import │ ├── icons │ │ ├── icon_hook.svg │ │ └── icon_hook.svg.import │ ├── libraries │ │ └── heatmap.gdnlib │ ├── theme │ │ ├── button │ │ │ ├── disabled.stylebox │ │ │ ├── focused.stylebox │ │ │ ├── hover.stylebox │ │ │ ├── normal.stylebox │ │ │ └── pressed.stylebox │ │ ├── empty.stylebox │ │ ├── fonts │ │ │ ├── default_font.tres │ │ │ ├── default_font_bold.tres │ │ │ ├── default_font_code.tres │ │ │ ├── font_title.tres │ │ │ ├── montserrat │ │ │ │ ├── Montserrat-Black.ttf │ │ │ │ ├── Montserrat-Bold.ttf │ │ │ │ └── Montserrat-Medium.ttf │ │ │ └── source_code_pro │ │ │ │ └── SourceCodePro-Medium.otf │ │ ├── gdquest.theme │ │ ├── icons │ │ │ ├── chevron-right.svg │ │ │ ├── chevron-right.svg.import │ │ │ ├── chevron-up.svg │ │ │ └── chevron-up.svg.import │ │ ├── panel │ │ │ └── panel.stylebox │ │ ├── separator │ │ │ └── line.tres │ │ └── slider │ │ │ ├── grabber_area.stylebox │ │ │ └── slider.stylebox │ └── tilesets │ │ ├── basic-circle.png.import │ │ ├── prototype.png │ │ ├── prototype.png.import │ │ ├── prototype.tres │ │ ├── textures │ │ ├── dirt.png │ │ ├── dirt.png.import │ │ ├── grass.png │ │ └── grass.png.import │ │ ├── tileset.png │ │ ├── tileset.png.import │ │ ├── valley.png │ │ ├── valley.png.import │ │ └── valley.tres ├── default_env.tres ├── icon.png ├── icon.png.import ├── project.godot └── src │ ├── AI │ ├── EnemyCharger.gd │ ├── EnemyCharger.tscn │ ├── EnemyPassivePatrol.gd │ ├── EnemyPassivePatrol.tscn │ ├── Heatmap.gdns │ ├── HopperEnemy.gd │ ├── HopperEnemy.tscn │ ├── Pathfinder.gd │ ├── PatrolAgent.gd │ ├── PatrolAgent.tscn │ ├── Scheduler │ │ ├── BalancedSubScheduler.gd │ │ ├── SchedulableJob.gd │ │ └── Scheduler.gd │ ├── States │ │ ├── Charge.gd │ │ ├── Cooldown.gd │ │ ├── Destroyed.gd │ │ ├── Hooked.gd │ │ ├── HorizontalPatrol.gd │ │ ├── Jump.gd │ │ ├── SearchRadius.gd │ │ └── Stunned.gd │ ├── Steering │ │ ├── BehaviorController2D.gd │ │ ├── Behaviors │ │ │ ├── BlendedBehavior2D.gd │ │ │ ├── Individual │ │ │ │ ├── AlignBehavior2D.gd │ │ │ │ ├── ArriveBehavior2D.gd │ │ │ │ ├── EvadeBehavior2D.gd │ │ │ │ ├── FleeBehavior2D.gd │ │ │ │ ├── FollowHeatmapBehavior2D.gd │ │ │ │ ├── InterceptBehavior2D.gd │ │ │ │ ├── SeekBehavior2D.gd │ │ │ │ └── StraightLineBehavior2D.gd │ │ │ └── PrioritySteering2D.gd │ │ ├── SteeringBehavior2D.gd │ │ └── SteeringMotion2D.gd │ ├── SwarmerEnemy.gd │ ├── SwarmerEnemy.tscn │ ├── SwarmerSpawner.gd │ └── SwarmerSpawner.tscn │ ├── Autoload │ ├── DrawingUtils.gd │ ├── Events.gd │ ├── LevelLoader.gd │ ├── Settings.gd │ ├── Steering.gd │ └── Utils.gd │ ├── Combat │ ├── DamageSource.gd │ ├── DamageSource.tscn │ ├── DummyEnemy.gd │ ├── DummyEnemy.tscn │ ├── Hit.gd │ ├── HitBox │ │ ├── HitBox.gd │ │ ├── HitBox.tscn │ │ └── hitbox_default.tres │ ├── Stats.gd │ └── Stats.tscn │ ├── Levels │ ├── Level.tscn │ ├── Level1.tscn │ ├── Level2.tscn │ └── SkyParallaxBackground.tscn │ ├── Main │ ├── Game.gd │ ├── Game.tscn │ └── StateMachine │ │ ├── State.gd │ │ └── StateMachine.gd │ ├── Native │ └── Heatmap │ │ ├── .gdignore │ │ ├── Heatmap.cpp │ │ ├── Heatmap.h │ │ ├── Heatmap.sln │ │ ├── Heatmap.vcxproj │ │ ├── Heatmap.vcxproj.filters │ │ ├── SConstruct │ │ └── gdlibrary.cpp │ ├── Objects │ ├── Checkpoint.gd │ ├── Checkpoint.tscn │ ├── FallLimitArea.tscn │ ├── HookTarget.gd │ ├── HookTarget.tscn │ ├── HookTargetPullable │ │ ├── HookTargetPullable.gd │ │ ├── HookTargetPullable.tscn │ │ ├── PropelledBody.gd │ │ └── PropelledBody.tscn │ ├── MovingPlatform │ │ ├── MovingPlatform.gd │ │ ├── MovingPlatform.tscn │ │ ├── Waypoints.gd │ │ └── Waypoints.tscn │ ├── Portal.gd │ └── Portal.tscn │ ├── Player │ ├── Camera │ │ ├── CameraAnchor.tscn │ │ ├── CameraAnchorDetector.gd │ │ ├── CameraAnchorDetector.tscn │ │ ├── CameraRig.gd │ │ ├── CameraRig.tscn │ │ ├── ShakingCamera.gd │ │ └── ShakingCamera.tscn │ ├── FloorDetector.gd │ ├── FloorDetector.tscn │ ├── Hook │ │ ├── Arrow.gd │ │ ├── Arrow.tscn │ │ ├── Hook.gd │ │ ├── Hook.tscn │ │ ├── HookingHint.gd │ │ ├── InputContinuous.gd │ │ ├── SnapDetector.gd │ │ ├── SnapDetector.tscn │ │ └── States │ │ │ ├── Aim.gd │ │ │ └── Fire.gd │ ├── LedgeWallDetector.gd │ ├── LedgeWallDetector.tscn │ ├── PassThrough.tscn │ ├── Player.gd │ ├── Player.tscn │ ├── Rectangle.gd │ ├── Skin.gd │ ├── Skin.tscn │ ├── Skin │ │ └── Shadow.gd │ └── States │ │ ├── Air.gd │ │ ├── Debug.gd │ │ ├── Die.gd │ │ ├── Hook.gd │ │ ├── HopOnEnemy.gd │ │ ├── Idle.gd │ │ ├── Ledge.gd │ │ ├── Move.gd │ │ ├── Run.gd │ │ ├── Spawn.gd │ │ ├── Stagger.gd │ │ └── Wall.gd │ └── UI │ ├── LoadingTransition.gd │ ├── LoadingTransition.tscn │ ├── Title.tscn │ └── debug │ ├── DebugDock.gd │ ├── DebugPanel.gd │ └── DebugPanel.tscn └── img ├── banner.png ├── banner.svg ├── flinthook-4.png ├── hollow-knight-3.png ├── momodora-2.png ├── ori-2.png └── prototypes ├── hook!-prototype-3.png └── level-design-loops-illustration.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Imported translations (automatically generated from CSV files) 8 | *.translation 9 | 10 | # Mono-specific ignores 11 | .mono/ 12 | data_*/ 13 | mono_crash.*.json 14 | 15 | # System/tool-specific ignores 16 | .directory 17 | *~ 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "GDNative"] 2 | path = GDNative 3 | url = https://github.com/GodotNativeTools/godot-cpp 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Nathan Lovato 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 | -------------------------------------------------------------------------------- /docs/feedback-report/img/01.gamedev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/docs/feedback-report/img/01.gamedev.png -------------------------------------------------------------------------------- /docs/feedback-report/img/02.dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/docs/feedback-report/img/02.dev.png -------------------------------------------------------------------------------- /docs/feedback-report/img/03.godot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/docs/feedback-report/img/03.godot.png -------------------------------------------------------------------------------- /docs/feedback-report/img/04.assignments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/docs/feedback-report/img/04.assignments.png -------------------------------------------------------------------------------- /docs/feedback-report/img/05.topics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/docs/feedback-report/img/05.topics.png -------------------------------------------------------------------------------- /game/addons/level_design_dock/LevelDesignDock.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Control 3 | 4 | signal packed_scene_button_created(button) 5 | onready var grid_container: GridContainer = $VBoxContainer/GridContainer 6 | 7 | const PackedSceneButtonScene := preload("res://addons/level_design_dock/PackedSceneButton.tscn") 8 | const PackedSceneButton := preload("res://addons/level_design_dock/PackedSceneButton.gd") 9 | 10 | func drop_data(position: Vector2, data: Dictionary) -> void: 11 | for packed_scene_path in get_valid_files(data["files"]): 12 | create_packed_scene_button(packed_scene_path) 13 | 14 | 15 | func can_drop_data(position: Vector2, data: Dictionary) -> bool: 16 | var can_drop := false 17 | if data["type"] == "files": 18 | can_drop = get_valid_files(data["files"]).size() > 0 19 | return can_drop 20 | 21 | 22 | func create_packed_scene_button(packed_scene_path: String) -> void: 23 | var new_button := PackedSceneButtonScene.instance() as PackedSceneButton 24 | new_button.packed_scene = load(packed_scene_path) 25 | new_button.text = packed_scene_path.get_file().trim_suffix(".tscn") 26 | grid_container.add_child(new_button) 27 | emit_signal("packed_scene_button_created", new_button) 28 | 29 | 30 | func get_valid_files(files_list: Array) -> Array: 31 | var valid_files: Array = [] 32 | for file_path in files_list: 33 | if file_path.ends_with(".tscn"): 34 | valid_files.append(file_path) 35 | return valid_files 36 | -------------------------------------------------------------------------------- /game/addons/level_design_dock/LevelDesignDock.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://addons/level_design_dock/LevelDesignDock.gd" type="Script" id=1] 4 | [ext_resource path="res://assets/theme/fonts/font_title.tres" type="DynamicFont" id=2] 5 | 6 | [node name="LevelDesignDock" type="Control"] 7 | anchor_right = 1.0 8 | anchor_bottom = 1.0 9 | script = ExtResource( 1 ) 10 | 11 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 12 | anchor_right = 1.0 13 | anchor_bottom = 1.0 14 | 15 | [node name="Title" type="Label" parent="VBoxContainer"] 16 | margin_right = 1920.0 17 | margin_bottom = 36.0 18 | size_flags_horizontal = 3 19 | custom_fonts/font = ExtResource( 2 ) 20 | text = "Available Scenes" 21 | align = 1 22 | valign = 1 23 | 24 | [node name="GridContainer" type="GridContainer" parent="VBoxContainer"] 25 | margin_top = 40.0 26 | margin_right = 1920.0 27 | margin_bottom = 1080.0 28 | size_flags_horizontal = 3 29 | size_flags_vertical = 3 30 | -------------------------------------------------------------------------------- /game/addons/level_design_dock/PackedSceneButton.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Button 3 | 4 | var packed_scene: PackedScene 5 | -------------------------------------------------------------------------------- /game/addons/level_design_dock/PackedSceneButton.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://addons/level_design_dock/PackedSceneButtonGroup.tres" type="ButtonGroup" id=1] 4 | [ext_resource path="res://addons/level_design_dock/PackedSceneButton.gd" type="Script" id=2] 5 | 6 | [node name="PackedSceneButton" type="Button"] 7 | margin_right = 40.0 8 | margin_bottom = 40.0 9 | rect_min_size = Vector2( 64, 64 ) 10 | toggle_mode = true 11 | group = ExtResource( 1 ) 12 | script = ExtResource( 2 ) 13 | -------------------------------------------------------------------------------- /game/addons/level_design_dock/PackedSceneButtonGroup.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="ButtonGroup" format=2] 2 | 3 | [resource] 4 | -------------------------------------------------------------------------------- /game/addons/level_design_dock/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="Level Design Dock" 4 | description="The FileSystem meets the Tilemap to easier the process of designing levels by instancing scenes just like painting tiles" 5 | author="Henrique \"Pigdev\" Campos" 6 | version="0.1.1" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /game/assets/UI/loading_wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/UI/loading_wheel.png -------------------------------------------------------------------------------- /game/assets/UI/loading_wheel.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/loading_wheel.png-07a52355d62247afcaaaf79302a3311d.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/UI/loading_wheel.png" 13 | dest_files=[ "res://.import/loading_wheel.png-07a52355d62247afcaaaf79302a3311d.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/UI/robi_portrait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/UI/robi_portrait.png -------------------------------------------------------------------------------- /game/assets/UI/robi_portrait.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/robi_portrait.png-68ad9ce9f4cb884df10b44b5417451b8.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/UI/robi_portrait.png" 13 | dest_files=[ "res://.import/robi_portrait.png-68ad9ce9f4cb884df10b44b5417451b8.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/characters/robi/hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/characters/robi/hook.png -------------------------------------------------------------------------------- /game/assets/characters/robi/hook.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/hook.png-d1f5caa231f9bb67c3d85b4c2932d056.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/characters/robi/hook.png" 13 | dest_files=[ "res://.import/hook.png-d1f5caa231f9bb67c3d85b4c2932d056.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/characters/robi/robi_shaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/characters/robi/robi_shaded.png -------------------------------------------------------------------------------- /game/assets/characters/robi/robi_shaded.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/robi_shaded.png-09fb4858b03ca9988b88379ada2dffd9.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/characters/robi/robi_shaded.png" 13 | dest_files=[ "res://.import/robi_shaded.png-09fb4858b03ca9988b88379ada2dffd9.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/characters/robi/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/characters/robi/shadow.png -------------------------------------------------------------------------------- /game/assets/characters/robi/shadow.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/shadow.png-fe7e4abf6b53754798b0ae3907dee806.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/characters/robi/shadow.png" 13 | dest_files=[ "res://.import/shadow.png-fe7e4abf6b53754798b0ae3907dee806.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/cloud_big_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/cloud_big_1.png -------------------------------------------------------------------------------- /game/assets/environment/background/cloud_big_1.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/cloud_big_1.png-8af83e13972366923e71e7376eb72822.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/cloud_big_1.png" 13 | dest_files=[ "res://.import/cloud_big_1.png-8af83e13972366923e71e7376eb72822.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=1 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/cloud_small_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/cloud_small_1.png -------------------------------------------------------------------------------- /game/assets/environment/background/cloud_small_1.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/cloud_small_1.png-b2addf668e401a05348eb9bb0b7f0ade.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/cloud_small_1.png" 13 | dest_files=[ "res://.import/cloud_small_1.png-b2addf668e401a05348eb9bb0b7f0ade.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=1 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/cloud_small_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/cloud_small_2.png -------------------------------------------------------------------------------- /game/assets/environment/background/cloud_small_2.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/cloud_small_2.png-3ff64d2d2e33f334f085144aae32957d.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/cloud_small_2.png" 13 | dest_files=[ "res://.import/cloud_small_2.png-3ff64d2d2e33f334f085144aae32957d.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=1 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/birch_tree_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/birch_tree_01.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/birch_tree_01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/birch_tree_01.png-0ccf0dc99f30e8c807ae141c198368f8.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/birch_tree_01.png" 13 | dest_files=[ "res://.import/birch_tree_01.png-0ccf0dc99f30e8c807ae141c198368f8.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/birch_tree_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/birch_tree_02.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/birch_tree_02.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/birch_tree_02.png-0471b59cc88388ed30ca9fa977a7edca.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/birch_tree_02.png" 13 | dest_files=[ "res://.import/birch_tree_02.png-0471b59cc88388ed30ca9fa977a7edca.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/birch_tree_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/birch_tree_03.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/birch_tree_03.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/birch_tree_03.png-7eeb8defa379d299d9944143047b0052.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/birch_tree_03.png" 13 | dest_files=[ "res://.import/birch_tree_03.png-7eeb8defa379d299d9944143047b0052.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/bush_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/bush_01.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/bush_01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/bush_01.png-46e6e35b32a484039289e84079092b75.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/bush_01.png" 13 | dest_files=[ "res://.import/bush_01.png-46e6e35b32a484039289e84079092b75.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/bush_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/bush_02.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/bush_02.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/bush_02.png-5c9b32c8e79b42e952bcd59345a40fe7.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/bush_02.png" 13 | dest_files=[ "res://.import/bush_02.png-5c9b32c8e79b42e952bcd59345a40fe7.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/debris_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/debris_01.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/debris_01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/debris_01.png-2bcd3d085c23d6947b3daa8e3aa72ebb.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/debris_01.png" 13 | dest_files=[ "res://.import/debris_01.png-2bcd3d085c23d6947b3daa8e3aa72ebb.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/debris_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/debris_02.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/debris_02.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/debris_02.png-3714f6488fe13eb2775bb47cbaaad785.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/debris_02.png" 13 | dest_files=[ "res://.import/debris_02.png-3714f6488fe13eb2775bb47cbaaad785.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/dirt_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/dirt_overlay.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/dirt_overlay.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/dirt_overlay.png-8d8b5918679bb91e2e1f2733eefab68b.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/dirt_overlay.png" 13 | dest_files=[ "res://.import/dirt_overlay.png-8d8b5918679bb91e2e1f2733eefab68b.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/rock_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/rock_01.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/rock_01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/rock_01.png-cc41a3b575fa934e47f8b1288a390709.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/rock_01.png" 13 | dest_files=[ "res://.import/rock_01.png-cc41a3b575fa934e47f8b1288a390709.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/rock_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/rock_02.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/rock_02.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/rock_02.png-2fe54c58effd432f6340a9d2f482959d.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/rock_02.png" 13 | dest_files=[ "res://.import/rock_02.png-2fe54c58effd432f6340a9d2f482959d.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/root_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/decoration/root_01.png -------------------------------------------------------------------------------- /game/assets/environment/background/decoration/root_01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/root_01.png-0aeda26c8778e12167102dcfdb29267b.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/decoration/root_01.png" 13 | dest_files=[ "res://.import/root_01.png-0aeda26c8778e12167102dcfdb29267b.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/mountains.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/mountains.png -------------------------------------------------------------------------------- /game/assets/environment/background/mountains.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/mountains.png-5dcd4591219602158bbbdc464d7a19a5.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/mountains.png" 13 | dest_files=[ "res://.import/mountains.png-5dcd4591219602158bbbdc464d7a19a5.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=1 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=false 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/background/sky.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/background/sky.png -------------------------------------------------------------------------------- /game/assets/environment/background/sky.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/sky.png-a5517b333d3147868e017127dcd96076.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/background/sky.png" 13 | dest_files=[ "res://.import/sky.png-a5517b333d3147868e017127dcd96076.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=1 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint01_bottomWingsB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/checkpoint01_bottomWingsB.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint01_bottomWingsB.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/checkpoint01_bottomWingsB.png-05f81af94805771419d862fbe1828d2b.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/checkpoint01_bottomWingsB.png" 13 | dest_files=[ "res://.import/checkpoint01_bottomWingsB.png-05f81af94805771419d862fbe1828d2b.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint02_bottomWingsT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/checkpoint02_bottomWingsT.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint02_bottomWingsT.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/checkpoint02_bottomWingsT.png-f5301bce34784afa2a963b46b3aa67c1.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/checkpoint02_bottomWingsT.png" 13 | dest_files=[ "res://.import/checkpoint02_bottomWingsT.png-f5301bce34784afa2a963b46b3aa67c1.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint03_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/checkpoint03_bottom.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint03_bottom.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/checkpoint03_bottom.png-19db38f1b2cc30e5cc20a835794b66ae.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/checkpoint03_bottom.png" 13 | dest_files=[ "res://.import/checkpoint03_bottom.png-19db38f1b2cc30e5cc20a835794b66ae.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint04_gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/checkpoint04_gradient.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint04_gradient.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/checkpoint04_gradient.png-3f573656acfd34ed46416227782373e1.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/checkpoint04_gradient.png" 13 | dest_files=[ "res://.import/checkpoint04_gradient.png-3f573656acfd34ed46416227782373e1.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint05_headWingsB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/checkpoint05_headWingsB.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint05_headWingsB.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/checkpoint05_headWingsB.png-4752600259c2f9ec2844b2e2659b50c2.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/checkpoint05_headWingsB.png" 13 | dest_files=[ "res://.import/checkpoint05_headWingsB.png-4752600259c2f9ec2844b2e2659b50c2.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint06_headWingsT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/checkpoint06_headWingsT.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint06_headWingsT.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/checkpoint06_headWingsT.png-c37dad95a07b18d3a349b0acc07c1553.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/checkpoint06_headWingsT.png" 13 | dest_files=[ "res://.import/checkpoint06_headWingsT.png-c37dad95a07b18d3a349b0acc07c1553.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint07_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/checkpoint07_head.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint07_head.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/checkpoint07_head.png-f1254a7b9805deb218aa476e913e94f9.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/checkpoint07_head.png" 13 | dest_files=[ "res://.import/checkpoint07_head.png-f1254a7b9805deb218aa476e913e94f9.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint08_crystal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/checkpoint08_crystal.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/checkpoint08_crystal.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/checkpoint08_crystal.png-0647028c779c87cc16beb1eb8349f760.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/checkpoint08_crystal.png" 13 | dest_files=[ "res://.import/checkpoint08_crystal.png-0647028c779c87cc16beb1eb8349f760.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/hookTarget01_glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/hookTarget01_glow.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/hookTarget01_glow.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/hookTarget01_glow.png-390adf7be46b2300e346d88830cdd283.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/hookTarget01_glow.png" 13 | dest_files=[ "res://.import/hookTarget01_glow.png-390adf7be46b2300e346d88830cdd283.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/hookTarget02_ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/hookTarget02_ball.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/hookTarget02_ball.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/hookTarget02_ball.png-78f26a9e60056ac2f54fb506fa0e4167.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/hookTarget02_ball.png" 13 | dest_files=[ "res://.import/hookTarget02_ball.png-78f26a9e60056ac2f54fb506fa0e4167.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/hookTarget03_wings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/hookTarget03_wings.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/hookTarget03_wings.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/hookTarget03_wings.png-e244d97f008fc20d1a419b81df500c5e.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/hookTarget03_wings.png" 13 | dest_files=[ "res://.import/hookTarget03_wings.png-e244d97f008fc20d1a419b81df500c5e.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/portal01_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/portal01_bottom.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/portal01_bottom.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/portal01_bottom.png-5c34558f3ea339bf6379f5f25db4d95b.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/portal01_bottom.png" 13 | dest_files=[ "res://.import/portal01_bottom.png-5c34558f3ea339bf6379f5f25db4d95b.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/portal02_wings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/portal02_wings.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/portal02_wings.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/portal02_wings.png-17e11e174c84fa04d775d89cd182fba8.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/portal02_wings.png" 13 | dest_files=[ "res://.import/portal02_wings.png-17e11e174c84fa04d775d89cd182fba8.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/interactive/portal03_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/interactive/portal03_frame.png -------------------------------------------------------------------------------- /game/assets/environment/interactive/portal03_frame.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/portal03_frame.png-86af14d8ba4d218e1902f2e8b74f8abf.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/interactive/portal03_frame.png" 13 | dest_files=[ "res://.import/portal03_frame.png-86af14d8ba4d218e1902f2e8b74f8abf.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/props/birch_tree_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/props/birch_tree_01.png -------------------------------------------------------------------------------- /game/assets/environment/props/birch_tree_01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/birch_tree_01.png-2ab8aa4a1774f9f8e9c13752371c14e5.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/props/birch_tree_01.png" 13 | dest_files=[ "res://.import/birch_tree_01.png-2ab8aa4a1774f9f8e9c13752371c14e5.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/props/birch_tree_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/props/birch_tree_02.png -------------------------------------------------------------------------------- /game/assets/environment/props/birch_tree_02.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/birch_tree_02.png-8fcacc631d4992811c95015a5e654f6e.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/props/birch_tree_02.png" 13 | dest_files=[ "res://.import/birch_tree_02.png-8fcacc631d4992811c95015a5e654f6e.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/props/birch_tree_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/props/birch_tree_03.png -------------------------------------------------------------------------------- /game/assets/environment/props/birch_tree_03.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/birch_tree_03.png-ebe107dac7a70fc68eabf3382069b162.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/props/birch_tree_03.png" 13 | dest_files=[ "res://.import/birch_tree_03.png-ebe107dac7a70fc68eabf3382069b162.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/props/bush_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/props/bush_01.png -------------------------------------------------------------------------------- /game/assets/environment/props/bush_01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/bush_01.png-6aef5f7223fce3d41c73ea1cd6202f55.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/props/bush_01.png" 13 | dest_files=[ "res://.import/bush_01.png-6aef5f7223fce3d41c73ea1cd6202f55.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/props/bush_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/props/bush_02.png -------------------------------------------------------------------------------- /game/assets/environment/props/bush_02.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/bush_02.png-168e6431783a8bb08671b106fe2d144c.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/props/bush_02.png" 13 | dest_files=[ "res://.import/bush_02.png-168e6431783a8bb08671b106fe2d144c.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/props/debris_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/props/debris_01.png -------------------------------------------------------------------------------- /game/assets/environment/props/debris_01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/debris_01.png-93ec4818c9713301caa22103de4414bf.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/props/debris_01.png" 13 | dest_files=[ "res://.import/debris_01.png-93ec4818c9713301caa22103de4414bf.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/props/dirt_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/props/dirt_overlay.png -------------------------------------------------------------------------------- /game/assets/environment/props/dirt_overlay.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/dirt_overlay.png-088e40f73aaecb2cb3c104ef47426bc8.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/props/dirt_overlay.png" 13 | dest_files=[ "res://.import/dirt_overlay.png-088e40f73aaecb2cb3c104ef47426bc8.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/props/rock_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/props/rock_01.png -------------------------------------------------------------------------------- /game/assets/environment/props/rock_01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/rock_01.png-601d7ab9e560278410e707ab7f996ac2.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/props/rock_01.png" 13 | dest_files=[ "res://.import/rock_01.png-601d7ab9e560278410e707ab7f996ac2.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/props/rock_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/props/rock_02.png -------------------------------------------------------------------------------- /game/assets/environment/props/rock_02.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/rock_02.png-c28d6de67d75e323453a70b9eb2bede6.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/props/rock_02.png" 13 | dest_files=[ "res://.import/rock_02.png-c28d6de67d75e323453a70b9eb2bede6.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/environment/props/root_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/environment/props/root_01.png -------------------------------------------------------------------------------- /game/assets/environment/props/root_01.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/root_01.png-9b7932718614ff40ed228e41b0228f87.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/environment/props/root_01.png" 13 | dest_files=[ "res://.import/root_01.png-9b7932718614ff40ed228e41b0228f87.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/icons/icon_hook.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon_hook.svg-12c60923ff847745e502f1821ca2838a.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/icons/icon_hook.svg" 13 | dest_files=[ "res://.import/icon_hook.svg-12c60923ff847745e502f1821ca2838a.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/libraries/heatmap.gdnlib: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | singleton=false 4 | load_once=true 5 | symbol_prefix="godot_" 6 | reloadable=true 7 | 8 | [entry] 9 | 10 | OSX.64="res://assets/libraries/osx/libheatmap.dylib" 11 | Windows.64="res://assets/libraries/win64/libheatmap.dll" 12 | X11.64="res://assets/libraries/x11/libheatmap.so" 13 | 14 | [dependencies] 15 | 16 | OSX.64=[ ] 17 | Windows.64=[ ] 18 | X11.64=[ ] 19 | -------------------------------------------------------------------------------- /game/assets/theme/button/disabled.stylebox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/button/disabled.stylebox -------------------------------------------------------------------------------- /game/assets/theme/button/focused.stylebox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/button/focused.stylebox -------------------------------------------------------------------------------- /game/assets/theme/button/hover.stylebox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/button/hover.stylebox -------------------------------------------------------------------------------- /game/assets/theme/button/normal.stylebox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/button/normal.stylebox -------------------------------------------------------------------------------- /game/assets/theme/button/pressed.stylebox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/button/pressed.stylebox -------------------------------------------------------------------------------- /game/assets/theme/empty.stylebox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/empty.stylebox -------------------------------------------------------------------------------- /game/assets/theme/fonts/default_font.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://assets/theme/fonts/montserrat/Montserrat-Medium.ttf" type="DynamicFontData" id=1] 4 | 5 | [resource] 6 | size = 20 7 | use_filter = true 8 | font_data = ExtResource( 1 ) 9 | -------------------------------------------------------------------------------- /game/assets/theme/fonts/default_font_bold.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://assets/theme/fonts/montserrat/Montserrat-Bold.ttf" type="DynamicFontData" id=1] 4 | 5 | 6 | [resource] 7 | 8 | size = 20 9 | use_filter = true 10 | font_data = ExtResource( 1 ) 11 | 12 | -------------------------------------------------------------------------------- /game/assets/theme/fonts/default_font_code.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://assets/theme/fonts/source_code_pro/SourceCodePro-Medium.otf" type="DynamicFontData" id=1] 4 | 5 | 6 | [resource] 7 | 8 | size = 20 9 | use_filter = true 10 | font_data = ExtResource( 1 ) 11 | 12 | -------------------------------------------------------------------------------- /game/assets/theme/fonts/font_title.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="DynamicFont" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://assets/theme/fonts/montserrat/Montserrat-Black.ttf" type="DynamicFontData" id=1] 4 | 5 | 6 | [resource] 7 | 8 | size = 28 9 | use_filter = true 10 | font_data = ExtResource( 1 ) 11 | 12 | -------------------------------------------------------------------------------- /game/assets/theme/fonts/montserrat/Montserrat-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/fonts/montserrat/Montserrat-Black.ttf -------------------------------------------------------------------------------- /game/assets/theme/fonts/montserrat/Montserrat-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/fonts/montserrat/Montserrat-Bold.ttf -------------------------------------------------------------------------------- /game/assets/theme/fonts/montserrat/Montserrat-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/fonts/montserrat/Montserrat-Medium.ttf -------------------------------------------------------------------------------- /game/assets/theme/fonts/source_code_pro/SourceCodePro-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/fonts/source_code_pro/SourceCodePro-Medium.otf -------------------------------------------------------------------------------- /game/assets/theme/gdquest.theme: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/gdquest.theme -------------------------------------------------------------------------------- /game/assets/theme/icons/chevron-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /game/assets/theme/icons/chevron-right.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/chevron-right.svg-f77dee7a088177a2ac1d467f4c7cd3e1.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/theme/icons/chevron-right.svg" 13 | dest_files=[ "res://.import/chevron-right.svg-f77dee7a088177a2ac1d467f4c7cd3e1.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/theme/icons/chevron-up.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /game/assets/theme/icons/chevron-up.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/chevron-up.svg-48b5b69265734774d0a7516dcc6f0863.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/theme/icons/chevron-up.svg" 13 | dest_files=[ "res://.import/chevron-up.svg-48b5b69265734774d0a7516dcc6f0863.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/theme/panel/panel.stylebox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/panel/panel.stylebox -------------------------------------------------------------------------------- /game/assets/theme/separator/line.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="StyleBoxLine" format=2] 2 | 3 | [resource] 4 | 5 | color = Color( 1, 1, 1, 0.196078 ) 6 | thickness = 2 7 | 8 | -------------------------------------------------------------------------------- /game/assets/theme/slider/grabber_area.stylebox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/slider/grabber_area.stylebox -------------------------------------------------------------------------------- /game/assets/theme/slider/slider.stylebox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/theme/slider/slider.stylebox -------------------------------------------------------------------------------- /game/assets/tilesets/basic-circle.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/basic-circle.png-40f10661b8ed375018b4e6e31067c459.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/tilesets/basic-circle.png" 13 | dest_files=[ "res://.import/basic-circle.png-40f10661b8ed375018b4e6e31067c459.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/tilesets/prototype.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/tilesets/prototype.png -------------------------------------------------------------------------------- /game/assets/tilesets/prototype.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/prototype.png-85ea78eac123d7185a730b7ca9cbd5e1.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/tilesets/prototype.png" 13 | dest_files=[ "res://.import/prototype.png-85ea78eac123d7185a730b7ca9cbd5e1.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/tilesets/prototype.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="TileSet" load_steps=4 format=2] 2 | 3 | [ext_resource path="res://assets/tilesets/prototype.png" type="Texture" id=1] 4 | 5 | [sub_resource type="ConvexPolygonShape2D" id=1] 6 | points = PoolVector2Array( 0, 0, 40, 0, 40, 40, 0, 40 ) 7 | 8 | [sub_resource type="ConvexPolygonShape2D" id=2] 9 | points = PoolVector2Array( 0, 0, 40, 0, 40, 40, 0, 40 ) 10 | 11 | [resource] 12 | 0/name = "solid" 13 | 0/texture = ExtResource( 1 ) 14 | 0/tex_offset = Vector2( 0, 0 ) 15 | 0/modulate = Color( 1, 1, 1, 1 ) 16 | 0/region = Rect2( 5, 5, 40, 40 ) 17 | 0/tile_mode = 0 18 | 0/occluder_offset = Vector2( 0, 0 ) 19 | 0/navigation_offset = Vector2( 0, 0 ) 20 | 0/shape_offset = Vector2( 0, 0 ) 21 | 0/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) 22 | 0/shape = SubResource( 1 ) 23 | 0/shape_one_way = false 24 | 0/shape_one_way_margin = 1.0 25 | 0/shapes = [ { 26 | "autotile_coord": Vector2( 0, 0 ), 27 | "one_way": false, 28 | "one_way_margin": 1.0, 29 | "shape": SubResource( 1 ), 30 | "shape_transform": Transform2D( 1, 0, 0, 1, 0, 0 ) 31 | } ] 32 | 0/z_index = 0 33 | 1/name = "pass-through" 34 | 1/texture = ExtResource( 1 ) 35 | 1/tex_offset = Vector2( 0, 0 ) 36 | 1/modulate = Color( 1, 1, 1, 1 ) 37 | 1/region = Rect2( 55, 5, 40, 40 ) 38 | 1/tile_mode = 0 39 | 1/occluder_offset = Vector2( 0, 0 ) 40 | 1/navigation_offset = Vector2( 0, 0 ) 41 | 1/shape_offset = Vector2( 0, 0 ) 42 | 1/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) 43 | 1/shape = SubResource( 2 ) 44 | 1/shape_one_way = true 45 | 1/shape_one_way_margin = 1.0 46 | 1/shapes = [ { 47 | "autotile_coord": Vector2( 0, 0 ), 48 | "one_way": true, 49 | "one_way_margin": 1.0, 50 | "shape": SubResource( 2 ), 51 | "shape_transform": Transform2D( 1, 0, 0, 1, 0, 0 ) 52 | } ] 53 | 1/z_index = 0 54 | 2/name = "red" 55 | 2/texture = ExtResource( 1 ) 56 | 2/tex_offset = Vector2( 0, 0 ) 57 | 2/modulate = Color( 1, 1, 1, 1 ) 58 | 2/region = Rect2( 5, 55, 40, 40 ) 59 | 2/tile_mode = 0 60 | 2/occluder_offset = Vector2( 0, 0 ) 61 | 2/navigation_offset = Vector2( 0, 0 ) 62 | 2/shape_offset = Vector2( 0, 0 ) 63 | 2/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) 64 | 2/shape_one_way = false 65 | 2/shape_one_way_margin = 0.0 66 | 2/shapes = [ ] 67 | 2/z_index = 0 68 | 3/name = "purple" 69 | 3/texture = ExtResource( 1 ) 70 | 3/tex_offset = Vector2( 0, 0 ) 71 | 3/modulate = Color( 1, 1, 1, 1 ) 72 | 3/region = Rect2( 55, 55, 40, 40 ) 73 | 3/tile_mode = 0 74 | 3/occluder_offset = Vector2( 0, 0 ) 75 | 3/navigation_offset = Vector2( 0, 0 ) 76 | 3/shape_offset = Vector2( 0, 0 ) 77 | 3/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) 78 | 3/shape_one_way = false 79 | 3/shape_one_way_margin = 0.0 80 | 3/shapes = [ ] 81 | 3/z_index = 0 82 | -------------------------------------------------------------------------------- /game/assets/tilesets/textures/dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/tilesets/textures/dirt.png -------------------------------------------------------------------------------- /game/assets/tilesets/textures/dirt.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/dirt.png-aee2018ad5ff67a794789fd289703f41.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/tilesets/textures/dirt.png" 13 | dest_files=[ "res://.import/dirt.png-aee2018ad5ff67a794789fd289703f41.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/tilesets/textures/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/tilesets/textures/grass.png -------------------------------------------------------------------------------- /game/assets/tilesets/textures/grass.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/grass.png-56c175b007c16f3eabb77a504a3c7255.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/tilesets/textures/grass.png" 13 | dest_files=[ "res://.import/grass.png-56c175b007c16f3eabb77a504a3c7255.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/tilesets/tileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/tilesets/tileset.png -------------------------------------------------------------------------------- /game/assets/tilesets/tileset.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/tileset.png-07d912f8b4c07111b880b883656d8231.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/tilesets/tileset.png" 13 | dest_files=[ "res://.import/tileset.png-07d912f8b4c07111b880b883656d8231.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/assets/tilesets/valley.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/assets/tilesets/valley.png -------------------------------------------------------------------------------- /game/assets/tilesets/valley.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/valley.png-cddf82037fa9d60ae0c775d2229a2706.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://assets/tilesets/valley.png" 13 | dest_files=[ "res://.import/valley.png-cddf82037fa9d60ae0c775d2229a2706.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=false 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /game/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/icon.png -------------------------------------------------------------------------------- /game/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /game/src/AI/EnemyCharger.gd: -------------------------------------------------------------------------------- 1 | extends KinematicBody2D 2 | 3 | onready var target_detector: Area2D = $TargetDetector -------------------------------------------------------------------------------- /game/src/AI/EnemyCharger.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=11 format=2] 2 | 3 | [ext_resource path="res://src/AI/EnemyCharger.gd" type="Script" id=1] 4 | [ext_resource path="res://src/Main/StateMachine/StateMachine.gd" type="Script" id=2] 5 | [ext_resource path="res://src/AI/States/SearchRadius.gd" type="Script" id=3] 6 | [ext_resource path="res://src/AI/States/Charge.gd" type="Script" id=4] 7 | [ext_resource path="res://src/AI/States/Cooldown.gd" type="Script" id=5] 8 | [ext_resource path="res://src/AI/Steering/BehaviorController2D.gd" type="Script" id=6] 9 | [ext_resource path="res://src/AI/Steering/Behaviors/Individual/StraightLineBehavior2D.gd" type="Script" id=7] 10 | [ext_resource path="res://src/Player/Rectangle.gd" type="Script" id=8] 11 | 12 | [sub_resource type="RectangleShape2D" id=1] 13 | extents = Vector2( 30, 30 ) 14 | 15 | [sub_resource type="CircleShape2D" id=2] 16 | radius = 300.0 17 | 18 | [node name="EnemyCharger" type="KinematicBody2D"] 19 | collision_layer = 0 20 | collision_mask = 2 21 | script = ExtResource( 1 ) 22 | 23 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 24 | shape = SubResource( 1 ) 25 | 26 | [node name="TargetDetector" type="Area2D" parent="."] 27 | input_pickable = false 28 | monitorable = false 29 | collision_layer = 0 30 | 31 | [node name="CollisionShape2D" type="CollisionShape2D" parent="TargetDetector"] 32 | shape = SubResource( 2 ) 33 | 34 | [node name="StateMachine" type="Node" parent="."] 35 | script = ExtResource( 2 ) 36 | initial_state = NodePath("Search") 37 | 38 | [node name="Search" type="Node" parent="StateMachine"] 39 | script = ExtResource( 3 ) 40 | 41 | [node name="Charge" type="Node" parent="StateMachine"] 42 | script = ExtResource( 4 ) 43 | behavior = NodePath("../../BehaviorController2D/StraightLineBehavior2D") 44 | 45 | [node name="Timer" type="Timer" parent="StateMachine/Charge"] 46 | 47 | [node name="Cooldown" type="Node" parent="StateMachine"] 48 | script = ExtResource( 5 ) 49 | 50 | [node name="BehaviorController2D" type="Node" parent="."] 51 | script = ExtResource( 6 ) 52 | actor_path = NodePath("..") 53 | max_acceleration = 600.0 54 | 55 | [node name="StraightLineBehavior2D" type="Node" parent="BehaviorController2D"] 56 | script = ExtResource( 7 ) 57 | 58 | [node name="Body" type="Node2D" parent="."] 59 | script = ExtResource( 8 ) 60 | size = Vector2( 54, 54 ) 61 | outline = Vector2( 6, 6 ) 62 | color_fill = Color( 0.890625, 0.583793, 0.149597, 1 ) 63 | color_outline = Color( 0.901961, 0.172549, 0.137255, 1 ) 64 | -------------------------------------------------------------------------------- /game/src/AI/EnemyPassivePatrol.gd: -------------------------------------------------------------------------------- 1 | extends KinematicBody2D 2 | # A passive enemy; simply patrols side to side and bumps into the player for damage. 3 | # Can be hooked onto, and will be damaged. 4 | 5 | 6 | onready var collider: CollisionShape2D = $CollisionShape2D setget ,get_collider 7 | onready var hook_target: Area2D = $HookTarget 8 | onready var body: Node2D = $Body 9 | onready var hitbox: Area2D = $Hitbox 10 | 11 | export var can_be_hooked_while_stunned := false 12 | export var move_speed := 200 13 | export var direction := -1 14 | 15 | func _ready() -> void: 16 | if can_be_hooked_while_stunned: 17 | hook_target.is_one_shot = false 18 | 19 | 20 | func get_collider() -> CollisionShape2D: 21 | return collider 22 | -------------------------------------------------------------------------------- /game/src/AI/Heatmap.gdns: -------------------------------------------------------------------------------- 1 | [gd_resource type="NativeScript" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://assets/libraries/heatmap.gdnlib" type="GDNativeLibrary" id=1] 4 | 5 | [resource] 6 | resource_name = "heatmap" 7 | class_name = "Heatmap" 8 | library = ExtResource( 1 ) 9 | -------------------------------------------------------------------------------- /game/src/AI/HopperEnemy.gd: -------------------------------------------------------------------------------- 1 | extends KinematicBody2D 2 | 3 | onready var hitbox: Area2D = $Hitbox 4 | onready var hook_target: HookTarget = $HookTarget 5 | onready var collider: CollisionShape2D = $CollisionShape2D 6 | onready var body: Node2D = $Body 7 | 8 | export(int, 0, 360, 1) var jump_angle_left := 45 9 | export(int, 0, 360, 1) var jump_angle_right := 45 10 | export var jump_power_left := 500 11 | export var jump_power_right := 500 12 | export(int, -1, 1, 2) var direction := 1 13 | export var gravity := 6000.0 14 | -------------------------------------------------------------------------------- /game/src/AI/PatrolAgent.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends KinematicBody2D 3 | # Simple AI agent to teach game AI programming basics. 4 | # Patrols between two points, stopping at each point to wait for a moment. 5 | # Detects gaps and walls, and turns around instead of getting stuck or falling in holes. 6 | 7 | 8 | const ARRIVE_THRESHOLD := 3.0 9 | 10 | onready var floor_detector: RayCast2D = $Pivot/FloorDetector 11 | onready var pivot: Position2D = $Pivot 12 | onready var timer: Timer = $Timer 13 | 14 | export var speed := 300.0 15 | export var gravity := 1000.0 16 | 17 | var waypoints := {} 18 | var _target: Vector2 19 | 20 | var _velocity := Vector2(0, gravity) 21 | 22 | 23 | func _ready() -> void: 24 | if Engine.editor_hint: 25 | set_physics_process(false) 26 | else: 27 | timer.connect('timeout', self, '_on_Timer_timeout') 28 | position = floor_detector.get_floor_position() 29 | 30 | waypoints = { 31 | start=$Start.global_position, 32 | end=$End.global_position, 33 | } 34 | _target = waypoints.end 35 | update() 36 | 37 | 38 | func _physics_process(delta: float) -> void: 39 | _velocity = Steering.arrive_to( 40 | _velocity, 41 | global_position, 42 | _target, 43 | delta, 44 | speed) 45 | move_and_slide(_velocity) 46 | 47 | if not floor_detector.is_close_to_floor() or \ 48 | global_position.distance_to(_target) < ARRIVE_THRESHOLD: 49 | stop() 50 | 51 | 52 | func stop() -> void: 53 | set_physics_process(false) 54 | timer.start() 55 | 56 | 57 | func walk(): 58 | set_physics_process(true) 59 | 60 | 61 | func turn() -> void: 62 | _velocity.x *= -1 63 | pivot.scale.x *= -1 64 | _target = waypoints.start if _target == waypoints.end else waypoints.end 65 | 66 | 67 | func _on_Timer_timeout() -> void: 68 | if Engine.editor_hint: 69 | return 70 | turn() 71 | walk() 72 | 73 | 74 | # Draws the path the agent walks in the editor 75 | func _draw() -> void: 76 | if not Engine.editor_hint or not $Start: 77 | return 78 | 79 | var draw_radius := 20.0 80 | var line_thickness := 6.0 81 | 82 | var start: Vector2 = waypoints.start 83 | var end: Vector2 = waypoints.end 84 | 85 | # Path 86 | draw_line(start, end, DrawingUtils.COLOR_BLUE_LIGHT, line_thickness) 87 | draw_circle(start, draw_radius, DrawingUtils.COLOR_BLUE_DEEP) 88 | draw_circle(end, draw_radius, DrawingUtils.COLOR_BLUE_DEEP) 89 | 90 | # Arrow 91 | var center := (start + end) / 2 92 | var angle := start.angle_to(end) 93 | DrawingUtils.draw_triangle(self, center, angle, draw_radius) 94 | 95 | func _get_configuration_warning() -> String: 96 | var warning := "" 97 | if not $Start or not $End: 98 | warning += "%s requires two Position2D children named Start and End to work." % name 99 | return warning 100 | -------------------------------------------------------------------------------- /game/src/AI/PatrolAgent.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://src/AI/PatrolAgent.gd" type="Script" id=1] 4 | [ext_resource path="res://src/Player/Rectangle.gd" type="Script" id=2] 5 | [ext_resource path="res://src/Player/FloorDetector.tscn" type="PackedScene" id=3] 6 | 7 | [sub_resource type="RectangleShape2D" id=1] 8 | extents = Vector2( 30, 30 ) 9 | 10 | [node name="PatrolAgent" type="KinematicBody2D"] 11 | collision_layer = 32 12 | collision_mask = 3 13 | script = ExtResource( 1 ) 14 | 15 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 16 | position = Vector2( 0, -30 ) 17 | shape = SubResource( 1 ) 18 | 19 | [node name="Body" type="Node2D" parent="."] 20 | position = Vector2( 0, -30 ) 21 | script = ExtResource( 2 ) 22 | size = Vector2( 50, 50 ) 23 | outline = Vector2( 10, 10 ) 24 | color_fill = Color( 0.894118, 0.203922, 0.396078, 1 ) 25 | color_outline = Color( 0.670588, 0.0431373, 0.0431373, 1 ) 26 | 27 | [node name="Pivot" type="Position2D" parent="."] 28 | editor/display_folded = true 29 | 30 | [node name="FloorDetector" parent="Pivot" instance=ExtResource( 3 )] 31 | position = Vector2( 40, 0 ) 32 | enabled = true 33 | 34 | [node name="Timer" type="Timer" parent="."] 35 | process_mode = 0 36 | one_shot = true 37 | 38 | [node name="Start" type="Position2D" parent="."] 39 | position = Vector2( -150, 0 ) 40 | 41 | [node name="End" type="Position2D" parent="."] 42 | position = Vector2( 220, 0 ) 43 | -------------------------------------------------------------------------------- /game/src/AI/Scheduler/BalancedSubScheduler.gd: -------------------------------------------------------------------------------- 1 | extends SchedulableJob 2 | class_name BalancedSubScheduler 3 | # A scheduler that spreads its budget evenly between all jobs. It runs as a job itself on the 4 | # priority scheduler as a low priority job. The user shouldn't interact with this directly. 5 | 6 | var _current_frame: int = 0 7 | var _job_list: Array = [] 8 | var _run_list: Array = [] 9 | 10 | func _add_new_job(job, frequency: int, phase: int) -> void: 11 | job.phase = phase 12 | job.frequency = frequency 13 | _job_list.append(weakref(job)) 14 | 15 | 16 | func _remove_job(job: SchedulableJob) -> void: 17 | var index := _job_list.find(job) 18 | if index >= 0: 19 | _job_list.remove(index) 20 | 21 | 22 | func _run(microseconds_budget: int) -> void: 23 | _current_frame += 1 24 | 25 | _run_list.clear() 26 | 27 | var i := _job_list.size()-1 28 | while i >= 0: 29 | var job := ((_job_list[i] as WeakRef).get_ref() as SchedulableJob) 30 | i -= 1 31 | if !job: 32 | _job_list.remove(i+1) 33 | continue 34 | 35 | if (_current_frame + job.phase) % job.frequency == 0: 36 | _run_list.append(job) 37 | 38 | var current_job := 0 39 | var usec_budget := microseconds_budget 40 | var last_time := OS.get_ticks_usec() 41 | 42 | var list_size := _run_list.size() 43 | for i in range(0, list_size): 44 | var job := _run_list[i] as SchedulableJob 45 | var current_time := OS.get_ticks_usec() 46 | usec_budget -= current_time - last_time 47 | 48 | var available_time := int(float(usec_budget) / float(_run_list.size() - current_job)) 49 | job._run(available_time) 50 | 51 | last_time = current_time 52 | current_job += 1 53 | -------------------------------------------------------------------------------- /game/src/AI/Scheduler/SchedulableJob.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | class_name SchedulableJob 3 | # A SchedulableJob to be used as an extension to create individual jobs. 4 | 5 | # # A job runs at a set frequency, offset by its calculated phase. When its time is up, `_run` will be 6 | # called with the amount of microseconds it is allowed to use before it should save its current state 7 | # and return, to be resumed at its next call. However, there is no mechanism in place to forcibly 8 | # abort a function - it is up to the job to play nice and keep track of how much time it's taken. 9 | 10 | # # The Scheduler has a helper function `get_elapsed_microseconds` that is a wrapper around godot's 11 | # `OS.get_ticks_usec()` call that can be used for this purpose. 12 | 13 | # # Notes 14 | # ----- 15 | # 1 second == 1,000 milliseconds == 1,000,000 microseconds 16 | # In most 60 fps games, an average frame takes 16.66 milliseconds to both update and render. 17 | 18 | # warning-ignore:unused_class_variable 19 | var priority: int 20 | # warning-ignore:unused_class_variable 21 | var frequency: int 22 | # warning-ignore:unused_class_variable 23 | var phase: int 24 | 25 | # warning-ignore:unused_argument 26 | func _run(microseconds_budget: int) -> void: 27 | pass 28 | -------------------------------------------------------------------------------- /game/src/AI/States/Charge.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # Charge attack in a straight line. 3 | # Controls the amount of acceleration and speed from the straight line behavior's 4 | # desired velocity, and only charges for a set amount of time before moving to cooldown. 5 | 6 | 7 | onready var _behavior: StraightLineBehavior2D = get_node(behavior) 8 | onready var timer: Timer = $Timer 9 | 10 | export var behavior := NodePath() 11 | export var charge_time := 1.0 12 | export var time_to_full_speed = 0.5 13 | 14 | var _steering := SteeringMotion2D.new() 15 | 16 | 17 | func enter(msg: Dictionary = {}) -> void: 18 | var target: KinematicBody2D = msg.target 19 | var target_position: Vector2 20 | if target.has_method("get_collider"): 21 | var target_shape: CollisionShape2D = target.get_collider() 22 | target_position = target_shape.global_position 23 | else: 24 | target_position = target.global_position 25 | 26 | _behavior.target = (target_position - _behavior.controller.actor.global_position).normalized() * 10000 27 | 28 | timer.connect("timeout", self, "_on_Timer_timeout") 29 | timer.start(charge_time) 30 | 31 | 32 | func exit() -> void: 33 | timer.disconnect("timeout", self, "_on_Timer_timeout") 34 | 35 | 36 | func physics_process(delta: float) -> void: 37 | _behavior.calculate_steering(_steering) 38 | var velocity := _steering.velocity 39 | 40 | var speed_proportion: float = clamp((charge_time - timer.time_left) / time_to_full_speed, 0, 1) 41 | (_behavior.controller.actor as KinematicBody2D).move_and_slide(velocity * speed_proportion) 42 | 43 | 44 | func _on_Timer_timeout() -> void: 45 | _state_machine.transition_to("Cooldown") 46 | -------------------------------------------------------------------------------- /game/src/AI/States/Cooldown.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # Waits for a given amount of time 3 | 4 | export var cooldown_time := 1.5 5 | 6 | func enter(msg: Dictionary = {}) -> void: 7 | yield(get_tree().create_timer(cooldown_time), "timeout") 8 | _state_machine.transition_to("Search") 9 | -------------------------------------------------------------------------------- /game/src/AI/States/Destroyed.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # State to make enemy fade to nothing, then get released by the scene tree. 3 | 4 | 5 | export var hurt_color: Color 6 | 7 | 8 | func enter(msg: Dictionary = {}) -> void: 9 | owner.body.set_color_fill(hurt_color) 10 | var animation_player: AnimationPlayer = $AnimationPlayer 11 | animation_player.play("FadeOut") 12 | yield(animation_player, "animation_finished") 13 | owner.queue_free() -------------------------------------------------------------------------------- /game/src/AI/States/Hooked.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # This state is for enemy's response to being grappled with the hook. 3 | # It simply waits until the player body collides with the hitbox, colors the body, 4 | # then transitions to `state_when_struck` 5 | 6 | 7 | export var hooked_color: Color 8 | 9 | var hook_position: Vector2 10 | 11 | 12 | func enter(msg: Dictionary = {}) -> void: 13 | owner.body.set_color_fill(hooked_color) 14 | hook_position = msg.hook_position 15 | owner.hitbox.connect("body_entered", self, "_on_Player_body_entered", [], CONNECT_ONESHOT) 16 | 17 | 18 | func _on_Player_body_entered(body: Player) -> void: 19 | _state_machine.transition_to("Stunned", {player = body, hook_position = hook_position}) -------------------------------------------------------------------------------- /game/src/AI/States/HorizontalPatrol.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # State for a simple patroller that goes side to side, changing direction when it bumps into walls. 3 | # Responds to player using grappling hook. 4 | 5 | 6 | onready var _move_speed: float = owner.move_speed 7 | onready var _direction: int = owner.direction 8 | 9 | 10 | func enter(msg: Dictionary = {}) -> void: 11 | owner.hook_target.connect("hooked_onto_from", self, "_on_Hook_hooked_onto_from", [], CONNECT_ONESHOT) 12 | owner.hitbox.connect("body_entered", self, "_on_Player_body_entered") 13 | owner.hook_target.is_active = true 14 | 15 | 16 | func exit() -> void: 17 | owner.hitbox.disconnect("body_entered", self, "_on_Player_body_entered") 18 | 19 | 20 | func physics_process(delta: float) -> void: 21 | var collision: KinematicCollision2D = owner.move_and_collide(Vector2(_direction * _move_speed * delta, 0)) 22 | if collision and collision.collider is TileMap: 23 | _direction *= -1 24 | 25 | 26 | func _on_Hook_hooked_onto_from(hook_position: Vector2) -> void: 27 | _state_machine.transition_to("Hooked", {hook_position = hook_position}) 28 | 29 | 30 | func _on_Player_body_entered(player: Player) -> void: 31 | player.take_damage(Hit.new(owner.hitbox)) -------------------------------------------------------------------------------- /game/src/AI/States/Jump.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | 3 | 4 | onready var timer: Timer = $Cooldown 5 | onready var acceleration := Vector2(0, owner.gravity) 6 | onready var move_direction := Vector2(-owner.direction, 1) 7 | onready var jump_vector_right = Vector2(cos(deg2rad(owner.jump_angle_right)), -sin(deg2rad(owner.jump_angle_right))) 8 | onready var jump_vector_left = Vector2(cos(deg2rad(owner.jump_angle_left)), -sin(deg2rad(owner.jump_angle_left))) 9 | 10 | var _velocity := Vector2.ZERO 11 | var _jumping := false 12 | 13 | 14 | func enter(msg: Dictionary = {}) -> void: 15 | owner.hitbox.connect("body_entered", self, "_on_Player_body_entered") 16 | owner.hook_target.connect("hooked_onto_from", self, "_on_Player_hooked_onto_from", [], CONNECT_ONESHOT) 17 | timer.connect("timeout", self, "_on_Cooldown_timeout") 18 | timer.start() 19 | 20 | 21 | func exit() -> void: 22 | owner.hitbox.disconnect("body_entered", self, "_on_Player_body_entered") 23 | 24 | 25 | func physics_process(delta: float) -> void: 26 | if not _jumping and owner.is_on_floor(): 27 | return 28 | 29 | _velocity = calculate_velocity(_velocity, acceleration, delta, move_direction) 30 | owner.move_and_slide(_velocity, Vector2.UP) 31 | if owner.is_on_floor(): 32 | _jumping = false 33 | _velocity = Vector2.ZERO 34 | move_direction.x *= -1 35 | timer.start() 36 | 37 | 38 | func _on_Player_body_entered(player: Player) -> void: 39 | player.take_damage(Hit.new(owner.hitbox)) 40 | 41 | 42 | func _on_Player_hooked_onto_from(hook_position: Vector2) -> void: 43 | _state_machine.transition_to("Hooked", {hook_position = hook_position}) 44 | 45 | 46 | func _on_Cooldown_timeout() -> void: 47 | var jump_vector: Vector2 = jump_vector_right if move_direction.x > 0 else jump_vector_left 48 | var jump_power: float = owner.jump_power_right if move_direction.x > 0 else owner.jump_power_left 49 | _velocity += calculate_velocity(_velocity, jump_vector * jump_power * Vector2(1,-1), 1.0, jump_vector * move_direction) 50 | _jumping = true 51 | 52 | 53 | static func calculate_velocity(old_velocity: Vector2, acceleration: Vector2, 54 | delta: float, move_direction: Vector2) -> Vector2: 55 | var new_velocity := old_velocity 56 | 57 | new_velocity += move_direction * acceleration * delta 58 | 59 | return new_velocity 60 | -------------------------------------------------------------------------------- /game/src/AI/States/SearchRadius.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # Detects the player entering the search area. 3 | # Sets the target position to aim for to the straight line charge behavior. 4 | 5 | 6 | func enter(msg: Dictionary = {}) -> void: 7 | owner.target_detector.connect("body_entered", self, "_on_Area2D_body_entered") 8 | var overlapping_bodies: Array = owner.target_detector.get_overlapping_bodies() 9 | if overlapping_bodies.size() > 0: 10 | _state_machine.transition_to("Charge", {target=overlapping_bodies[0]}) 11 | 12 | 13 | func exit() -> void: 14 | owner.target_detector.disconnect("body_entered", self, "_on_Area2D_body_entered") 15 | 16 | 17 | func _on_Area2D_body_entered(body: PhysicsBody2D) -> void: 18 | _state_machine.transition_to("Charge", {target=body}) 19 | -------------------------------------------------------------------------------- /game/src/AI/States/Stunned.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # State that connects to the player's signal about hopping off an entity, 3 | # and gets knocked away before transitioning. 4 | 5 | 6 | export var knock_back_speed := 450.0 7 | 8 | var knocked_away := false 9 | var current_speed: float 10 | var knock_back_direction: Vector2 11 | 12 | var _player: Player 13 | 14 | 15 | func enter(msg: Dictionary = {}) -> void: 16 | knock_back_direction = (msg.hook_position - owner.global_position).normalized() 17 | knocked_away = false 18 | _player = msg.player 19 | _player.connect("hopped_off_entity", self, "_on_Player_hopped_off_entity", [], CONNECT_ONESHOT) 20 | current_speed = knock_back_speed 21 | 22 | 23 | func physics_process(delta: float) -> void: 24 | if knocked_away: 25 | owner.move_and_collide(knock_back_direction * delta * current_speed) 26 | current_speed *= 0.95 27 | 28 | 29 | func _on_Player_hopped_off_entity() -> void: 30 | knocked_away = true 31 | yield(get_tree().create_timer(0.5), "timeout") 32 | _state_machine.transition_to("Destroyed") 33 | -------------------------------------------------------------------------------- /game/src/AI/Steering/BehaviorController2D.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | class_name BehaviorController2D 3 | # Root node of steering-based AI behaviors. Stores and provides information about its children. 4 | 5 | # # BehaviorController2D is the hub through which behaviors access various 6 | # information about themselves, and their targets. It holds the maximum speeds and 7 | # acceleration amounts, and has properties to store velocities that can be used in 8 | # predictive behaviors. BehaviorController2D does not automatically collect this 9 | # information. It is up to the developer to set its properties. 10 | 11 | 12 | export var actor_path: NodePath 13 | export var max_speed := 400.0 14 | export var max_acceleration := 1000.0 15 | export var max_rotation_speed := 10.0 16 | export var max_angular_acceleration := 10.0 17 | 18 | var velocity := Vector2.ZERO 19 | var angular_velocity := 0.0 20 | var target_velocity := Vector2.ZERO 21 | var target_angular_velocity := 0.0 22 | 23 | onready var actor: Node2D = get_node(actor_path) 24 | 25 | 26 | # Returns true if a Node2D was successfully put into the controller. 27 | func has_valid_agent() -> bool: 28 | return actor != null 29 | -------------------------------------------------------------------------------- /game/src/AI/Steering/Behaviors/BlendedBehavior2D.gd: -------------------------------------------------------------------------------- 1 | extends SteeringBehavior2D 2 | class_name BlendedBehavior2D 3 | # Runs through all of its children and blends all motions together by a defined weight. 4 | 5 | # # The blended behavior first calculates a child's motion, multiplies it by its weight, then adds it 6 | # to the motion buffer before returning it. The motion is clamped to the controller's maximums. 7 | # The weights are multipliers on the strength of the individual behaviors. They're not scaled down to sum to one. 8 | # The index of a given weight in the array should refer to the given index of its child. 9 | 10 | # # Notes 11 | # ----- 12 | # You can build complex behaviors with a blended behavior compared to indivudal ones without writing 13 | # a complex, custom behavior. But it is more costly to run since all of BlendedBehavior2D's children are evaluated. 14 | # Figuring out the weight to give each behavior can also be tricky and takes some trial-and-error. 15 | 16 | # # Weights can lead to conflicting forces, which may cause weird behaviors. 17 | 18 | # # The recommended use is to have a priority steering with a set of blended behaviors, based on need. 19 | 20 | export var weights := [] 21 | 22 | var _steering := SteeringMotion2D.new() 23 | 24 | 25 | # Returns the steering motion with a blend of all of the behavior's children, clamped to the controller's maximum values. 26 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 27 | steering.reset_values() 28 | 29 | var size := get_child_count() 30 | for i in range(0, size): 31 | var child := get_child(i) as SteeringBehavior2D 32 | if not child: 33 | continue 34 | 35 | var steering := child as SteeringBehavior2D 36 | steering.calculate_steering(_steering) 37 | 38 | var weight := 1.0 39 | if weights.size() >= i: 40 | weight = weights[i] 41 | 42 | steering.motion += (_steering.motion * weight) 43 | steering.angular_velocity += (_steering.angular_motion * weight) 44 | 45 | steering.motion.clamped(controller.max_acceleration) 46 | steering.angular_velocity = min(controller.max_angular_acceleration, steering.angular_velocity) 47 | 48 | return steering 49 | -------------------------------------------------------------------------------- /game/src/AI/Steering/Behaviors/Individual/AlignBehavior2D.gd: -------------------------------------------------------------------------------- 1 | extends SteeringBehavior2D 2 | class_name AlignBehavior2D 3 | # Tries to rotate itself to match the target's own rotation. 4 | 5 | # # Properties: 6 | 7 | # # - `alignment_tolerance` is there to prevent flicker, so as to not constantly 8 | # overshoot and start rotating back the other way. 9 | # - `deceleration_radius` is the interval of degrees to begin slowing down the 10 | # rotation 11 | # - `time_to_target` is the amount of time in a fixed time scale 12 | # to weight the amount of rotation by. It should be a small value, 13 | # and defaults to 0.1. 14 | 15 | # # The only target supported is Node2D and should be fed in ahead of time. 16 | 17 | 18 | export var deceleration_radius := 0.0 19 | export var alignment_tolerance := 0.0 20 | export var time_to_target := 0.1 21 | 22 | var target: Node2D 23 | 24 | 25 | # Returns a steering motion that has no linear acceleration but a angular acceleration that will 26 | # match its own to its target's orientation. 27 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 28 | if not target: 29 | return steering.reset_values() 30 | return _align(steering, target.rotation) 31 | 32 | 33 | # The internal function that calculates the alignment based on the radians passed into the function. 34 | # Returns the steering motion with the angular acceleration that will match its own to the rotation value. 35 | func _align(steering: SteeringMotion2D, desired_rotation: float) -> SteeringMotion2D: 36 | var rotation_size := abs(desired_rotation - get_actor().rotation) 37 | var alignment_tolerance_rad := deg2rad(alignment_tolerance) 38 | 39 | if rotation_size <= alignment_tolerance_rad: 40 | steering.reset_values() 41 | else: 42 | var target_rotation := deg2rad(controller.max_rotation_speed) 43 | var deceleration_radius_rad := deg2rad(deceleration_radius) 44 | 45 | if rotation_size <= deceleration_radius_rad: 46 | target_rotation *= rotation_size / deceleration_radius_rad 47 | target_rotation *= desired_rotation / rotation_size 48 | 49 | steering.angular_velocity = (target_rotation - controller.angular_velocity) / time_to_target 50 | 51 | var angular_acceleration := abs(steering.angular_velocity) 52 | var max_angular_acceleration_rad := deg2rad(controller.max_angular_acceleration) 53 | if angular_acceleration > max_angular_acceleration_rad: 54 | steering.angular_velocity *= max_angular_acceleration_rad / angular_acceleration 55 | 56 | steering.velocity = Vector2.ZERO 57 | 58 | return steering 59 | -------------------------------------------------------------------------------- /game/src/AI/Steering/Behaviors/Individual/ArriveBehavior2D.gd: -------------------------------------------------------------------------------- 1 | extends SteeringBehavior2D 2 | class_name ArriveBehavior2D 3 | # ArriveBehavior2D aims to arrive at where the target is. It follows the 4 | # target and slows down as it gets close to it, within the deceleration_radius. 5 | 6 | # # Properties: 7 | 8 | # # - `arrival_tolerance` is the distance away from the target that will be good enough to stop moving. 9 | # - `deceleration_radius` is the distance away from the target that the AI should begin weighing down its acceleration 10 | # and slow down. 11 | # - `time_to_target` is a fixed time scale value of time by which it should weigh its velocity by to arrive smoothly. 12 | # This value should be small and defaults to 0.1. 13 | 14 | # 15 | 16 | export var arrival_tolerance := 5.0 17 | export var deceleration_radius := 200.0 18 | export var time_to_target := 0.1 19 | 20 | var target: Node2D 21 | 22 | 23 | # Returns the steering motion filled with a linear acceleration amount that will take it towards the target with a 24 | # smooth deceleration, or zero if it is already inside of an arrival threshold. 25 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 26 | if not target: 27 | return steering.reset_values() 28 | 29 | var actor = get_actor() 30 | var to_target: Vector2 = target.position - actor.position 31 | var distance := to_target.length() 32 | 33 | if distance <= arrival_tolerance: 34 | steering.reset_values() 35 | else: 36 | var target_speed := controller.max_speed 37 | if distance <= deceleration_radius: 38 | target_speed *= distance / deceleration_radius 39 | 40 | var target_velocity := to_target.normalized() * target_speed 41 | target_velocity = (target_velocity - controller.velocity) * (1.0 / time_to_target) 42 | 43 | steering.velocity = target_velocity.clamped(controller.max_acceleration) 44 | steering.angular_velocity = 0.0 45 | 46 | return steering 47 | -------------------------------------------------------------------------------- /game/src/AI/Steering/Behaviors/Individual/EvadeBehavior2D.gd: -------------------------------------------------------------------------------- 1 | extends InterceptBehavior2D 2 | class_name EvadeBehavior2D 3 | # EvadeBehavior2D is the opposite of Intercept. It moves away from the target's projected position. 4 | # See `InterceptBehavior2D` for more information. 5 | 6 | # 7 | # Returns a steering motion filled with a linear acceleration amount that will move it away from the 8 | # point where the target will be in `max_prediction_time`. 9 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 10 | ._calculate_steering_internal(steering) 11 | steering.velocity *= -1 12 | return steering 13 | -------------------------------------------------------------------------------- /game/src/AI/Steering/Behaviors/Individual/FleeBehavior2D.gd: -------------------------------------------------------------------------------- 1 | extends SeekBehavior2D 2 | class_name FleeBehavior2D 3 | # FleeBehavior2D is the opposite of Seek. It constantly accelerates away from the target. 4 | 5 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 6 | ._calculate_steering_internal(steering) 7 | steering.velocity *= -1 8 | return steering 9 | -------------------------------------------------------------------------------- /game/src/AI/Steering/Behaviors/Individual/FollowHeatmapBehavior2D.gd: -------------------------------------------------------------------------------- 1 | extends SteeringBehavior2D 2 | class_name FollowHeatmapBehavior2D 3 | # A 2D steering behavior that expects a heatmap to be somewhere in the scene. 4 | # It uses that to figure out where it should be going - from large values in its cell 5 | # towards smaller values, with 0 being the goal. 6 | 7 | 8 | var _heatmap 9 | 10 | var _last_point_index: int = INF 11 | var _last_velocity := Vector2(0, 0) 12 | 13 | 14 | func _ready() -> void: 15 | _heatmap = get_tree().root.find_node("Heatmap", true, false) 16 | assert(_heatmap) 17 | 18 | 19 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 20 | var actor = get_actor() 21 | var point_index: int = _heatmap.calculate_point_index_for_world_position(actor.global_position) 22 | if point_index != _last_point_index: 23 | _last_point_index = point_index 24 | var to_target: Vector2 = _heatmap.best_direction_for(actor.global_position, true) 25 | 26 | steering.velocity = to_target * controller.max_acceleration 27 | _last_velocity = steering.velocity 28 | else: 29 | steering.velocity = _last_velocity 30 | 31 | steering.angular_velocity = 0 32 | return steering 33 | -------------------------------------------------------------------------------- /game/src/AI/Steering/Behaviors/Individual/InterceptBehavior2D.gd: -------------------------------------------------------------------------------- 1 | extends SteeringBehavior2D 2 | class_name InterceptBehavior2D 3 | # Attempts to predict where the target is headed, and points its acceleration towards that point. 4 | 5 | # # Properties: 6 | 7 | # # - `max_prediction_time` is how far into the future in fixed time scale the AI will use to predict 8 | # where the target is headed 9 | 10 | 11 | export var max_prediction_time := 0.0 12 | 13 | var target: Node2D 14 | 15 | 16 | # Returns the steering motion filled with a linear acceleration amount that will send it on an 17 | # intercept course with wherever the target is headed so that its arrival will cross with the 18 | # target's. 19 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 20 | if not target: 21 | return steering.reset_values() 22 | 23 | var target_position := target.position 24 | var distance2 := (target_position - get_actor().position).length_squared() 25 | var speed2 := controller.velocity.length_squared() 26 | 27 | var prediction_time := max_prediction_time 28 | 29 | if speed2 > 0: 30 | var prediction_time2 = distance2 / speed2 31 | if prediction_time2 < max_prediction_time * max_prediction_time: 32 | prediction_time = sqrt(prediction_time2) 33 | 34 | steering.velocity = target_position + (controller.target_velocity * prediction_time) 35 | steering.velocity = (steering.velocity - get_actor().position).normalized() * controller.max_acceleration 36 | steering.angular_velocity = 0 37 | 38 | return steering 39 | -------------------------------------------------------------------------------- /game/src/AI/Steering/Behaviors/Individual/SeekBehavior2D.gd: -------------------------------------------------------------------------------- 1 | extends SteeringBehavior2D 2 | class_name SeekBehavior2D 3 | # Constantly accelerates towards the target as fast as it can. 4 | 5 | 6 | var target: Node2D 7 | 8 | 9 | # Returns the steering motion filled with a linear acceleration amount that will make it go to where 10 | # the target currently is, as fast as it is allowed by the controller. 11 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 12 | if not target: 13 | return steering.reset_values() 14 | 15 | steering.velocity = (target.position - get_actor().position).normalized() * controller.max_acceleration 16 | steering.angular_velocity = 0 17 | return steering 18 | -------------------------------------------------------------------------------- /game/src/AI/Steering/Behaviors/Individual/StraightLineBehavior2D.gd: -------------------------------------------------------------------------------- 1 | extends SteeringBehavior2D 2 | class_name StraightLineBehavior2D 3 | # 2D Behavior that acts like Seek, but for a specific Vector2 position instead of an Object. 4 | 5 | export var arrival_tolerance := 5.0 6 | 7 | var target: Vector2 8 | 9 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 10 | var actor = get_actor() 11 | var to_target: Vector2 = target - actor.position 12 | var distance := to_target.length() 13 | 14 | if distance <= arrival_tolerance: 15 | steering.reset_values() 16 | else: 17 | steering.velocity = (target - get_actor().position).normalized() * controller.max_acceleration 18 | steering.angular_velocity = 0 19 | return steering 20 | -------------------------------------------------------------------------------- /game/src/AI/Steering/Behaviors/PrioritySteering2D.gd: -------------------------------------------------------------------------------- 1 | extends SteeringBehavior2D 2 | class_name PrioritySteering2D 3 | # Runs from top to bottom through its children, a collection of 4 | # SteeringBehavior2D nodes, requests each to calculate its steering, and stops 5 | # once one of them returns non-zero steering. 6 | 7 | # # PrioritySteering2D gives priority to the behaviors at the top of its child list. 8 | 9 | # # Notes 10 | # ----- 11 | # Some behaviors will always return an acceleration amount, while others may not. You 12 | # usually want to act on them. For instance, evading an obstacle to avoid impact. 13 | 14 | 15 | var last_child_index := 0 16 | 17 | 18 | # Returns the child whose motion was non-zero. Returns null if no children produced an acceleration. 19 | func get_last_selected_child() -> SteeringBehavior2D: 20 | assert(last_child_index >= 0) 21 | return get_child(last_child_index) as SteeringBehavior2D 22 | 23 | 24 | # Fills the steering motion object with the first non-zero calculation it can find. The priority goes from 25 | # top to bottom. 26 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 27 | last_child_index = -1 28 | var size := get_child_count() 29 | 30 | for i in range(0, size): 31 | var child := get_child(i) as SteeringBehavior2D 32 | if child == null: 33 | continue 34 | 35 | child.calculate_steering(steering) 36 | if !steering.is_zero(): 37 | last_child_index = i 38 | 39 | if size > 0: 40 | return steering 41 | else: 42 | return steering.reset_values() 43 | -------------------------------------------------------------------------------- /game/src/AI/Steering/SteeringBehavior2D.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | class_name SteeringBehavior2D 3 | # The base class for a behavior in 2D space. Subclasses of this class should only 4 | # override `_calculate_steering_internal`. 5 | 6 | # # Calling `calculate_steering` on a class that extends this should fill the provided `SteeringMotion2D` 7 | # with an amount of linear and angular acceleration. It is up to the caller to then use that 8 | # information to move the AI actor. 9 | 10 | 11 | var is_enabled := true 12 | onready var controller: BehaviorController2D = _find_controller(self) 13 | 14 | 15 | # Returns the motion with the linear and/or angular acceleration filled in, based on the behavior. 16 | func calculate_steering(steering: SteeringMotion2D) -> SteeringMotion2D: 17 | if not is_enabled or not controller or not controller.has_valid_agent(): 18 | return steering.reset_values() 19 | else: 20 | return _calculate_steering_internal(steering) 21 | 22 | 23 | # Returns the actor upon which the behaviors are acting, to access their position in space. 24 | func get_actor() -> Node2D: 25 | assert(controller) 26 | return controller.actor 27 | 28 | 29 | # Virtual function 30 | # Returns the motion with the linear and/or angular acceleration filled in, based on the behavior. 31 | func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D: 32 | return steering 33 | 34 | 35 | # Recursively searches its parent to find the behavior controller that will feed it information. 36 | func _find_controller(current_node: Node) -> Node: 37 | if not current_node is BehaviorController2D: 38 | return _find_controller(current_node.get_parent()) 39 | return current_node 40 | -------------------------------------------------------------------------------- /game/src/AI/Steering/SteeringMotion2D.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | class_name SteeringMotion2D 3 | # Data container for linear and angular steering velocities calculated by behaviors that extend SteeringBehavior2D. 4 | 5 | 6 | var velocity := Vector2.ZERO 7 | var angular_velocity := 0.0 8 | 9 | 10 | func reset_values() -> void: 11 | velocity = Vector2.ZERO 12 | angular_velocity = 0.0 13 | 14 | 15 | func is_zero() -> bool: 16 | return velocity == Vector2.ZERO and angular_velocity == 0.0 17 | -------------------------------------------------------------------------------- /game/src/AI/SwarmerEnemy.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | # Basic controller for a node2D - its behavior checks the heatmap and points 3 | # its directional vector towards the less heat (the 'goal') 4 | 5 | 6 | onready var behavior: FollowHeatmapBehavior2D = $BehaviorController2D/FollowHeatmapBehavior2D 7 | 8 | var _motion: SteeringMotion2D = SteeringMotion2D.new() 9 | var speed: float = 250 10 | 11 | 12 | func _physics_process(delta: float) -> void: 13 | var desired_velocity := behavior.calculate_steering(_motion) 14 | position += _motion.velocity.normalized() * speed * delta 15 | -------------------------------------------------------------------------------- /game/src/AI/SwarmerEnemy.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://src/AI/SwarmerEnemy.gd" type="Script" id=1] 4 | [ext_resource path="res://src/Player/Rectangle.gd" type="Script" id=2] 5 | [ext_resource path="res://src/AI/Steering/BehaviorController2D.gd" type="Script" id=3] 6 | [ext_resource path="res://src/AI/Steering/Behaviors/Individual/FollowHeatmapBehavior2D.gd" type="Script" id=4] 7 | 8 | [node name="SwarmerEnemy" type="Node2D"] 9 | script = ExtResource( 1 ) 10 | 11 | [node name="Body" type="Node2D" parent="."] 12 | script = ExtResource( 2 ) 13 | size = Vector2( 24, 24 ) 14 | outline = Vector2( 6, 6 ) 15 | color_fill = Color( 0.580392, 0.294118, 0.737255, 1 ) 16 | color_outline = Color( 0.27451, 0.12549, 0.807843, 1 ) 17 | 18 | [node name="BehaviorController2D" type="Node" parent="."] 19 | script = ExtResource( 3 ) 20 | actor_path = NodePath("..") 21 | 22 | [node name="FollowHeatmapBehavior2D" type="Node" parent="BehaviorController2D"] 23 | script = ExtResource( 4 ) 24 | -------------------------------------------------------------------------------- /game/src/AI/SwarmerSpawner.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | # Spawns a number of swarmer enemies in the scene, who use the heatmap behavior 3 | # to chase the player for economical pathfinding. 4 | 5 | 6 | export var spawner_count := 50 7 | export var spawn_per_frame := 10 8 | export var swarmer: PackedScene 9 | export var spawn_radius := 200 10 | export var minimum_speed: float = 200 11 | export var maximum_speed: float = 300 12 | 13 | 14 | func _ready(): 15 | randomize() 16 | var r_squared := spawn_radius*spawn_radius 17 | 18 | for i in range(spawner_count): 19 | if i % spawn_per_frame: 20 | yield(get_tree(), "idle_frame") 21 | var x := rand_range(-spawn_radius, spawn_radius) 22 | var y := rand_range(-1, 1) * sqrt(r_squared-x*x) 23 | 24 | var instance := swarmer.instance() 25 | instance.set_name("Swarmer%s"% i) 26 | add_child(instance) 27 | instance.speed = rand_range(minimum_speed, maximum_speed) 28 | instance.global_position = global_position + Vector2(x,y) 29 | -------------------------------------------------------------------------------- /game/src/AI/SwarmerSpawner.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://src/AI/SwarmerSpawner.gd" type="Script" id=1] 4 | [ext_resource path="res://src/AI/SwarmerEnemy.tscn" type="PackedScene" id=2] 5 | 6 | [node name="SwarmerSpawner" type="Node2D"] 7 | script = ExtResource( 1 ) 8 | swarmer = ExtResource( 2 ) 9 | -------------------------------------------------------------------------------- /game/src/Autoload/DrawingUtils.gd: -------------------------------------------------------------------------------- 1 | # Functions to draw shapes unavailable in CanvasItem 2 | extends Node2D 3 | class_name DrawingUtils 4 | 5 | const DEFAULT_POINTS_COUNT := 32 6 | 7 | const COLOR_BLUE_LIGHT := Color("09a6ca") 8 | const COLOR_BLUE_DEEP := Color("0046ff") 9 | 10 | const COLOR_SUCCESS := Color("2cb638") 11 | const COLOR_WARNING := Color("ff9b00") 12 | const COLOR_ERROR := Color("ff004e") 13 | 14 | 15 | static func draw_circle_outline(obj: CanvasItem=null, position:=Vector2.ZERO, radius:float=30.0, color:=Color(), thickness:=1.0) -> void: 16 | var points_array := PoolVector2Array() 17 | for i in range(DEFAULT_POINTS_COUNT + 1): 18 | var angle := 2 * PI * i / DEFAULT_POINTS_COUNT 19 | var point := position + Vector2(cos(angle) * radius, sin(angle) * radius) 20 | points_array.append(point) 21 | obj.draw_polyline(points_array, color, thickness, true) 22 | 23 | 24 | static func draw_triangle(obj: CanvasItem=null, center:Vector2=Vector2.ZERO, angle:float=0.0, radius:float=10.0, color:=Color.white) -> void: 25 | var points := PoolVector2Array() 26 | var colors := PoolColorArray([color]) 27 | for i in range(3): 28 | var angle_point := angle + i * 2.0 * PI / 3.0 + PI 29 | var offset := Vector2(radius * cos(angle_point), radius * sin(angle_point)) 30 | var point := center + offset 31 | points.append(point) 32 | obj.draw_polygon(points, colors) 33 | -------------------------------------------------------------------------------- /game/src/Autoload/Events.gd: -------------------------------------------------------------------------------- 1 | # warning-ignore:unused_signal 2 | extends Node 3 | 4 | signal player_moved(player) 5 | signal checkpoint_visited(checkpoint_name) 6 | -------------------------------------------------------------------------------- /game/src/Autoload/LevelLoader.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | # Loads and unloads levels 3 | 4 | var _game: Node = null 5 | var _player: Player = null 6 | var _level: Node2D = null 7 | 8 | onready var scene_tree := get_tree() 9 | 10 | 11 | func setup(game: Node, player: Player, Level: PackedScene) -> void: 12 | _game = game 13 | _player = player 14 | trigger(Level) 15 | 16 | 17 | func trigger(NewLevel: PackedScene, portal_name: String = "") -> void: 18 | if _level: 19 | scene_tree.paused = true 20 | _game.transition.fade_to_black() 21 | yield(_game.transition, "faded_to_black") 22 | _level.queue_free() 23 | yield(_level, "tree_exited") 24 | 25 | _game.remove_child(_player) 26 | _level = NewLevel.instance() 27 | 28 | var player_position_node: Node2D = ( 29 | _level.get_node("Checkpoints").get_child(0) 30 | if portal_name.empty() 31 | else _level.get_node("Portals/%s" % portal_name) 32 | ) 33 | _player.global_position = player_position_node.global_position 34 | _player.has_teleported = not portal_name.empty() 35 | 36 | for checkpoint_name in _game.visited_checkpoints.get(_level.name, []): 37 | var checkpoint: Area2D = _level.get_node("Checkpoints/%s" % checkpoint_name) 38 | checkpoint.is_visited = true 39 | 40 | _game.level = _level 41 | _game.add_child(_level) 42 | _game.add_child(_player) 43 | 44 | _game.transition.fade_back_in() 45 | 46 | scene_tree.paused = false 47 | -------------------------------------------------------------------------------- /game/src/Autoload/Settings.gd: -------------------------------------------------------------------------------- 1 | # Gives access to game settings 2 | extends Node 3 | 4 | signal controls_changed() 5 | 6 | 7 | enum { KBD_MOUSE, GAMEPAD } 8 | var controls := KBD_MOUSE setget set_controls 9 | 10 | enum AimStick { LEFT=0, RIGHT=1 } 11 | const JOYSTICK_AIM_INPUTS := { 12 | AimStick.LEFT: { 13 | 'aim_left': { 14 | axis=JOY_AXIS_0, 15 | value=-1.0, 16 | }, 17 | 'aim_right': { 18 | axis=JOY_AXIS_0, 19 | value=-1.0, 20 | }, 21 | 'aim_up': { 22 | axis=JOY_AXIS_1, 23 | value=-1.0, 24 | }, 25 | 'aim_down': { 26 | axis=JOY_AXIS_1, 27 | value=1.0, 28 | }, 29 | }, 30 | AimStick.RIGHT: { 31 | 'aim_left': { 32 | axis=JOY_AXIS_2, 33 | value=-1.0, 34 | }, 35 | 'aim_right': { 36 | axis=JOY_AXIS_2, 37 | value=-1.0, 38 | }, 39 | 'aim_up': { 40 | axis=JOY_AXIS_3, 41 | value=-1.0, 42 | }, 43 | 'aim_down': { 44 | axis=JOY_AXIS_3, 45 | value=1.0, 46 | }, 47 | }, 48 | } 49 | var aim_stick: int = 0 setget set_aim_stick 50 | 51 | 52 | func _input(event: InputEvent) -> void: 53 | if event is InputEventJoypadButton or event is InputEventJoypadMotion: 54 | if controls == KBD_MOUSE: 55 | self.controls = GAMEPAD 56 | elif event is InputEventMouse and controls == GAMEPAD: 57 | self.controls = KBD_MOUSE 58 | 59 | 60 | func set_controls(value: int) -> void: 61 | controls = value 62 | emit_signal("controls_changed") 63 | 64 | 65 | # Switch between using the gamepad's left and right joystick 66 | # to aim the hook. 67 | # Replaces the Input map actions 68 | func set_aim_stick(value: int) -> void: 69 | assert(value in [AimStick.LEFT, AimStick.RIGHT]) 70 | var setting: int = ProjectSettings.get_setting('debug/testing/controls/aim_stick') 71 | if value == setting: 72 | return 73 | 74 | aim_stick = value 75 | ProjectSettings.set_setting('debug/testing/controls/aim_stick', value) 76 | 77 | var action_suffixes := ['left', 'right', 'up', 'down'] 78 | for suffix in action_suffixes: 79 | var action_name: String = 'aim_' + suffix 80 | InputMap.action_erase_events(action_name) 81 | var event := get_joypad_motion_event(action_name, aim_stick) 82 | InputMap.action_add_event(action_name, event) 83 | print(action_name) 84 | 85 | 86 | # Returns a new InputEventJoypadMotion event for aim_* input events, 87 | # using JOYSTICK_AIM_INPUTS for the base data 88 | # Use AimStick.* for the stick argument 89 | func get_joypad_motion_event(action: String, stick: int) -> InputEventJoypadMotion: 90 | var event := InputEventJoypadMotion.new() 91 | var action_data: Dictionary = JOYSTICK_AIM_INPUTS[stick][action] 92 | action_data.deadzone = 0.0 93 | event.axis = action_data.axis 94 | event.axis_value = action_data.value 95 | return event 96 | -------------------------------------------------------------------------------- /game/src/Autoload/Steering.gd: -------------------------------------------------------------------------------- 1 | # Utility functions to calculate steering motion 2 | # To use as an autoloaded Node 3 | extends Node 4 | 5 | const DEFAULT_MASS := 2.0 6 | const DEFAULT_SLOW_RADIUS := 200.0 7 | const DEFAULT_MAX_SPEED := 400.0 8 | 9 | 10 | static func follow( 11 | velocity: Vector2, 12 | global_position: Vector2, 13 | target_position: Vector2, 14 | delta: float, 15 | max_speed := DEFAULT_MAX_SPEED, 16 | mass := DEFAULT_MASS 17 | ) -> Vector2: 18 | # Calculates and returns a velocity steering towards target_position 19 | var desired_velocity: Vector2 = (target_position - global_position).normalized() * max_speed 20 | var steering: Vector2 = (desired_velocity - velocity) / mass 21 | return velocity + steering * delta * 60 22 | 23 | 24 | static func arrive_to( 25 | velocity: Vector2, 26 | global_position: Vector2, 27 | target_position: Vector2, 28 | delta: float, 29 | max_speed := DEFAULT_MAX_SPEED, 30 | slow_radius := DEFAULT_SLOW_RADIUS, 31 | mass := DEFAULT_MASS 32 | ) -> Vector2: 33 | # Calculates and returns a new velocity with the arrive steering behavior 34 | var to_target := global_position.distance_to(target_position) 35 | var desired_velocity := (target_position - global_position).normalized() * max_speed 36 | if to_target < slow_radius: 37 | desired_velocity *= (to_target / slow_radius) * .75 + .25 38 | var steering: Vector2 = (desired_velocity - velocity) / mass 39 | return velocity + steering * delta * 60 40 | -------------------------------------------------------------------------------- /game/src/Autoload/Utils.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | # Globally accessible utils functionality 3 | 4 | # Returns the direction in which the player is aiming with the stick 5 | static func get_aim_joystick_direction() -> Vector2: 6 | return get_aim_joystick_strength().normalized() 7 | 8 | 9 | # Returns the strength of the XY input with the joystick used for aiming, 10 | # each axis being a value between 0 and 1 11 | # Adds a circular deadzone, as Godot's built-in system is per-axis, 12 | # creating a rectangular deadzone on the joystick 13 | static func get_aim_joystick_strength() -> Vector2: 14 | var deadzone_radius := 0.5 15 | var input_strength := Vector2( 16 | Input.get_action_strength("aim_right") - Input.get_action_strength("aim_left"), 17 | Input.get_action_strength("aim_down") - Input.get_action_strength("aim_up") 18 | ) 19 | return input_strength if input_strength.length() > deadzone_radius else Vector2.ZERO 20 | 21 | # Checks if two numbers are approximately equal 22 | static func is_equal_approx(a: float, b: float, cmp_epsilon: float = 1e-5) -> bool: 23 | var tolerance := cmp_epsilon * abs(a) 24 | if tolerance < cmp_epsilon: 25 | tolerance = cmp_epsilon 26 | return abs(a - b) < tolerance 27 | -------------------------------------------------------------------------------- /game/src/Combat/DamageSource.gd: -------------------------------------------------------------------------------- 1 | # Represents a weapon or any entity that can damage others Currently 2 | # barebones, but we can extend this class to add an element like fire, ice, etc. 3 | # Or a status modifier like poison, the chances to apply a given status effect, 4 | # and other metadata that we can later transform into an actual attack. See Hit.gd 5 | extends Area2D 6 | 7 | class_name DamageSource 8 | 9 | export var damage := 1 10 | export var is_instakill := false 11 | -------------------------------------------------------------------------------- /game/src/Combat/DamageSource.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://src/Combat/DamageSource.gd" type="Script" id=1] 4 | 5 | [sub_resource type="CircleShape2D" id=1] 6 | radius = 30.0 7 | 8 | [node name="DamageSource" type="Area2D"] 9 | collision_layer = 1024 10 | collision_mask = 0 11 | script = ExtResource( 1 ) 12 | 13 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 14 | shape = SubResource( 1 ) 15 | -------------------------------------------------------------------------------- /game/src/Combat/DummyEnemy.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | # Takes damage from a damage source, i.e. Hit, dying if health is depleated 4 | 5 | onready var stats: Stats = $Stats as Stats 6 | 7 | func _ready() -> void: 8 | stats.connect("health_depleted", self, "_on_Stats_health_depleated") 9 | 10 | 11 | func take_damage(source: Hit) -> void: 12 | stats.take_damage(source) 13 | 14 | 15 | func _on_Stats_health_depleated(): 16 | queue_free() 17 | -------------------------------------------------------------------------------- /game/src/Combat/DummyEnemy.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=2] 2 | 3 | [ext_resource path="res://src/Combat/DummyEnemy.gd" type="Script" id=1] 4 | [ext_resource path="res://src/Combat/HitBox/HitBox.tscn" type="PackedScene" id=2] 5 | [ext_resource path="res://src/Combat/Stats.tscn" type="PackedScene" id=3] 6 | [ext_resource path="res://src/Objects/HookTarget.tscn" type="PackedScene" id=4] 7 | [ext_resource path="res://src/Combat/DamageSource.tscn" type="PackedScene" id=5] 8 | 9 | 10 | [sub_resource type="RectangleShape2D" id=1] 11 | extents = Vector2( 40, 70 ) 12 | 13 | [node name="DummyEnemy" type="Node2D"] 14 | script = ExtResource( 1 ) 15 | 16 | [node name="Polygon2D" type="Polygon2D" parent="."] 17 | z_index = -1 18 | color = Color( 1, 0.223529, 0.223529, 1 ) 19 | polygon = PoolVector2Array( -40, 0, 40, 0, 40, -140, -40, -140 ) 20 | 21 | [node name="HitBox" parent="." instance=ExtResource( 2 )] 22 | 23 | [node name="CollisionShape2D" parent="HitBox" index="0"] 24 | position = Vector2( 0, -70 ) 25 | shape = SubResource( 1 ) 26 | 27 | [node name="Stats" parent="." instance=ExtResource( 3 )] 28 | max_health = 15 29 | 30 | [node name="HookTarget" parent="." groups=[ 31 | "enemy", 32 | ] instance=ExtResource( 4 )] 33 | position = Vector2( 0, -70 ) 34 | 35 | [node name="DamageSource" parent="." instance=ExtResource( 5 )] 36 | 37 | [editable path="HitBox"] 38 | -------------------------------------------------------------------------------- /game/src/Combat/Hit.gd: -------------------------------------------------------------------------------- 1 | # Represents an attack or a hit 2 | # Created from a DamageSource. 3 | # See DamageSource for more information. 4 | class_name Hit 5 | 6 | var damage := 0 7 | var is_instakill := false 8 | 9 | func _init(source: DamageSource) -> void: 10 | damage = source.damage 11 | is_instakill = source.is_instakill 12 | -------------------------------------------------------------------------------- /game/src/Combat/HitBox/HitBox.gd: -------------------------------------------------------------------------------- 1 | extends Area2D 2 | 3 | onready var collider: CollisionShape2D = $CollisionShape2D 4 | 5 | var is_active := true setget set_is_active 6 | 7 | 8 | func _ready() -> void: 9 | # warning-ignore:return_value_discarded 10 | connect("area_entered", self, "_on_area_entered") 11 | 12 | 13 | func _on_area_entered(damage_source: Area2D) -> void: 14 | var hit := Hit.new(damage_source) 15 | owner.take_damage(hit) 16 | 17 | 18 | func set_is_active(value: bool) -> void: 19 | is_active = value 20 | collider.disabled = not value 21 | -------------------------------------------------------------------------------- /game/src/Combat/HitBox/HitBox.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://src/Combat/HitBox/HitBox.gd" type="Script" id=1] 4 | [ext_resource path="res://src/Combat/HitBox/hitbox_default.tres" type="Shape2D" id=2] 5 | 6 | [node name="HitBox" type="Area2D"] 7 | monitorable = false 8 | collision_layer = 2048 9 | collision_mask = 1024 10 | script = ExtResource( 1 ) 11 | 12 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 13 | shape = ExtResource( 2 ) 14 | -------------------------------------------------------------------------------- /game/src/Combat/HitBox/hitbox_default.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="CircleShape2D" format=2] 2 | 3 | [resource] 4 | radius = 28.0 5 | -------------------------------------------------------------------------------- /game/src/Combat/Stats.gd: -------------------------------------------------------------------------------- 1 | # Stats for the player or the monsters, to manage health, etc. 2 | # Attach an instance of Stats to any object to give it health and stats. 3 | extends Node 4 | 5 | class_name Stats 6 | 7 | signal health_changed(old_value, new_value) 8 | signal health_depleted() 9 | signal damage_taken() 10 | 11 | var modifiers = {} 12 | 13 | var invulnerable := false 14 | 15 | export var max_health := 1.0 setget set_max_health 16 | var health := max_health 17 | 18 | export var attack: int = 1 19 | export var armor: int = 1 20 | 21 | 22 | func _ready() -> void: 23 | health = max_health 24 | 25 | 26 | func take_damage(hit: Hit) -> void: 27 | if invulnerable: 28 | return 29 | 30 | if hit.is_instakill: 31 | emit_signal("health_depleted") 32 | return 33 | 34 | var old_health = health 35 | health -= hit.damage 36 | emit_signal("damage_taken") 37 | health = max(0, health) 38 | emit_signal("health_changed", health, old_health) 39 | if health == 0: 40 | emit_signal("health_depleted") 41 | 42 | 43 | func heal(amount: float) -> void: 44 | var old_health = health 45 | health = min(health + amount, max_health) 46 | emit_signal("health_changed", health, old_health) 47 | 48 | 49 | func set_max_health(value: float) -> void: 50 | if value == null: 51 | return 52 | max_health = max(1, value) 53 | 54 | 55 | func add_modifier(id: int, modifier) -> void: 56 | modifiers[id] = modifier 57 | 58 | 59 | func remove_modifier(id: int) -> void: 60 | modifiers.erase(id) 61 | 62 | 63 | func set_invulnerable_for_seconds(time: float) -> void: 64 | invulnerable = true 65 | 66 | var timer := get_tree().create_timer(time) 67 | yield(timer, "timeout") 68 | 69 | invulnerable = false 70 | -------------------------------------------------------------------------------- /game/src/Combat/Stats.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://src/Combat/Stats.gd" type="Script" id=1] 4 | 5 | [node name="Stats" type="Node"] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /game/src/Main/Game.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | 4 | onready var transition: ColorRect = $UI/Transition 5 | 6 | export(PackedScene) var StartLevel := preload("res://src/Levels/Level1.tscn") 7 | 8 | var visited_checkpoints := {} 9 | var level: Node2D = null 10 | 11 | 12 | func _ready() -> void: 13 | LevelLoader.setup(self, $Player, StartLevel) 14 | # warning-ignore:return_value_discarded 15 | Events.connect("checkpoint_visited", self, "_on_Events_checkpoint_visited") 16 | 17 | 18 | func _on_Events_checkpoint_visited(checkpoint_name: String) -> void: 19 | visited_checkpoints[level.name] = visited_checkpoints.get(level.name, []) 20 | visited_checkpoints[level.name].push_back(checkpoint_name) 21 | 22 | 23 | func _unhandled_input(event: InputEvent) -> void: 24 | if event.is_action_pressed("restart"): 25 | # warning-ignore:return_value_discarded 26 | get_tree().reload_current_scene() 27 | elif event.is_action_pressed("DEBUG_die"): 28 | var last_checkpoint_name: String = visited_checkpoints[level.name].back() 29 | var last_checkpoint: Area2D = level.get_node("Checkpoints/" + last_checkpoint_name) 30 | $Player.state_machine.transition_to("Die", {last_checkpoint = last_checkpoint}) 31 | elif event.is_action_pressed("toggle_full_screen"): 32 | OS.window_fullscreen = not OS.window_fullscreen 33 | -------------------------------------------------------------------------------- /game/src/Main/Game.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=2] 2 | 3 | [ext_resource path="res://src/Main/Game.gd" type="Script" id=1] 4 | [ext_resource path="res://assets/theme/gdquest.theme" type="Theme" id=2] 5 | [ext_resource path="res://src/UI/debug/DebugDock.gd" type="Script" id=3] 6 | [ext_resource path="res://src/UI/debug/DebugPanel.tscn" type="PackedScene" id=4] 7 | [ext_resource path="res://src/UI/LoadingTransition.tscn" type="PackedScene" id=5] 8 | [ext_resource path="res://src/Player/Player.tscn" type="PackedScene" id=8] 9 | [ext_resource path="res://src/Levels/SkyParallaxBackground.tscn" type="PackedScene" id=9] 10 | 11 | [node name="Game" type="Node"] 12 | script = ExtResource( 1 ) 13 | 14 | [node name="UI" type="CanvasLayer" parent="."] 15 | layer = 100 16 | 17 | [node name="DebugDock" type="MarginContainer" parent="UI"] 18 | anchor_bottom = 1.0 19 | margin_right = 440.0 20 | mouse_filter = 2 21 | theme = ExtResource( 2 ) 22 | script = ExtResource( 3 ) 23 | 24 | [node name="Column" type="VBoxContainer" parent="UI/DebugDock"] 25 | margin_left = 16.0 26 | margin_top = 16.0 27 | margin_right = 424.0 28 | margin_bottom = 1064.0 29 | mouse_filter = 2 30 | 31 | [node name="DebugPanel" parent="UI/DebugDock/Column" instance=ExtResource( 4 )] 32 | anchor_right = 0.0 33 | anchor_bottom = 0.0 34 | margin_right = 408.0 35 | margin_bottom = 196.0 36 | mouse_filter = 2 37 | reference_path = NodePath("../../../../../Game/Player/StateMachine/Move") 38 | properties = PoolStringArray( "velocity", "acceleration", "max_speed" ) 39 | 40 | [node name="DebugPanel2" parent="UI/DebugDock/Column" instance=ExtResource( 4 )] 41 | anchor_right = 0.0 42 | anchor_bottom = 0.0 43 | margin_top = 204.0 44 | margin_right = 408.0 45 | margin_bottom = 332.0 46 | mouse_filter = 2 47 | reference_path = NodePath("../../../../../Game/Player/StateMachine") 48 | properties = PoolStringArray( "_state_name" ) 49 | 50 | [node name="Transition" parent="UI" instance=ExtResource( 5 )] 51 | 52 | [node name="Player" parent="." instance=ExtResource( 8 )] 53 | 54 | [node name="SkyParallaxBackground" parent="." instance=ExtResource( 9 )] 55 | 56 | [editable path="Player"] 57 | -------------------------------------------------------------------------------- /game/src/Main/StateMachine/State.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | class_name State 3 | # State interface to use in Hierarchical State Machines. 4 | # The lowest leaf tries to handle callbacks, and if it can't, it delegates the work to its parent. 5 | # It's up to the user to call the parent state's functions, e.g. `_parent.physics_process(delta)` 6 | # Use State as a child of a StateMachine node. 7 | 8 | 9 | onready var _state_machine := _get_state_machine(self) 10 | var _parent: State = null 11 | 12 | 13 | func _ready() -> void: 14 | yield(owner, "ready") 15 | _parent = get_parent() as State 16 | 17 | 18 | func unhandled_input(_event: InputEvent) -> void: 19 | pass 20 | 21 | 22 | func physics_process(_delta: float) -> void: 23 | pass 24 | 25 | 26 | func enter(_msg: Dictionary = {}) -> void: 27 | pass 28 | 29 | 30 | func exit() -> void: 31 | pass 32 | 33 | 34 | func _get_state_machine(node: Node) -> Node: 35 | if node != null and not node.is_in_group("state_machine"): 36 | return _get_state_machine(node.get_parent()) 37 | return node 38 | -------------------------------------------------------------------------------- /game/src/Main/StateMachine/StateMachine.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | class_name StateMachine 3 | # Generic State Machine. Initializes states and delegates engine callbacks 4 | # (_physics_process, _unhandled_input) to the active state. 5 | 6 | 7 | export var initial_state := NodePath() 8 | 9 | onready var state: State = get_node(initial_state) setget set_state 10 | onready var _state_name := state.name 11 | 12 | 13 | func _init() -> void: 14 | add_to_group("state_machine") 15 | 16 | 17 | func _ready() -> void: 18 | yield(owner, "ready") 19 | state.enter() 20 | 21 | 22 | func _unhandled_input(event: InputEvent) -> void: 23 | state.unhandled_input(event) 24 | 25 | 26 | func _physics_process(delta: float) -> void: 27 | state.physics_process(delta) 28 | 29 | 30 | func transition_to(target_state_path: String, msg: Dictionary = {}) -> void: 31 | if not has_node(target_state_path): 32 | return 33 | 34 | var target_state := get_node(target_state_path) 35 | 36 | state.exit() 37 | self.state = target_state 38 | state.enter(msg) 39 | 40 | 41 | func set_state(value: State) -> void: 42 | state = value 43 | _state_name = state.name 44 | -------------------------------------------------------------------------------- /game/src/Native/Heatmap/.gdignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/game/src/Native/Heatmap/.gdignore -------------------------------------------------------------------------------- /game/src/Native/Heatmap/Heatmap.h: -------------------------------------------------------------------------------- 1 | // Heatmap 2 | // Francois "@Razoric480" Belair 3 | // 4 | // GDNative Godot Class that uses a floodfill algorithm. Radiating out from the player position, it covers the whole tilemap. 5 | // Each step removed adds one to the layer count. This can then be used by agents for quick 6 | // lookup based pathfinding, instead of calculating A-star paths or other, potentially more expensive pathfinding routines. 7 | 8 | #ifndef HEATMAP_H 9 | #define HEATMAP_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace godot { 17 | //Forward declarations 18 | class TileMap; 19 | 20 | // HeatCell. Simple structure used by the `refresh_cells_heat` member function to contain both 21 | // position and current layer in the flood fill search. 22 | struct HeatCell { 23 | HeatCell(Vector2 t_position, int t_layer) { 24 | position = t_position; 25 | layer = t_layer; 26 | } 27 | Vector2 position; 28 | int layer; 29 | 30 | bool operator ==(HeatCell const& other) { 31 | return other.layer == layer && other.position == position; 32 | } 33 | 34 | bool operator !=(HeatCell const& other) { 35 | return other.layer != layer || other.position != position; 36 | } 37 | }; 38 | 39 | class Heatmap : public Node2D { 40 | GODOT_CLASS(Heatmap, Node2D) 41 | 42 | public: 43 | Heatmap(); 44 | ~Heatmap(); 45 | 46 | static void _register_methods(); 47 | 48 | void _init(); 49 | void _ready(); 50 | void _draw(); 51 | void _process(float delta); 52 | 53 | Vector2 best_direction_for(Vector2 t_location, bool t_is_world_location); 54 | unsigned int calculate_point_index(Vector2 t_point); 55 | unsigned int calculate_point_index_for_world_position(Vector2 t_world_position); 56 | 57 | private: 58 | void find_all_obstacles(); 59 | Vector2 refresh_cells_heat(Vector2 t_cell_position); 60 | bool is_out_of_bounds(Vector2 t_position); 61 | void on_Events_player_moved(Node2D* t_player); 62 | void thread_done(Vector2 t_cell_position); 63 | 64 | private: 65 | NodePath m_pathfinding_tilemap; 66 | bool m_draw_debug; 67 | 68 | TileMap* m_grid; 69 | Rect2 m_map_limits; 70 | float m_x_min; 71 | float m_x_max; 72 | float m_y_min; 73 | float m_y_max; 74 | int m_max_heat; 75 | int m_max_heat_cache; 76 | bool m_updating; 77 | 78 | std::vector m_cells_heat; 79 | std::vector m_cells_heat_cache; 80 | std::vector m_obstacles; 81 | Vector2 m_last_player_cell_position; 82 | std::future m_future; 83 | }; 84 | } 85 | 86 | #endif /* HEATMAP_H */ 87 | -------------------------------------------------------------------------------- /game/src/Native/Heatmap/Heatmap.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29230.47 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Heatmap", "Heatmap.vcxproj", "{95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}.Debug|x64.ActiveCfg = Debug|x64 15 | {95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}.Debug|x64.Build.0 = Debug|x64 16 | {95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}.Release|x64.ActiveCfg = Release|x64 17 | {95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {6B60736E-491D-46DB-B927-593F9A8CEDF2} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /game/src/Native/Heatmap/Heatmap.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | 23 | 24 | Source Files 25 | 26 | 27 | Source Files 28 | 29 | 30 | -------------------------------------------------------------------------------- /game/src/Native/Heatmap/gdlibrary.cpp: -------------------------------------------------------------------------------- 1 | #include "Heatmap.h" 2 | 3 | extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) { 4 | godot::Godot::gdnative_init(o); 5 | } 6 | 7 | extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) { 8 | godot::Godot::gdnative_terminate(o); 9 | } 10 | 11 | extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) { 12 | godot::Godot::nativescript_init(handle); 13 | 14 | godot::register_class(); 15 | } -------------------------------------------------------------------------------- /game/src/Objects/Checkpoint.gd: -------------------------------------------------------------------------------- 1 | extends Area2D 2 | 3 | 4 | onready var collision_shape: CollisionShape2D = $CollisionShape2D 5 | 6 | const COLOR_INACTIVE := Color(1, 1, 1) 7 | 8 | var is_visited := false setget set_is_visited 9 | 10 | 11 | func _ready() -> void: 12 | connect("body_entered", self, "_on_body_entered") 13 | 14 | 15 | func _on_body_entered(body: PhysicsBody2D) -> void: 16 | if not body is Player: 17 | return 18 | 19 | self.is_visited = true 20 | 21 | disconnect("body_entered", self, "_on_body_entered") 22 | Events.emit_signal("checkpoint_visited", self.name) 23 | 24 | 25 | func set_is_visited(value: bool) -> void: 26 | is_visited = value 27 | if is_visited: 28 | modulate = COLOR_INACTIVE 29 | -------------------------------------------------------------------------------- /game/src/Objects/Checkpoint.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=11 format=2] 2 | 3 | [ext_resource path="res://src/Objects/Checkpoint.gd" type="Script" id=1] 4 | [ext_resource path="res://assets/environment/interactive/checkpoint01_bottomWingsB.png" type="Texture" id=2] 5 | [ext_resource path="res://assets/environment/interactive/checkpoint02_bottomWingsT.png" type="Texture" id=3] 6 | [ext_resource path="res://assets/environment/interactive/checkpoint03_bottom.png" type="Texture" id=4] 7 | [ext_resource path="res://assets/environment/interactive/checkpoint04_gradient.png" type="Texture" id=5] 8 | [ext_resource path="res://assets/environment/interactive/checkpoint05_headWingsB.png" type="Texture" id=6] 9 | [ext_resource path="res://assets/environment/interactive/checkpoint06_headWingsT.png" type="Texture" id=7] 10 | [ext_resource path="res://assets/environment/interactive/checkpoint07_head.png" type="Texture" id=8] 11 | [ext_resource path="res://assets/environment/interactive/checkpoint08_crystal.png" type="Texture" id=9] 12 | 13 | [sub_resource type="CapsuleShape2D" id=1] 14 | radius = 48.0 15 | height = 100.0 16 | 17 | [node name="Checkpoint" type="Area2D"] 18 | modulate = Color( 0.588235, 0.588235, 0.588235, 1 ) 19 | collision_layer = 0 20 | script = ExtResource( 1 ) 21 | 22 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 23 | position = Vector2( 0, -100 ) 24 | shape = SubResource( 1 ) 25 | 26 | [node name="BottomWingsB" type="Sprite" parent="CollisionShape2D"] 27 | show_behind_parent = true 28 | position = Vector2( 0, 14.5 ) 29 | texture = ExtResource( 2 ) 30 | 31 | [node name="BottomWingsT" type="Sprite" parent="CollisionShape2D"] 32 | show_behind_parent = true 33 | position = Vector2( 0, 14 ) 34 | texture = ExtResource( 3 ) 35 | 36 | [node name="Bottom" type="Sprite" parent="CollisionShape2D"] 37 | show_behind_parent = true 38 | position = Vector2( 0, 39 ) 39 | texture = ExtResource( 4 ) 40 | 41 | [node name="Gradient" type="Sprite" parent="CollisionShape2D"] 42 | show_behind_parent = true 43 | position = Vector2( 0, 35 ) 44 | texture = ExtResource( 5 ) 45 | 46 | [node name="HeadWingsB" type="Sprite" parent="CollisionShape2D"] 47 | show_behind_parent = true 48 | position = Vector2( 0, -96.5 ) 49 | texture = ExtResource( 6 ) 50 | 51 | [node name="HeadWingsT" type="Sprite" parent="CollisionShape2D"] 52 | show_behind_parent = true 53 | position = Vector2( 0, -96.5 ) 54 | texture = ExtResource( 7 ) 55 | 56 | [node name="Head" type="Sprite" parent="CollisionShape2D"] 57 | show_behind_parent = true 58 | position = Vector2( 0, -79.5 ) 59 | texture = ExtResource( 8 ) 60 | 61 | [node name="Crystal" type="Sprite" parent="CollisionShape2D"] 62 | show_behind_parent = true 63 | position = Vector2( 0, -30 ) 64 | texture = ExtResource( 9 ) 65 | -------------------------------------------------------------------------------- /game/src/Objects/FallLimitArea.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://src/Combat/DamageSource.tscn" type="PackedScene" id=1] 4 | 5 | [sub_resource type="LineShape2D" id=1] 6 | 7 | [node name="FallLimitArea" instance=ExtResource( 1 )] 8 | position = Vector2( 0, -2.93628 ) 9 | is_instakill = true 10 | 11 | [node name="CollisionShape2D" parent="." index="0"] 12 | shape = SubResource( 1 ) 13 | -------------------------------------------------------------------------------- /game/src/Objects/HookTarget.gd: -------------------------------------------------------------------------------- 1 | extends Area2D 2 | class_name HookTarget 3 | # Area2D the Hook can hook onto 4 | # If is_one_shot is true, the player can only hook onto the point once 5 | 6 | 7 | signal hooked_onto_from(hook_position) 8 | 9 | onready var timer: Timer = $Timer 10 | 11 | export var is_one_shot := false 12 | 13 | const COLOR_ACTIVE: Color = Color(1, 1, 1) 14 | const COLOR_INACTIVE: Color = Color(0.588235, 0.588235, 0.588235) 15 | 16 | var is_active := true setget set_is_active 17 | 18 | 19 | func _ready() -> void: 20 | # warning-ignore:return_value_discarded 21 | timer.connect("timeout", self, "_on_Timer_timeout") 22 | 23 | 24 | func _on_Timer_timeout() -> void: 25 | self.is_active = true 26 | 27 | 28 | func hooked_from(hook_position: Vector2) -> void: 29 | self.is_active = false 30 | emit_signal("hooked_onto_from", hook_position) 31 | 32 | 33 | func set_is_active(value:bool) -> void: 34 | is_active = value 35 | modulate = COLOR_ACTIVE if is_active else COLOR_INACTIVE 36 | 37 | if not is_active and not is_one_shot: 38 | timer.start() 39 | -------------------------------------------------------------------------------- /game/src/Objects/HookTarget.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=2] 2 | 3 | [ext_resource path="res://src/Objects/HookTarget.gd" type="Script" id=1] 4 | [ext_resource path="res://assets/environment/interactive/hookTarget03_wings.png" type="Texture" id=2] 5 | [ext_resource path="res://assets/environment/interactive/hookTarget02_ball.png" type="Texture" id=3] 6 | [ext_resource path="res://assets/environment/interactive/hookTarget01_glow.png" type="Texture" id=4] 7 | 8 | [sub_resource type="CircleShape2D" id=1] 9 | radius = 25.1794 10 | 11 | [sub_resource type="CanvasItemMaterial" id=2] 12 | blend_mode = 1 13 | 14 | [node name="HookTarget" type="Area2D"] 15 | collision_layer = 4 16 | script = ExtResource( 1 ) 17 | 18 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 19 | self_modulate = Color( 1, 1, 1, 0 ) 20 | shape = SubResource( 1 ) 21 | 22 | [node name="Wings" type="Sprite" parent="CollisionShape2D"] 23 | show_behind_parent = true 24 | texture = ExtResource( 2 ) 25 | 26 | [node name="Ball" type="Sprite" parent="CollisionShape2D"] 27 | show_behind_parent = true 28 | texture = ExtResource( 3 ) 29 | 30 | [node name="Glow" type="Sprite" parent="CollisionShape2D"] 31 | modulate = Color( 1, 1, 1, 0.588235 ) 32 | show_behind_parent = true 33 | material = SubResource( 2 ) 34 | texture = ExtResource( 4 ) 35 | 36 | [node name="Timer" type="Timer" parent="."] 37 | wait_time = 0.5 38 | one_shot = true 39 | -------------------------------------------------------------------------------- /game/src/Objects/HookTargetPullable/HookTargetPullable.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | # One-shot hook target with an object attached 3 | 4 | onready var target: HookTarget = $HookTarget 5 | onready var body: RigidBody2D = $PropelledBody 6 | 7 | 8 | func _ready() -> void: 9 | target.connect("hooked_onto_from", self, "_on_HookTarget_hooked_onto_from") 10 | 11 | 12 | func _on_HookTarget_hooked_onto_from(hook_position: Vector2) -> void: 13 | var impulse_offset := target.position 14 | var direction := (hook_position - target.global_position).normalized() 15 | body.propel(impulse_offset, direction) 16 | 17 | -------------------------------------------------------------------------------- /game/src/Objects/HookTargetPullable/HookTargetPullable.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=2] 2 | 3 | [ext_resource path="res://src/Objects/HookTargetPullable/HookTargetPullable.gd" type="Script" id=1] 4 | [ext_resource path="res://src/Objects/HookTargetPullable/PropelledBody.tscn" type="PackedScene" id=2] 5 | [ext_resource path="res://src/Player/Rectangle.gd" type="Script" id=3] 6 | [ext_resource path="res://src/Objects/HookTarget.tscn" type="PackedScene" id=4] 7 | 8 | 9 | [sub_resource type="RectangleShape2D" id=1] 10 | extents = Vector2( 40, 130 ) 11 | 12 | [node name="PullableTarget" type="Node2D"] 13 | script = ExtResource( 1 ) 14 | 15 | [node name="PropelledBody" parent="." instance=ExtResource( 2 )] 16 | 17 | [node name="Rectangle" type="Node2D" parent="PropelledBody"] 18 | script = ExtResource( 3 ) 19 | size = Vector2( 70, 253 ) 20 | outline = Vector2( 10, 10 ) 21 | color_fill = Color( 0.478431, 0.00784314, 0.517647, 1 ) 22 | color_outline = Color( 0.937255, 0.345098, 0.913725, 1 ) 23 | 24 | [node name="CollisionShape2D" type="CollisionShape2D" parent="PropelledBody"] 25 | shape = SubResource( 1 ) 26 | 27 | [node name="HookTarget" parent="." instance=ExtResource( 4 )] 28 | one_shot = true 29 | -------------------------------------------------------------------------------- /game/src/Objects/HookTargetPullable/PropelledBody.gd: -------------------------------------------------------------------------------- 1 | extends RigidBody2D 2 | 3 | 4 | func propel(from: Vector2, direction: Vector2) -> void: 5 | var impulse_strength := 2000.0 6 | 7 | mode = MODE_RIGID 8 | apply_impulse(from, direction * impulse_strength) 9 | Engine.time_scale = 0.03 10 | var timer := get_tree().create_timer(0.02) 11 | yield(timer, "timeout") 12 | Engine.time_scale = 1.0 13 | -------------------------------------------------------------------------------- /game/src/Objects/HookTargetPullable/PropelledBody.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://src/Objects/HookTargetPullable/PropelledBody.gd" type="Script" id=1] 4 | 5 | [node name="PropelledBody" type="RigidBody2D"] 6 | collision_layer = 0 7 | collision_mask = 2 8 | mode = 1 9 | gravity_scale = 20.0 10 | sleeping = true 11 | script = ExtResource( 1 ) 12 | -------------------------------------------------------------------------------- /game/src/Objects/MovingPlatform/MovingPlatform.gd: -------------------------------------------------------------------------------- 1 | # Moving platform, moves to target positions given by the Waypoints node 2 | tool 3 | extends KinematicBody2D 4 | 5 | onready var timer: Timer = $Timer 6 | onready var tween: Tween = $Tween 7 | onready var waypoints: = $Waypoints 8 | 9 | export var speed: = 400.0 10 | export var wait_time: = 1.0 setget set_wait_time 11 | 12 | 13 | func _ready() -> void: 14 | if Engine.editor_hint: 15 | return 16 | if not waypoints: 17 | printerr("Missing Waypoints node for %s: %s" % [name, get_path()]) 18 | return 19 | position = waypoints.get_start_position() 20 | timer.start() 21 | 22 | 23 | func _draw() -> void: 24 | var shape: = $CollisionShape2D 25 | var extents: Vector2 = shape.shape.extents * 2.0 26 | var rect: = Rect2(shape.position - extents / 2.0, extents) 27 | draw_rect(rect, Color('fff')) 28 | 29 | 30 | func _on_Timer_timeout() -> void: 31 | var target_position: Vector2 = waypoints.get_next_point_position() 32 | var distance_to_target: = position.distance_to(target_position) 33 | tween.interpolate_property(self, "position", position, target_position, distance_to_target / speed) 34 | tween.start() 35 | 36 | 37 | func _on_Tween_tween_all_completed() -> void: 38 | timer.start() 39 | 40 | 41 | func set_wait_time(value: float) -> void: 42 | wait_time = value 43 | if not timer: 44 | yield(self, "ready") 45 | timer.wait_time = wait_time 46 | -------------------------------------------------------------------------------- /game/src/Objects/MovingPlatform/MovingPlatform.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://src/Objects/MovingPlatform/MovingPlatform.gd" type="Script" id=1] 4 | 5 | [sub_resource type="RectangleShape2D" id=1] 6 | extents = Vector2( 89, 18 ) 7 | 8 | [node name="MovingPlatform" type="KinematicBody2D"] 9 | collision_layer = 2 10 | collision_mask = 0 11 | motion/sync_to_physics = true 12 | script = ExtResource( 1 ) 13 | 14 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 15 | shape = SubResource( 1 ) 16 | 17 | [node name="Timer" type="Timer" parent="."] 18 | one_shot = true 19 | 20 | [node name="Tween" type="Tween" parent="."] 21 | playback_process_mode = 0 22 | [connection signal="timeout" from="Timer" to="." method="_on_Timer_timeout"] 23 | [connection signal="tween_all_completed" from="Tween" to="." method="_on_Tween_tween_all_completed"] 24 | -------------------------------------------------------------------------------- /game/src/Objects/MovingPlatform/Waypoints.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Line2D 3 | 4 | enum Mode {CYCLE, PING_PONG} 5 | 6 | export(Mode) var mode: = Mode.CYCLE setget set_mode 7 | 8 | export var triangle_color: = Color(0.722656, 0.908997, 1) 9 | export var triangle_radius: = 6.0 10 | 11 | var _active_point_index: = 0 12 | var _direction: = 1 13 | 14 | 15 | func _ready() -> void: 16 | # set_as_toplevel(true) 17 | visible = Engine.editor_hint 18 | 19 | 20 | func _draw() -> void: 21 | if not Engine.editor_hint: 22 | return 23 | if not points.size() > 1: 24 | return 25 | var triangles: = [] 26 | var previous_point: = points[0] 27 | for point in points: 28 | if point == points[0] or (point == points[-1] and mode == Mode.CYCLE): 29 | continue 30 | triangles.append({ 31 | center=(point + previous_point) / 2, 32 | angle=previous_point.angle_to_point(point)} 33 | ) 34 | previous_point = point 35 | 36 | if mode == Mode.CYCLE: 37 | triangles.append({ 38 | center=(points[-1] - points[0]) / 2, 39 | angle=points[-1].angle_to_point(points[0])} 40 | ) 41 | draw_line(points[-1], points[0], default_color, width) 42 | for triangle in triangles: 43 | DrawingUtils.draw_triangle(self, triangle['center'], triangle['angle'], triangle_radius, triangle_color) 44 | 45 | 46 | func get_start_position() -> Vector2: 47 | return points[0] 48 | 49 | 50 | func get_current_point_position() -> Vector2: 51 | return points[_active_point_index] 52 | 53 | 54 | func get_next_point_position(): 55 | match mode: 56 | Mode.CYCLE: 57 | _active_point_index = (_active_point_index + 1) % points.size() 58 | Mode.PING_PONG: 59 | var index: = _active_point_index + _direction 60 | if index < 0 or index > points.size() - 1: 61 | _direction *= -1 62 | _active_point_index += _direction 63 | return get_current_point_position() 64 | 65 | 66 | func set_mode(value: int) -> void: 67 | mode = value 68 | update() 69 | -------------------------------------------------------------------------------- /game/src/Objects/MovingPlatform/Waypoints.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://src/Objects/MovingPlatform/Waypoints.gd" type="Script" id=1] 4 | 5 | [node name="Waypoints" type="Line2D"] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /game/src/Objects/Portal.gd: -------------------------------------------------------------------------------- 1 | extends Area2D 2 | 3 | 4 | export(String, FILE) var next_level_file_path := "" 5 | export(String) var next_level_portal_name := "" 6 | 7 | 8 | func _ready() -> void: 9 | # warning-ignore:return_value_discarded 10 | connect("body_entered", self, "_on_body_entered") 11 | 12 | 13 | func _get_configuration_warning() -> String: 14 | return "next_level_file_path is mandatory!" if next_level_file_path.empty() else "" 15 | 16 | 17 | func _on_body_entered(body: PhysicsBody2D) -> void: 18 | if body is Player and not body.has_teleported: 19 | var NextLevel = load(next_level_file_path) 20 | LevelLoader.trigger(NextLevel, next_level_portal_name) 21 | body.has_teleported = false 22 | -------------------------------------------------------------------------------- /game/src/Objects/Portal.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=2] 2 | 3 | [ext_resource path="res://src/Objects/Portal.gd" type="Script" id=1] 4 | [ext_resource path="res://assets/environment/interactive/portal01_bottom.png" type="Texture" id=2] 5 | [ext_resource path="res://assets/environment/interactive/portal02_wings.png" type="Texture" id=3] 6 | [ext_resource path="res://assets/environment/interactive/portal03_frame.png" type="Texture" id=4] 7 | 8 | [sub_resource type="CapsuleShape2D" id=1] 9 | radius = 31.0 10 | height = 178.0 11 | 12 | [node name="Portal" type="Area2D"] 13 | position = Vector2( -1, 0 ) 14 | script = ExtResource( 1 ) 15 | __meta__ = { 16 | "_edit_group_": true 17 | } 18 | 19 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 20 | position = Vector2( 0, -120 ) 21 | shape = SubResource( 1 ) 22 | 23 | [node name="Bottom" type="Sprite" parent="CollisionShape2D"] 24 | show_behind_parent = true 25 | position = Vector2( 0.5, 98.8891 ) 26 | texture = ExtResource( 2 ) 27 | 28 | [node name="Wings" type="Sprite" parent="CollisionShape2D"] 29 | show_behind_parent = true 30 | position = Vector2( 0.5, -26.1109 ) 31 | texture = ExtResource( 3 ) 32 | 33 | [node name="Frame" type="Sprite" parent="CollisionShape2D"] 34 | show_behind_parent = true 35 | position = Vector2( 1.5, -26.1109 ) 36 | texture = ExtResource( 4 ) 37 | -------------------------------------------------------------------------------- /game/src/Player/Camera/CameraAnchor.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [sub_resource type="CircleShape2D" id=1] 4 | radius = 440.0 5 | 6 | [node name="CameraAnchor" type="Area2D" groups=[ 7 | "anchor", 8 | ]] 9 | modulate = Color( 0.780392, 1, 0, 1 ) 10 | collision_layer = 32 11 | collision_mask = 0 12 | 13 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 14 | shape = SubResource( 1 ) 15 | -------------------------------------------------------------------------------- /game/src/Player/Camera/CameraAnchorDetector.gd: -------------------------------------------------------------------------------- 1 | extends Area2D 2 | 3 | 4 | onready var camera_position := $CameraPosition 5 | 6 | 7 | func _ready() -> void: 8 | # warning-ignore:return_value_discarded 9 | Events.connect("player_moved", self, "_on_Events_player_moved") 10 | 11 | 12 | func _on_Events_player_moved(player: KinematicBody2D) -> void: 13 | # first reset parameters 14 | camera_position.position = Vector2.ZERO 15 | player.camera_rig.is_active = true 16 | # player.shaking_camera.reset_smoothing_speed() 17 | 18 | for area in get_overlapping_areas(): 19 | if not area.is_in_group("anchor"): 20 | continue 21 | 22 | # if needed update player position based on anchor point 23 | if player.global_position.x < area.global_position.x: 24 | camera_position.global_position = area.global_position 25 | player.camera_rig.is_active = false 26 | player.shaking_camera.smoothing_speed = 1.5 27 | -------------------------------------------------------------------------------- /game/src/Player/Camera/CameraAnchorDetector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://src/Player/Camera/CameraAnchorDetector.gd" type="Script" id=1] 4 | 5 | [sub_resource type="CircleShape2D" id=1] 6 | radius = 82.0503 7 | 8 | [node name="CameraAnchorDetector" type="Area2D"] 9 | position = Vector2( 0, -30 ) 10 | collision_layer = 0 11 | collision_mask = 32 12 | script = ExtResource( 1 ) 13 | 14 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 15 | shape = SubResource( 1 ) 16 | 17 | [node name="CameraPosition" type="RemoteTransform2D" parent="."] 18 | update_rotation = false 19 | update_scale = false 20 | -------------------------------------------------------------------------------- /game/src/Player/Camera/CameraRig.gd: -------------------------------------------------------------------------------- 1 | extends Position2D 2 | # Rig to move a child camera based on the player's input, to give them more forward visibility 3 | 4 | 5 | onready var camera: Camera2D = $ShakingCamera 6 | 7 | export var offset := Vector2(300.0, 300.0) 8 | export var mouse_range := Vector2(100.0, 500.0) 9 | 10 | var is_active := true 11 | 12 | 13 | func _physics_process(_delta: float) -> void: 14 | update_position() 15 | 16 | 17 | func update_position(_velocity: Vector2 = Vector2.ZERO) -> void: 18 | # Updates the camera rig's position based on the player's state and controller position 19 | if not is_active: 20 | return 21 | 22 | match Settings.controls: 23 | 24 | Settings.GAMEPAD: 25 | var joystick_strength := Utils.get_aim_joystick_strength() 26 | camera.position = joystick_strength * offset 27 | 28 | Settings.KBD_MOUSE, _: 29 | var mouse_position := get_local_mouse_position() 30 | var distance_ratio := clamp(mouse_position.length(), mouse_range.x, mouse_range.y) / mouse_range.y 31 | camera.position = distance_ratio * mouse_position.normalized() * offset 32 | -------------------------------------------------------------------------------- /game/src/Player/Camera/CameraRig.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://src/Player/Camera/CameraRig.gd" type="Script" id=1] 4 | [ext_resource path="res://src/Player/Camera/ShakingCamera.gd" type="Script" id=2] 5 | 6 | [node name="CameraRig" type="Position2D"] 7 | script = ExtResource( 1 ) 8 | 9 | [node name="ShakingCamera" type="Camera2D" parent="."] 10 | current = true 11 | process_mode = 0 12 | smoothing_enabled = true 13 | smoothing_speed = 3.0 14 | drag_margin_left = 0.0 15 | drag_margin_top = 0.0 16 | drag_margin_right = 0.0 17 | drag_margin_bottom = 0.0 18 | script = ExtResource( 2 ) 19 | amplitude = 8.0 20 | duration = 0.8 21 | default_smoothing_speed = { 22 | "gamepad": 1, 23 | "mouse": 3 24 | } 25 | 26 | [node name="Timer" type="Timer" parent="ShakingCamera"] 27 | wait_time = 0.8 28 | one_shot = true 29 | -------------------------------------------------------------------------------- /game/src/Player/Camera/ShakingCamera.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Camera2D 3 | # Shakes the screen when is_shaking is set to true 4 | # To make it react to events happening in the game world, use the Events signal routing singleton 5 | # Uses different smoothing values depending on the active controller 6 | 7 | onready var timer = $Timer 8 | 9 | export var amplitude = 4.0 10 | export var duration = 0.3 setget set_duration 11 | export var DAMP_EASING = 1.0 12 | export var is_shaking = false setget set_is_shaking 13 | export var default_smoothing_speed := { 14 | mouse=3, 15 | gamepad=1 16 | } 17 | 18 | enum States {IDLE, SHAKING} 19 | var state = States.IDLE 20 | 21 | 22 | func reset_smoothing_speed() -> void: 23 | match Settings.controls: 24 | Settings.GAMEPAD: 25 | smoothing_speed = default_smoothing_speed.gamepad 26 | 27 | Settings.KBD_MOUSE, _: 28 | smoothing_speed = default_smoothing_speed.mouse 29 | 30 | 31 | func _ready() -> void: 32 | # warning-ignore:return_value_discarded 33 | Settings.connect('controls_changed', self, 'reset_smoothing_speed') 34 | timer.connect('timeout', self, '_on_ShakeTimer_timeout') 35 | 36 | self.duration = duration 37 | reset_smoothing_speed() 38 | set_process(false) 39 | 40 | 41 | func _process(_delta) -> void: 42 | var damping = ease(timer.time_left / timer.wait_time, DAMP_EASING) 43 | offset = Vector2( 44 | rand_range(amplitude, -amplitude) * damping, 45 | rand_range(amplitude, -amplitude) * damping) 46 | 47 | 48 | func _get_configuration_warning() -> String: 49 | return "" if $Timer else "%s requires a Timer child named Timer" % name 50 | 51 | 52 | func set_duration(value: float) -> void: 53 | duration = value 54 | if timer: 55 | timer.wait_time = duration 56 | 57 | 58 | func set_is_shaking(value: bool) -> void: 59 | is_shaking = value 60 | if is_shaking: 61 | _change_state(States.SHAKING) 62 | else: 63 | _change_state(States.IDLE) 64 | 65 | 66 | func _change_state(new_state: int) -> void: 67 | match new_state: 68 | States.IDLE: 69 | offset = Vector2() 70 | set_process(false) 71 | States.SHAKING: 72 | set_process(true) 73 | timer.start() 74 | state = new_state 75 | 76 | 77 | func _on_ShakeTimer_timeout() -> void: 78 | self.is_shaking = false 79 | -------------------------------------------------------------------------------- /game/src/Player/Camera/ShakingCamera.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://src/Player/Camera/ShakingCamera.gd" type="Script" id=1] 4 | 5 | [node name="ShakingCamera" type="Camera2D"] 6 | current = true 7 | smoothing_enabled = true 8 | script = ExtResource( 1 ) 9 | amplitude = 8.0 10 | duration = 0.8 11 | 12 | [node name="Timer" type="Timer" parent="."] 13 | wait_time = 0.8 14 | one_shot = true 15 | -------------------------------------------------------------------------------- /game/src/Player/FloorDetector.gd: -------------------------------------------------------------------------------- 1 | extends RayCast2D 2 | class_name FloorDetector 3 | # Down facing ray, relative to its parent, used to detect the floor and the floor's position. 4 | 5 | 6 | func is_close_to_floor() -> bool: 7 | return is_colliding() 8 | 9 | 10 | func get_floor_position() -> Vector2: 11 | force_raycast_update() 12 | return get_collision_point() 13 | 14 | 15 | func get_floor_distance_ratio() -> float: 16 | force_raycast_update() 17 | var ratio := 1.0 - abs(get_collision_point().y - global_position.y) / cast_to.y 18 | return ratio 19 | -------------------------------------------------------------------------------- /game/src/Player/FloorDetector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://src/Player/FloorDetector.gd" type="Script" id=1] 4 | 5 | [node name="FloorDetector" type="RayCast2D"] 6 | position = Vector2( 0, -30 ) 7 | cast_to = Vector2( 0, 80 ) 8 | collision_mask = 10 9 | script = ExtResource( 1 ) 10 | -------------------------------------------------------------------------------- /game/src/Player/Hook/Arrow.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | 4 | onready var head := $Head 5 | onready var tail := $Tail 6 | onready var tween: Tween = $Tween 7 | 8 | onready var start_length: float = head.position.x 9 | 10 | var hook_position := Vector2.ZERO setget set_hook_position 11 | var length := 40.0 setget set_length 12 | 13 | 14 | func set_hook_position(value: Vector2) -> void: 15 | hook_position = value 16 | var to_target := hook_position - global_position 17 | self.length = to_target.length() 18 | rotation = to_target.angle() 19 | # warning-ignore:return_value_discarded 20 | tween.interpolate_property( 21 | self, 'length', length, start_length, 22 | 0.25, Tween.TRANS_QUAD, Tween.EASE_OUT) 23 | # warning-ignore:return_value_discarded 24 | tween.start() 25 | 26 | 27 | func set_length(value: float) -> void: 28 | length = value 29 | tail.points[-1].x = length 30 | head.position.x = tail.points[-1].x + tail.position.x 31 | -------------------------------------------------------------------------------- /game/src/Player/Hook/Arrow.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://src/Player/Hook/Arrow.gd" type="Script" id=1] 4 | [ext_resource path="res://assets/characters/robi/hook.png" type="Texture" id=2] 5 | 6 | [node name="Arrow" type="Node2D"] 7 | script = ExtResource( 1 ) 8 | 9 | [node name="Tail" type="Line2D" parent="."] 10 | position = Vector2( 7.85185, -0.296295 ) 11 | points = PoolVector2Array( 40, 0, 30, 0 ) 12 | width = 6.0 13 | default_color = Color( 0.101961, 0.188235, 0.235294, 1 ) 14 | texture_mode = 250139424 15 | joint_mode = 2 16 | begin_cap_mode = 2 17 | end_cap_mode = 2 18 | 19 | [node name="Head" type="Sprite" parent="."] 20 | position = Vector2( 60.5297, -0.130506 ) 21 | rotation = 1.57079 22 | scale = Vector2( 0.349428, 0.349428 ) 23 | texture = ExtResource( 2 ) 24 | offset = Vector2( -1.56991, -69.8977 ) 25 | 26 | [node name="Tween" type="Tween" parent="."] 27 | -------------------------------------------------------------------------------- /game/src/Player/Hook/Hook.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Position2D 3 | class_name Hook, "res://assets/icons/icon_hook.svg" 4 | # Throws a raycast that can interact with Hookable bodies and calculate a pull vector towards those bodies. 5 | # The raycast is updated manually for greater precision with where the player is aiming 6 | # Draws the hook's range in the editor 7 | 8 | 9 | # warning-ignore:unused_signal 10 | signal hooked_onto_target(target_global_position) 11 | 12 | onready var ray_cast: RayCast2D = $RayCast2D 13 | onready var arrow: Node2D = $Arrow 14 | onready var snap_detector: Area2D = $SnapDetector 15 | onready var cooldown: Timer = $Cooldown 16 | 17 | var is_aiming := false setget set_is_aiming 18 | var is_active := true setget set_is_active 19 | 20 | onready var _radius: float = snap_detector.calculate_length() 21 | 22 | func _ready() -> void: 23 | if Engine.editor_hint: 24 | update() 25 | 26 | 27 | func _draw() -> void: 28 | if not Engine.editor_hint: 29 | return 30 | 31 | DrawingUtils.draw_circle_outline(self, Vector2.ZERO, snap_detector.calculate_length(), Color.lightgreen) 32 | 33 | 34 | func can_hook() -> bool: 35 | return is_active and snap_detector.has_target() and cooldown.is_stopped() 36 | 37 | 38 | func get_aim_direction() -> Vector2: 39 | var direction := Vector2.ZERO 40 | match Settings.controls: 41 | Settings.GAMEPAD: 42 | direction = Utils.get_aim_joystick_direction() 43 | Settings.KBD_MOUSE, _: 44 | direction = (get_global_mouse_position() - global_position).normalized() 45 | return direction 46 | 47 | 48 | func set_is_aiming(value: bool) -> void: 49 | is_aiming = value 50 | Engine.time_scale = 0.05 if is_aiming == true else 1.0 51 | 52 | 53 | func set_is_active(value: bool) -> void: 54 | is_active = value 55 | set_process_unhandled_input(value) 56 | -------------------------------------------------------------------------------- /game/src/Player/Hook/Hook.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=2] 2 | 3 | [ext_resource path="res://src/Player/Hook/Hook.gd" type="Script" id=1] 4 | [ext_resource path="res://src/Player/Hook/Arrow.tscn" type="PackedScene" id=2] 5 | [ext_resource path="res://src/Combat/DamageSource.tscn" type="PackedScene" id=3] 6 | [ext_resource path="res://src/Player/Hook/SnapDetector.tscn" type="PackedScene" id=4] 7 | [ext_resource path="res://src/Main/StateMachine/StateMachine.gd" type="Script" id=5] 8 | [ext_resource path="res://src/Player/Hook/States/Aim.gd" type="Script" id=6] 9 | [ext_resource path="res://src/Player/Hook/States/Fire.gd" type="Script" id=7] 10 | 11 | [node name="Hook" type="Position2D"] 12 | script = ExtResource( 1 ) 13 | 14 | [node name="RayCast2D" type="RayCast2D" parent="."] 15 | cast_to = Vector2( 460, 0 ) 16 | collision_mask = 6 17 | 18 | [node name="Arrow" parent="." instance=ExtResource( 2 )] 19 | 20 | [node name="Tail" parent="Arrow" index="0"] 21 | texture_mode = 0 22 | 23 | [node name="Head" parent="Arrow" index="1"] 24 | editor/display_folded = true 25 | 26 | [node name="DamageSource" parent="Arrow/Head" index="0" instance=ExtResource( 3 )] 27 | 28 | [node name="SnapDetector" parent="." instance=ExtResource( 4 )] 29 | 30 | [node name="Cooldown" type="Timer" parent="."] 31 | process_mode = 0 32 | wait_time = 0.2 33 | one_shot = true 34 | 35 | [node name="StateMachine" type="Node" parent="."] 36 | script = ExtResource( 5 ) 37 | initial_state = NodePath("Aim") 38 | 39 | [node name="Aim" type="Node" parent="StateMachine"] 40 | script = ExtResource( 6 ) 41 | 42 | [node name="Fire" type="Node" parent="StateMachine"] 43 | script = ExtResource( 7 ) 44 | 45 | [editable path="Arrow"] 46 | 47 | [editable path="Arrow/Head/DamageSource"] 48 | -------------------------------------------------------------------------------- /game/src/Player/Hook/HookingHint.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends DrawingUtils 3 | # Draws a target to indicate if and where the player can hook 4 | 5 | # 6 | # # export var color: Color = COLOR_BLUE_LIGHT 7 | 8 | # 9 | # # func _ready() -> void: 10 | # set_as_toplevel(true) 11 | # update() 12 | 13 | # 14 | # # func _draw() -> void: 15 | # draw_circle_outline(self, Vector2.ZERO, 20.0, color, 4.0) 16 | # draw_circle(Vector2.ZERO, 10.0, color) 17 | -------------------------------------------------------------------------------- /game/src/Player/Hook/InputContinuous.gd: -------------------------------------------------------------------------------- 1 | # Generates input events continuously based on a timer 2 | # Allows the player to hook onto a target even if they pressed the hook key before the hook was in range 3 | extends Node 4 | 5 | export var timer_duration := 0.05 6 | 7 | const ACTION_HOOK := 'hook' 8 | 9 | var _action_names := [] 10 | 11 | func _unhandled_input(event: InputEvent) -> void: 12 | if event.is_action_pressed(ACTION_HOOK) and not ACTION_HOOK in _action_names: 13 | _action_names.append(ACTION_HOOK) 14 | _remove_delayed(_action_names.size() - 1, timer_duration) 15 | 16 | 17 | func _physics_process(delta: float) -> void: 18 | for action_name in _action_names: 19 | var event := InputEventAction.new() 20 | event.action = action_name 21 | event.pressed = true 22 | Input.parse_input_event(event) 23 | 24 | 25 | # Coroutine 26 | func _remove_delayed(action_id:int, delay:float) -> void: 27 | yield(get_tree().create_timer(delay), "timeout") 28 | _action_names.remove(action_id) 29 | -------------------------------------------------------------------------------- /game/src/Player/Hook/SnapDetector.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Area2D 3 | # Detects and returns the best snapping target for the hook 4 | 5 | 6 | onready var hooking_hint: Position2D = $HookingHint 7 | onready var ray_cast: RayCast2D = $RayCast2D 8 | 9 | var target: HookTarget setget set_target 10 | 11 | 12 | func _ready() -> void: 13 | if Engine.editor_hint: 14 | set_physics_process(false) 15 | ray_cast.set_as_toplevel(true) 16 | 17 | 18 | func _physics_process(_delta: float) -> void: 19 | self.target = find_best_target() 20 | 21 | 22 | # Returns the closest target, skipping targets when there is an obstacle 23 | # between the player and the target. 24 | func find_best_target() -> HookTarget: 25 | var targets := get_overlapping_areas() 26 | 27 | var closest_target: HookTarget = null 28 | var distance_to_closest := 100000.0 29 | for t in targets: 30 | if not t.is_active: 31 | continue 32 | 33 | var distance := global_position.distance_to(t.global_position) 34 | if distance > distance_to_closest: 35 | continue 36 | 37 | # Skip the target if there is a collider in the way 38 | ray_cast.global_position = global_position 39 | ray_cast.cast_to = t.global_position - global_position 40 | if ray_cast.is_colliding(): 41 | continue 42 | 43 | distance_to_closest = distance 44 | closest_target = t 45 | return closest_target 46 | 47 | 48 | func has_target() -> bool: 49 | return target != null 50 | 51 | 52 | # Returns the length of the hook, from the origin to the tip of the collision shape 53 | # Used to draw the hook's radius in the editor 54 | func calculate_length() -> float: 55 | var length := -1.0 56 | for collider in [$CapsuleH, $CapsuleV]: 57 | if not collider: 58 | continue 59 | var capsule: CapsuleShape2D = collider.shape 60 | var capsule_length: float = collider.position.length() + capsule.height / 2 * sin(collider.rotation) + capsule.radius 61 | length = max(length, capsule_length) 62 | return length 63 | 64 | 65 | func set_target(value: HookTarget) -> void: 66 | target = value 67 | hooking_hint.visible = target != null 68 | hooking_hint.global_position = target.global_position if target else hooking_hint.global_position 69 | -------------------------------------------------------------------------------- /game/src/Player/Hook/SnapDetector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://src/Player/Hook/SnapDetector.gd" type="Script" id=1] 4 | [ext_resource path="res://src/Player/Hook/HookingHint.gd" type="Script" id=2] 5 | 6 | [sub_resource type="CapsuleShape2D" id=1] 7 | radius = 90.0002 8 | height = 160.0 9 | 10 | [sub_resource type="CapsuleShape2D" id=2] 11 | radius = 100.0 12 | height = 139.999 13 | 14 | [node name="SnapDetector" type="Area2D"] 15 | monitorable = false 16 | collision_layer = 0 17 | collision_mask = 4 18 | script = ExtResource( 1 ) 19 | 20 | [node name="HookingHint" type="Position2D" parent="."] 21 | visible = false 22 | script = ExtResource( 2 ) 23 | color = Color( 0.0352941, 0.65098, 0.792157, 1 ) 24 | 25 | [node name="CapsuleH" type="CollisionShape2D" parent="."] 26 | modulate = Color( 0.0392157, 0.152941, 0.772549, 0.682353 ) 27 | show_behind_parent = true 28 | position = Vector2( 230, 0 ) 29 | rotation = 1.57079 30 | shape = SubResource( 1 ) 31 | 32 | [node name="CapsuleV" type="CollisionShape2D" parent="."] 33 | modulate = Color( 0.0392157, 0.152941, 0.772549, 0.682353 ) 34 | show_behind_parent = true 35 | position = Vector2( 300, 0 ) 36 | shape = SubResource( 2 ) 37 | 38 | [node name="RayCast2D" type="RayCast2D" parent="."] 39 | position = Vector2( 60, 0 ) 40 | enabled = true 41 | cast_to = Vector2( 100, 0 ) 42 | collision_mask = 2 43 | -------------------------------------------------------------------------------- /game/src/Player/Hook/States/Aim.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | 3 | 4 | func unhandled_input(event: InputEvent) -> void: 5 | if event.is_action_pressed("aim"): 6 | owner.is_aiming = not owner.is_aiming 7 | elif event.is_action_pressed("hook"): 8 | _state_machine.transition_to("Fire") 9 | 10 | 11 | func physics_process(_delta: float) -> void: 12 | var cast: Vector2 = owner.get_aim_direction() * owner._radius 13 | var angle := cast.angle() 14 | owner.ray_cast.cast_to = cast 15 | owner.arrow.rotation = angle 16 | owner.snap_detector.rotation = angle 17 | owner.ray_cast.force_raycast_update() 18 | -------------------------------------------------------------------------------- /game/src/Player/Hook/States/Fire.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | 3 | 4 | func _on_Cooldown_timeout() -> void: 5 | _state_machine.transition_to("Aim") 6 | 7 | 8 | func enter(_msg: Dictionary = {}) -> void: 9 | owner.is_aiming = false 10 | 11 | var target: HookTarget = owner.snap_detector.target 12 | if not target: 13 | var distance := min(owner._radius, owner.get_local_mouse_position().length()) 14 | owner.arrow.hook_position = owner.global_position + owner.get_local_mouse_position().normalized() * distance / 2.0 15 | _state_machine.transition_to("Aim") 16 | return 17 | 18 | owner.arrow.hook_position = target.global_position 19 | target.hooked_from(owner.global_position) 20 | 21 | owner.cooldown.connect("timeout", self, "_on_Cooldown_timeout", [], CONNECT_ONESHOT) 22 | owner.cooldown.start() 23 | 24 | owner.emit_signal("hooked_onto_target", target.global_position) 25 | -------------------------------------------------------------------------------- /game/src/Player/LedgeWallDetector.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Position2D 3 | # Detects ledges using two Raycasts casting horizontally. 4 | # If one ray is in a wall and the other is in the air, it means the node is near a ledge. 5 | 6 | 7 | onready var ray_bottom: RayCast2D = $RayBottom 8 | onready var ray_top: RayCast2D = $RayTop 9 | 10 | export var is_active := true 11 | 12 | 13 | func _ready(): 14 | assert(ray_top.cast_to.x >= 0) 15 | assert(ray_bottom.cast_to.x >= 0) 16 | 17 | 18 | func _unhandled_input(event: InputEvent) -> void: 19 | if event.is_action_pressed("move_left"): 20 | scale.x = -1 21 | elif event.is_action_pressed("move_right"): 22 | scale.x = 1 23 | 24 | 25 | func is_against_ledge() -> bool: 26 | return is_active and ray_bottom.is_colliding() and not ray_top.is_colliding() 27 | 28 | 29 | func is_against_wall() -> bool: 30 | return is_active and (ray_bottom.is_colliding() or ray_top.is_colliding()) 31 | 32 | 33 | func get_cast_to_directed() -> Vector2: 34 | return Vector2(ray_top.cast_to.x * scale.x, 0.0) 35 | 36 | 37 | func get_top_global_position() -> Vector2: 38 | return ray_top.global_position 39 | 40 | 41 | func get_ray_length() -> float: 42 | return ray_top.cast_to.x 43 | -------------------------------------------------------------------------------- /game/src/Player/LedgeWallDetector.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://src/Player/LedgeWallDetector.gd" type="Script" id=1] 4 | 5 | [node name="LedeWallDetector" type="Position2D"] 6 | script = ExtResource( 1 ) 7 | 8 | [node name="RayBottom" type="RayCast2D" parent="."] 9 | position = Vector2( 24, 0 ) 10 | enabled = true 11 | exclude_parent = false 12 | cast_to = Vector2( 20, 0 ) 13 | collision_mask = 2 14 | 15 | [node name="RayTop" type="RayCast2D" parent="."] 16 | position = Vector2( 24, -35 ) 17 | enabled = true 18 | exclude_parent = false 19 | cast_to = Vector2( 20, 0 ) 20 | collision_mask = 2 21 | -------------------------------------------------------------------------------- /game/src/Player/PassThrough.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [sub_resource type="RayShape2D" id=1] 4 | length = 10.0 5 | 6 | [node name="PassThrough" type="Area2D"] 7 | collision_layer = 0 8 | collision_mask = 8 9 | 10 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 11 | rotation = -3.14159 12 | shape = SubResource( 1 ) 13 | one_way_collision = true 14 | -------------------------------------------------------------------------------- /game/src/Player/Player.gd: -------------------------------------------------------------------------------- 1 | extends KinematicBody2D 2 | class_name Player 3 | 4 | 5 | # warning-ignore:unused_signal 6 | signal hopped_off_entity 7 | 8 | onready var state_machine: StateMachine = $StateMachine 9 | 10 | onready var hook: Position2D = $Hook 11 | 12 | onready var skin: Position2D = $Skin 13 | onready var collider: CollisionShape2D = $CollisionShape2D setget ,get_collider 14 | 15 | onready var stats: Stats = $Stats 16 | onready var hitbox: Area2D = $HitBox 17 | 18 | onready var camera_rig: Position2D = $CameraRig 19 | onready var shaking_camera: Camera2D = $CameraRig/ShakingCamera 20 | 21 | onready var ledge_wall_detector: Position2D = $LedgeWallDetector 22 | onready var floor_detector: RayCast2D = $FloorDetector 23 | 24 | onready var pass_through: Area2D = $PassThrough 25 | 26 | 27 | const FLOOR_NORMAL := Vector2.UP 28 | 29 | var is_active := true setget set_is_active 30 | var has_teleported := false 31 | var last_checkpoint: Area2D = null 32 | 33 | 34 | func _ready() -> void: 35 | # warning-ignore:return_value_discarded 36 | stats.connect("health_depleted", self, "_on_Player_health_depleted") 37 | # warning-ignore:return_value_discarded 38 | Events.connect("checkpoint_visited", self, "_on_Events_checkpoint_visited") 39 | 40 | 41 | func take_damage(source: Hit) -> void: 42 | stats.take_damage(source) 43 | 44 | 45 | func set_is_active(value: bool) -> void: 46 | is_active = value 47 | if not collider: 48 | return 49 | collider.disabled = not value 50 | hook.is_active = value 51 | ledge_wall_detector.is_active = value 52 | hitbox.monitoring = value 53 | 54 | 55 | func get_collider() -> CollisionShape2D: 56 | return collider 57 | 58 | 59 | func _on_Player_health_depleted() -> void: 60 | state_machine.transition_to("Die", {last_checkpoint = last_checkpoint}) 61 | 62 | 63 | func _on_Events_checkpoint_visited(checkpoint_name: String) -> void: 64 | last_checkpoint = LevelLoader._level.get_node("Checkpoints/%s" % checkpoint_name) 65 | -------------------------------------------------------------------------------- /game/src/Player/Rectangle.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Node2D 3 | # Draws a rectangle with an outline 4 | 5 | 6 | export var size := Vector2(40.0, 40.0) setget set_size 7 | export var outline := Vector2(6.0, 6.0) setget set_outline 8 | 9 | export var color_fill := Color(0.890625, 0.583793, 0.149597) setget set_color_fill 10 | export var color_outline := Color(0.890625, 0.583793, 0.149597) setget set_color_outline 11 | 12 | 13 | func _draw() -> void: 14 | var size_complete := size + outline 15 | var rect_outline := Rect2(-size_complete / 2, size_complete) 16 | var rect_fill := Rect2(-size / 2, size) 17 | draw_rect(rect_outline, color_outline) 18 | draw_rect(rect_fill, color_fill) 19 | 20 | 21 | func set_size(value:Vector2) -> void: 22 | size = value 23 | update() 24 | 25 | func set_outline(value:Vector2) -> void: 26 | outline = value 27 | update() 28 | 29 | 30 | func set_color_fill(value:Color) -> void: 31 | color_fill = value 32 | update() 33 | 34 | 35 | func set_color_outline(value:Color) -> void: 36 | color_outline = value 37 | update() 38 | -------------------------------------------------------------------------------- /game/src/Player/Skin.gd: -------------------------------------------------------------------------------- 1 | extends Position2D 2 | # The player's animated skin. Provides a simple interface to play animations. 3 | 4 | signal animation_finished(name) 5 | 6 | onready var anim: AnimationPlayer = $AnimationPlayer 7 | onready var floor_detector: FloorDetector = $FloorDetector 8 | onready var shadow: Sprite = $Shadow 9 | 10 | 11 | func _ready() -> void: 12 | # warning-ignore:return_value_discarded 13 | anim.connect("animation_finished", self, "_on_Anim_animation_finished") 14 | 15 | 16 | func _on_Anim_animation_finished(name: String) -> void: 17 | emit_signal("animation_finished", name) 18 | 19 | 20 | func _physics_process(_delta: float) -> void: 21 | floor_detector.force_raycast_update() 22 | shadow.visible = floor_detector.is_close_to_floor() 23 | var ratio := floor_detector.get_floor_distance_ratio() 24 | shadow.scale = Vector2(ratio, ratio) * shadow.scale_start 25 | shadow.global_position = floor_detector.get_floor_position() 26 | 27 | 28 | func play(name: String, data: Dictionary = {}) -> void: 29 | # Plays the requested animation and safeguards against errors 30 | assert(name in anim.get_animation_list()) 31 | anim.stop() 32 | if name == "ledge": 33 | assert('from' in data) 34 | position = data.from 35 | anim.play(name) 36 | 37 | -------------------------------------------------------------------------------- /game/src/Player/Skin/Shadow.gd: -------------------------------------------------------------------------------- 1 | extends Sprite 2 | 3 | 4 | var scale_start := scale 5 | 6 | 7 | func _ready() -> void: 8 | set_as_toplevel(true) 9 | -------------------------------------------------------------------------------- /game/src/Player/States/Debug.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # Moves the player freely around the world, ignoring collisions. 3 | # This state has its own movement code so it doesn't depend on or mess with the Move state 4 | # Use the move_* input to move the character, or click 5 | # debug_sprint, assigned to Shift on the keyboard and B on an XBOX controller, moves the character faster 6 | 7 | 8 | var velocity := Vector2.ZERO 9 | const speed := Vector2(600.0, 600.0) 10 | 11 | 12 | func unhandled_input(event: InputEvent) -> void: 13 | if event.is_action_pressed('toggle_debug_move'): 14 | _state_machine.transition_to('Move/Air', {'velocity': Vector2.ZERO}) 15 | if event.is_action_pressed('click'): 16 | owner.position += owner.get_local_mouse_position() 17 | 18 | 19 | func physics_process(delta: float) -> void: 20 | var direction := get_move_direction() 21 | var multiplier := 3.0 if Input.is_action_pressed('debug_sprint') else 1.0 22 | velocity = speed * direction * multiplier 23 | owner.position += velocity * delta 24 | Events.emit_signal("player_moved", owner) 25 | 26 | 27 | func enter(_msg: Dictionary = {}): 28 | owner.is_active = false 29 | 30 | 31 | func exit(): 32 | owner.is_active = true 33 | 34 | 35 | func get_move_direction() -> Vector2: 36 | return Vector2( 37 | Input.get_action_strength("move_right") - Input.get_action_strength("move_left"), 38 | Input.get_action_strength("move_down") - Input.get_action_strength("move_up")) 39 | -------------------------------------------------------------------------------- /game/src/Player/States/Die.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | 3 | 4 | var last_checkpoint: Area2D = null 5 | 6 | 7 | func _on_Player_animation_finished(_anim_name: String) -> void: 8 | _state_machine.transition_to('Spawn', {last_checkpoint = last_checkpoint}) 9 | 10 | 11 | func enter(msg: Dictionary = {}) -> void: 12 | assert("last_checkpoint" in msg) 13 | last_checkpoint = msg.last_checkpoint 14 | owner.camera_rig.is_active = true 15 | owner.skin.play("die") 16 | owner.skin.connect("animation_finished", self, "_on_Player_animation_finished") 17 | 18 | 19 | func exit() -> void: 20 | owner.skin.disconnect("animation_finished", self, "_on_Player_animation_finished") 21 | -------------------------------------------------------------------------------- /game/src/Player/States/Hook.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # Moves the character to the target position using the arrive_to steering behavior 3 | # Preserves the character's inertia past the hooking point 4 | 5 | 6 | const HOOK_MAX_SPEED := 1600.0 7 | 8 | export var arrive_push := 500.0 9 | 10 | var target_global_position := Vector2(INF, INF) 11 | var velocity := Vector2.ZERO 12 | var _target_is_living_entity := false 13 | 14 | 15 | func physics_process(delta: float) -> void: 16 | var new_velocity := Steering.arrive_to( 17 | velocity, 18 | owner.global_position, 19 | target_global_position, 20 | delta, 21 | HOOK_MAX_SPEED 22 | ) 23 | new_velocity = new_velocity if new_velocity.length() > arrive_push else new_velocity.normalized() * arrive_push 24 | velocity = owner.move_and_slide(new_velocity, owner.FLOOR_NORMAL) 25 | Events.emit_signal("player_moved", owner) 26 | 27 | var to_target: Vector2 = target_global_position - owner.global_position 28 | var distance := to_target.length() 29 | 30 | if distance < velocity.length() * delta: 31 | velocity = velocity.normalized() * arrive_push 32 | if _target_is_living_entity: 33 | _state_machine.transition_to("HopOnEnemy") 34 | else: 35 | _state_machine.transition_to("Move/Air", {velocity = velocity}) 36 | 37 | if owner.is_on_floor(): 38 | _state_machine.transition_to("Move/Run") 39 | 40 | 41 | func enter(msg: Dictionary = {}) -> void: 42 | _target_is_living_entity = owner.hook.snap_detector.target.owner is KinematicBody2D 43 | match msg: 44 | {"target_global_position": var tgp, "velocity": var v}: 45 | target_global_position = tgp 46 | velocity = v 47 | 48 | 49 | func exit() -> void: 50 | target_global_position = Vector2(INF, INF) 51 | velocity = Vector2.ZERO 52 | -------------------------------------------------------------------------------- /game/src/Player/States/HopOnEnemy.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # Player state when grappling an enemy. Waits to let player aim/take stock, then jumps up. 3 | 4 | 5 | export var hop_impulse := 500.0 6 | export var wait_duration := 0.6 7 | 8 | 9 | func enter(_msg: Dictionary = {}) -> void: 10 | owner.stats.set_invulnerable_for_seconds(wait_duration*3) 11 | 12 | var timer := get_tree().create_timer(wait_duration) 13 | yield(timer, "timeout") 14 | 15 | owner.emit_signal("hopped_off_entity") 16 | 17 | owner.state_machine.transition_to('Move/Air', {impulse = hop_impulse, velocity = Vector2.ZERO}) 18 | -------------------------------------------------------------------------------- /game/src/Player/States/Idle.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends State 3 | 4 | 5 | onready var jump_delay: Timer = $JumpDelay 6 | 7 | 8 | func _get_configuration_warning() -> String: 9 | return "" if $JumpDelay else "%s requires a Timer child named JumpDelay" % name 10 | 11 | 12 | func unhandled_input(event: InputEvent) -> void: 13 | _parent.unhandled_input(event) 14 | 15 | 16 | func physics_process(delta: float) -> void: 17 | if owner.is_on_floor() and _parent.get_move_direction().x != 0.0: 18 | _state_machine.transition_to("Move/Run") 19 | elif not owner.is_on_floor(): 20 | _state_machine.transition_to("Move/Air") 21 | else: 22 | _parent.physics_process(delta) 23 | 24 | 25 | func enter(msg: Dictionary = {}) -> void: 26 | _parent.enter(msg) 27 | 28 | _parent.max_speed = _parent.max_speed_default 29 | _parent.velocity = Vector2.ZERO 30 | _parent.snap_vector.y = _parent.snap_distance 31 | if jump_delay.time_left > 0.0: 32 | _state_machine.transition_to("Move/Air") 33 | 34 | 35 | func exit() -> void: 36 | _parent.exit() 37 | -------------------------------------------------------------------------------- /game/src/Player/States/Ledge.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # Pulls the character up a ledge, temporarily taking control away from the player 3 | 4 | 5 | func _on_Skin_animation_finished(name: String) -> void: 6 | if name == "ledge": 7 | _state_machine.transition_to("Move/Run") 8 | 9 | 10 | func enter(msg: Dictionary = {}) -> void: 11 | assert("move_state" in msg and msg.move_state is State) 12 | 13 | owner.skin.connect("animation_finished", self, "_on_Skin_animation_finished") 14 | 15 | var start: Vector2 = owner.global_position 16 | var ld = owner.ledge_wall_detector 17 | owner.global_position = ld.get_top_global_position() + ld.get_cast_to_directed() 18 | owner.global_position = owner.floor_detector.get_floor_position() 19 | msg.move_state.velocity.y = 0.0 20 | owner.skin.play('ledge', {from = start - owner.global_position}) 21 | 22 | 23 | func exit() -> void: 24 | owner.skin.disconnect("animation_finished", self, "_on_Skin_animation_finished") 25 | -------------------------------------------------------------------------------- /game/src/Player/States/Run.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # Horizontal movement on the ground. 3 | # Delegates movement to its parent Move state and extends it 4 | # with state transitions 5 | 6 | onready var slow_starter: Timer = $SlowStarter 7 | onready var tween: Tween = $Tween 8 | 9 | export var slow_duration_seconds := 0.4 10 | 11 | 12 | func _ready() -> void: 13 | # warning-ignore:return_value_discarded 14 | slow_starter.connect("timeout", self, "_on_SlowDown_timeout") 15 | 16 | 17 | func _on_SlowDown_timeout() -> void: 18 | # warning-ignore:return_value_discarded 19 | tween.interpolate_property( 20 | _parent, 21 | "max_speed", 22 | _parent.max_speed, 23 | _parent.max_speed_default, 24 | slow_duration_seconds, 25 | Tween.TRANS_LINEAR, 26 | Tween.EASE_OUT 27 | ) 28 | # warning-ignore:return_value_discarded 29 | tween.start() 30 | 31 | 32 | func unhandled_input(event: InputEvent) -> void: 33 | _parent.unhandled_input(event) 34 | 35 | 36 | func physics_process(delta: float) -> void: 37 | if owner.is_on_floor(): 38 | if _parent.get_move_direction().x == 0.0: 39 | _state_machine.transition_to("Move/Idle") 40 | else: 41 | _state_machine.transition_to("Move/Air") 42 | _parent.physics_process(delta) 43 | 44 | 45 | func enter(msg: Dictionary = {}) -> void: 46 | _parent.enter(msg) 47 | if not Utils.is_equal_approx(_parent.max_speed.x, _parent.max_speed_default.x): 48 | slow_starter.start() 49 | 50 | 51 | func exit() -> void: 52 | _parent.exit() 53 | -------------------------------------------------------------------------------- /game/src/Player/States/Spawn.gd: -------------------------------------------------------------------------------- 1 | extends State 2 | # Takes control away from the player and makes the character spawn 3 | 4 | 5 | func _on_Player_animation_finished(_anim_name: String) -> void: 6 | _state_machine.transition_to('Move/Idle') 7 | 8 | 9 | func enter(msg: Dictionary = {}) -> void: 10 | assert("last_checkpoint" in msg) 11 | owner.stats.set_invulnerable_for_seconds(2) 12 | owner.global_position = msg.last_checkpoint.global_position 13 | owner.is_active = false 14 | owner.camera_rig.is_active = false 15 | owner.skin.play("spawn") 16 | owner.skin.connect("animation_finished", self, "_on_Player_animation_finished") 17 | 18 | 19 | func exit() -> void: 20 | owner.is_active = true 21 | owner.camera_rig.is_active = true 22 | owner.skin.disconnect("animation_finished", self, "_on_Player_animation_finished") 23 | -------------------------------------------------------------------------------- /game/src/Player/States/Stagger.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends State 3 | 4 | 5 | onready var duration: Timer = $Duration 6 | 7 | 8 | func _get_configuration_warning() -> String: 9 | return "" if $Duration else "%s requires a Timer child named Duration" % name 10 | 11 | 12 | func enter(_msg: Dictionary = {}): 13 | duration.start() 14 | yield(duration, "timeout") 15 | _state_machine.transition_to("Move/Idle") 16 | -------------------------------------------------------------------------------- /game/src/Player/States/Wall.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends State 3 | # Handles wall movement: sliding against the wall and wall jump 4 | 5 | 6 | export var slide_acceleration := 1600.0 7 | export var max_slide_speed := 400.0 8 | export (float, 0.0, 1.0) var friction_factor := 0.15 9 | 10 | export var jump_strength := Vector2(500.0, 400.0) 11 | var _wall_normal := -1 12 | var _velocity := Vector2.ZERO 13 | 14 | 15 | func unhandled_input(event: InputEvent) -> void: 16 | if event.is_action_pressed("jump"): 17 | jump() 18 | 19 | 20 | func physics_process(delta: float) -> void: 21 | if _velocity.y > max_slide_speed: 22 | _velocity.y = lerp(_velocity.y, max_slide_speed, friction_factor) 23 | else: 24 | _velocity.y += slide_acceleration * delta 25 | _velocity.y = clamp(_velocity.y, -max_slide_speed, max_slide_speed) 26 | _velocity = owner.move_and_slide(_velocity, owner.FLOOR_NORMAL) 27 | 28 | if owner.is_on_floor(): 29 | _state_machine.transition_to("Move/Idle") 30 | 31 | var is_moving_away_from_wall := sign(_parent.get_move_direction().x) == sign(_wall_normal) 32 | if is_moving_away_from_wall or not owner.ledge_wall_detector.is_against_wall(): 33 | _state_machine.transition_to("Move/Air", {"velocity":_velocity}) 34 | 35 | if owner.ledge_wall_detector.is_against_ledge(): 36 | _state_machine.transition_to("Ledge", {move_state = _parent}) 37 | 38 | 39 | func enter(msg: Dictionary = {}) -> void: 40 | _parent.enter(msg) 41 | 42 | _wall_normal = msg.normal 43 | _velocity.y = clamp(msg.velocity.y, -max_slide_speed, max_slide_speed) 44 | 45 | 46 | func exit() -> void: 47 | _parent.exit() 48 | 49 | 50 | func jump() -> void: 51 | # The direction vector not being normalized is intended 52 | var impulse := Vector2(_wall_normal, -1.0) * jump_strength 53 | var msg := { 54 | velocity = impulse, 55 | wall_jump = true 56 | } 57 | _state_machine.transition_to("Move/Air", msg) 58 | -------------------------------------------------------------------------------- /game/src/UI/LoadingTransition.gd: -------------------------------------------------------------------------------- 1 | extends ColorRect 2 | 3 | signal faded_to_black 4 | 5 | onready var animation_player: AnimationPlayer = $AnimationPlayer 6 | 7 | 8 | func fade_to_black() -> void: 9 | animation_player.play("fade_in") 10 | animation_player.queue("fade_in_extras") 11 | animation_player.queue("loading") 12 | 13 | 14 | func fade_back_in() -> void: 15 | animation_player.clear_queue() 16 | animation_player.play("fade_out") 17 | 18 | 19 | func _on_AnimationPlayer_animation_changed(old_name: String, new_name: String) -> void: 20 | if old_name == "fade_in": 21 | emit_signal("faded_to_black") 22 | -------------------------------------------------------------------------------- /game/src/UI/Title.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://assets/theme/fonts/font_title.tres" type="DynamicFont" id=1] 4 | 5 | 6 | [node name="Title" type="Label"] 7 | anchor_left = 0.5 8 | anchor_top = 0.5 9 | anchor_right = 0.5 10 | anchor_bottom = 0.5 11 | margin_left = -376.0 12 | margin_top = -18.0 13 | margin_right = 376.0 14 | margin_bottom = 18.0 15 | custom_fonts/font = ExtResource( 1 ) 16 | text = "Color palette" 17 | align = 1 18 | valign = 1 19 | uppercase = true 20 | 21 | -------------------------------------------------------------------------------- /game/src/UI/debug/DebugDock.gd: -------------------------------------------------------------------------------- 1 | extends MarginContainer 2 | # Contains UI widgets that display debug info about the game world 3 | 4 | func _input(event: InputEvent) -> void: 5 | if event.is_action_pressed('toggle_debug_menu'): 6 | visible = not visible 7 | accept_event() 8 | -------------------------------------------------------------------------------- /game/src/UI/debug/DebugPanel.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Control 3 | # Displays the values of properties of a given node 4 | # You can directly change the `properties` property to display multiple values from the `reference` node 5 | # E.g. properties = PoolStringArray(['speed', 'position', 'modulate']) 6 | 7 | onready var _container: VBoxContainer = $VBoxContainer/MarginContainer/VBoxContainer 8 | onready var _title: Label = $VBoxContainer/ReferenceName 9 | 10 | onready var reference: Node = get_node(reference_path) setget set_reference 11 | 12 | export var reference_path: NodePath 13 | export var properties: PoolStringArray setget set_properties 14 | 15 | 16 | func _ready() -> void: 17 | if not reference: 18 | return 19 | _setup() 20 | 21 | 22 | func _process(_delta) -> void: 23 | _update() 24 | 25 | 26 | func _setup() -> void: 27 | _clear() 28 | _title.text = reference.name 29 | for property in properties: 30 | track(property) 31 | 32 | 33 | func _get_configuration_warning() -> String: 34 | return "" if not reference_path.is_empty() else "Reference Path should not be empty." 35 | 36 | 37 | func track(property: String) -> void: 38 | var label := Label.new() 39 | label.autowrap = true 40 | label.name = property.capitalize() 41 | _container.add_child(label) 42 | if not property in properties: 43 | properties.append(property) 44 | 45 | 46 | func _clear() -> void: 47 | for property_label in _container.get_children(): 48 | property_label.queue_free() 49 | 50 | 51 | func _update() -> void: 52 | if Engine.editor_hint: 53 | return 54 | var search_array: Array = properties 55 | for property in properties: 56 | var label: Label = _container.get_child(search_array.find(property)) 57 | var value = reference.get(property) 58 | 59 | var text := "" 60 | if value is Vector2: 61 | text = "(%01d %01d)" % [value.x, value.y] 62 | else: 63 | text = str(value) 64 | label.text = "%s: %s" % [property.capitalize(), text] 65 | 66 | 67 | func set_properties(value: PoolStringArray) -> void: 68 | properties = value 69 | if not reference: 70 | return 71 | _setup() 72 | 73 | 74 | func set_reference(value: Node) -> void: 75 | reference = value 76 | if reference: 77 | _setup() 78 | -------------------------------------------------------------------------------- /game/src/UI/debug/DebugPanel.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://assets/theme/gdquest.theme" type="Theme" id=1] 4 | [ext_resource path="res://src/UI/debug/DebugPanel.gd" type="Script" id=2] 5 | [ext_resource path="res://assets/theme/fonts/font_title.tres" type="DynamicFont" id=3] 6 | 7 | [node name="DebugPanel" type="PanelContainer"] 8 | anchor_right = 1.0 9 | anchor_bottom = 1.0 10 | margin_right = -1630.0 11 | margin_bottom = -998.0 12 | theme = ExtResource( 1 ) 13 | script = ExtResource( 2 ) 14 | reference_path = NodePath("") 15 | properties = PoolStringArray( ) 16 | 17 | [node name="VBoxContainer" type="VBoxContainer" parent="."] 18 | margin_right = 290.0 19 | margin_bottom = 102.0 20 | 21 | [node name="ReferenceName" type="Label" parent="VBoxContainer"] 22 | margin_right = 290.0 23 | margin_bottom = 54.0 24 | rect_min_size = Vector2( 0, 54 ) 25 | custom_fonts/font = ExtResource( 3 ) 26 | text = "DebugPanel" 27 | align = 1 28 | valign = 1 29 | 30 | [node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"] 31 | margin_top = 62.0 32 | margin_right = 290.0 33 | margin_bottom = 102.0 34 | size_flags_horizontal = 3 35 | size_flags_vertical = 3 36 | custom_constants/margin_right = 20 37 | custom_constants/margin_top = 20 38 | custom_constants/margin_left = 20 39 | custom_constants/margin_bottom = 20 40 | 41 | [node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/MarginContainer"] 42 | margin_left = 20.0 43 | margin_top = 20.0 44 | margin_right = 270.0 45 | margin_bottom = 20.0 46 | size_flags_horizontal = 3 47 | size_flags_vertical = 3 48 | -------------------------------------------------------------------------------- /img/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/img/banner.png -------------------------------------------------------------------------------- /img/flinthook-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/img/flinthook-4.png -------------------------------------------------------------------------------- /img/hollow-knight-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/img/hollow-knight-3.png -------------------------------------------------------------------------------- /img/momodora-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/img/momodora-2.png -------------------------------------------------------------------------------- /img/ori-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/img/ori-2.png -------------------------------------------------------------------------------- /img/prototypes/hook!-prototype-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/img/prototypes/hook!-prototype-3.png -------------------------------------------------------------------------------- /img/prototypes/level-design-loops-illustration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdquest-demos/godot-platformer-2d/0ea6cb6056dceeba4b1eeca73bddd9e037e0a7ef/img/prototypes/level-design-loops-illustration.png --------------------------------------------------------------------------------