├── .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
--------------------------------------------------------------------------------