From cf40d21fa48e5b054b32f58bde3da6a393895ab9 Mon Sep 17 00:00:00 2001 From: Trey M Date: Fri, 13 Oct 2023 00:09:48 -0500 Subject: [PATCH] 2D Movment, Ammo items and ui --- assets/fonts/pixel_code/LICENSE.txt | 93 +++++++++++++++ assets/fonts/pixel_code/PixelCode.otf | Bin 0 -> 20844 bytes assets/fonts/pixel_code/PixelCode.otf.import | 33 ++++++ assets/images/projectile/bullet.xcf | Bin 687 -> 0 bytes assets/images/projectile/phaser/README.txt | 1 + assets/images/projectile/phaser/red_01.png | Bin 0 -> 8780 bytes .../red_01.png.import} | 8 +- assets/images/projectile/phaser/red_02.png | Bin 0 -> 14254 bytes .../projectile/phaser/red_02.png.import | 34 ++++++ assets/images/projectile/projectile.webp | Bin 774 -> 0 bytes assets/shaders/effects/blur.tres | 2 +- .../{ => ui}/health_bar/health_bar.gdshader | 0 .../{ => ui}/health_bar/health_bar.tres | 2 +- .../ui/weapon_status/weapon_status.gdshader | 7 ++ .../ui/weapon_status/weapon_status.tres | 7 ++ assets/themes/main.tres | 6 + project.godot | 42 +++++-- scenes/items/ammo/ammo.gd | 19 +++ scenes/items/ammo/ammo_item.gd | 9 ++ scenes/items/ammo/ammo_item.tscn | 11 ++ .../items/ammo/missile_ammo/missile_ammo.gd | 1 + .../items/ammo/missile_ammo/missile_ammo.tscn | 22 ++++ scenes/items/ammo/phaser_ammo/phaser_ammo.gd | 5 + .../items/ammo/phaser_ammo/phaser_ammo.tscn | 22 ++++ scenes/items/helth/health.tscn | 1 - scenes/items/item.gd | 31 ++--- scenes/items/item.tscn | 2 - scenes/main.gd | 39 +++++++ scenes/main.tscn | 15 ++- scenes/projectiles/missile/missile.gd | 1 - scenes/projectiles/missile/missile.tscn | 2 + scenes/projectiles/phaser/phaser.gd | 1 + scenes/projectiles/phaser/phaser.tscn | 21 ++++ scenes/projectiles/projectile.gd | 19 ++- scenes/projectiles/projectile.tscn | 14 +-- scenes/ships/enemy/enemy.gd | 30 ++--- scenes/ships/enemy/enemy.tscn | 17 ++- scenes/ships/hardpoint.gd | 18 +++ scenes/ships/hardpoint.tscn | 6 + scenes/ships/player/camera.gd | 33 ------ scenes/ships/player/player.gd | 21 +++- scenes/ships/player/player.tscn | 16 ++- scenes/ships/ship.gd | 110 ++++++++++++++---- scenes/ships/ship.tscn | 16 ++- scenes/ui/canvas_layer.tscn | 10 +- scenes/ui/gameover_menu/game_over_menu.gd | 7 ++ scenes/ui/gameover_menu/game_over_menu.tscn | 68 +++++++++++ scenes/ui/ingame_menu/in_game_menu.gd | 53 +++++++++ scenes/ui/ingame_menu/in_game_menu.tscn | 40 +++++-- scenes/ui/main_menu/main_menu.tscn | 18 ++- scenes/ui/pause_menu/pause_menu.gd | 1 - .../ui/ui_elements/health_bar/health_bar.tscn | 4 +- scenes/world/camera.gd | 24 ++++ scenes/world/world.gd | 3 + scenes/world/world.tscn | 9 +- src/enemy_ai/enemy_ai.gd | 17 +++ src/enemy_manager.gd | 2 +- src/event_bus.gd | 2 + src/game_manager.gd | 36 ------ src/game_state.gd | 6 + src/ui.gd | 23 ++-- 61 files changed, 831 insertions(+), 229 deletions(-) create mode 100644 assets/fonts/pixel_code/LICENSE.txt create mode 100644 assets/fonts/pixel_code/PixelCode.otf create mode 100644 assets/fonts/pixel_code/PixelCode.otf.import delete mode 100644 assets/images/projectile/bullet.xcf create mode 100644 assets/images/projectile/phaser/README.txt create mode 100644 assets/images/projectile/phaser/red_01.png rename assets/images/projectile/{projectile.webp.import => phaser/red_01.png.import} (67%) create mode 100644 assets/images/projectile/phaser/red_02.png create mode 100644 assets/images/projectile/phaser/red_02.png.import delete mode 100644 assets/images/projectile/projectile.webp rename assets/shaders/{ => ui}/health_bar/health_bar.gdshader (100%) rename assets/shaders/{ => ui}/health_bar/health_bar.tres (75%) create mode 100644 assets/shaders/ui/weapon_status/weapon_status.gdshader create mode 100644 assets/shaders/ui/weapon_status/weapon_status.tres create mode 100644 assets/themes/main.tres create mode 100644 scenes/items/ammo/ammo.gd create mode 100644 scenes/items/ammo/ammo_item.gd create mode 100644 scenes/items/ammo/ammo_item.tscn create mode 100644 scenes/items/ammo/missile_ammo/missile_ammo.gd create mode 100644 scenes/items/ammo/missile_ammo/missile_ammo.tscn create mode 100644 scenes/items/ammo/phaser_ammo/phaser_ammo.gd create mode 100644 scenes/items/ammo/phaser_ammo/phaser_ammo.tscn create mode 100644 scenes/main.gd create mode 100644 scenes/projectiles/phaser/phaser.gd create mode 100644 scenes/projectiles/phaser/phaser.tscn create mode 100644 scenes/ships/hardpoint.gd create mode 100644 scenes/ships/hardpoint.tscn delete mode 100644 scenes/ships/player/camera.gd create mode 100644 scenes/ui/gameover_menu/game_over_menu.gd create mode 100644 scenes/ui/gameover_menu/game_over_menu.tscn create mode 100644 scenes/world/camera.gd create mode 100644 src/enemy_ai/enemy_ai.gd delete mode 100644 src/game_manager.gd diff --git a/assets/fonts/pixel_code/LICENSE.txt b/assets/fonts/pixel_code/LICENSE.txt new file mode 100644 index 0000000..cb90aaf --- /dev/null +++ b/assets/fonts/pixel_code/LICENSE.txt @@ -0,0 +1,93 @@ +Copyright (c) 2022, Qwerasd + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file diff --git a/assets/fonts/pixel_code/PixelCode.otf b/assets/fonts/pixel_code/PixelCode.otf new file mode 100644 index 0000000000000000000000000000000000000000..a0aaa0d2a403886bf823d66c41eab2ac8ca9b325 GIT binary patch literal 20844 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/pixel_code/PixelCode.otf.import b/assets/fonts/pixel_code/PixelCode.otf.import new file mode 100644 index 0000000..5a4d9e5 --- /dev/null +++ b/assets/fonts/pixel_code/PixelCode.otf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://drxtf8blqxxh1" +path="res://.godot/imported/PixelCode.otf-0abd59be39299b913acc138c1b8f037e.fontdata" + +[deps] + +source_file="res://assets/fonts/pixel_code/PixelCode.otf" +dest_files=["res://.godot/imported/PixelCode.otf-0abd59be39299b913acc138c1b8f037e.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/assets/images/projectile/bullet.xcf b/assets/images/projectile/bullet.xcf deleted file mode 100644 index c3905e0f8a07cd52329cb01653414f2b2b14e387..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 687 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 QivR!s07*qoM6N<$g1D`IfB*mh literal 0 HcmV?d00001 diff --git a/assets/images/projectile/phaser/red_02.png.import b/assets/images/projectile/phaser/red_02.png.import new file mode 100644 index 0000000..1ed57e3 --- /dev/null +++ b/assets/images/projectile/phaser/red_02.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bo5sa3pbjcufm" +path="res://.godot/imported/red_02.png-fd58fca7fae36efb80352cbfdbcff0ae.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/images/projectile/phaser/red_02.png" +dest_files=["res://.godot/imported/red_02.png-fd58fca7fae36efb80352cbfdbcff0ae.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/assets/images/projectile/projectile.webp b/assets/images/projectile/projectile.webp deleted file mode 100644 index 5b2c9966c0590e7d1a6ae262c4ecdd750584e1d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 774 zcmaKqO=uHQ6otP`TPm@oh*^lZ7$kL}Er^N)+|)!{0u_c>YpO0xCQVw%Bw>=`Mrs#= z+k%Kc_!D)}jceTsx)lV$DoD58xM@WZBz-+I5z|HEg*WeT?m2hfz3*kxb+y&RT_R~B57x5uGszSMA=J3mSZ`$UOVeJlYx5o z`1M&z#i!Ar*6y0F>|kNG?n1Vz78n-CE)XI z{5`;>5Qk00{HaVzy~V`ar5!lG1G{s+C(S8o;svjw`c}4$3jw|vV6VKOvxO0KI51ne zbWwX;>kt#9xqzb?+`cw6cCt4&D!6u;cz;?H2{HoSh{;qv$4 z%^f)KwU+a9;eUGcwTea9T!FzEJ)Dmaz1p6eazUP#t1zWmO@4rfkKjm2wXb#QdyfqN z=JsFB-}&j~+6kGCTRe#z?IXBQhB?C`i%Yk#_ARkxs?)}7zT0S}*Ud=EnBPwyuN_{w IXErpgZQ$aP1ONa4 diff --git a/assets/shaders/effects/blur.tres b/assets/shaders/effects/blur.tres index b3b66d0..8b3336f 100644 --- a/assets/shaders/effects/blur.tres +++ b/assets/shaders/effects/blur.tres @@ -4,4 +4,4 @@ [resource] shader = ExtResource("1_b6fhc") -shader_parameter/amount = null +shader_parameter/amount = 3.0 diff --git a/assets/shaders/health_bar/health_bar.gdshader b/assets/shaders/ui/health_bar/health_bar.gdshader similarity index 100% rename from assets/shaders/health_bar/health_bar.gdshader rename to assets/shaders/ui/health_bar/health_bar.gdshader diff --git a/assets/shaders/health_bar/health_bar.tres b/assets/shaders/ui/health_bar/health_bar.tres similarity index 75% rename from assets/shaders/health_bar/health_bar.tres rename to assets/shaders/ui/health_bar/health_bar.tres index 19d9650..efc203a 100644 --- a/assets/shaders/health_bar/health_bar.tres +++ b/assets/shaders/ui/health_bar/health_bar.tres @@ -1,6 +1,6 @@ [gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://gybsnxxx6x3f"] -[ext_resource type="Shader" path="res://assets/shaders/health_bar/health_bar.gdshader" id="1_cp53s"] +[ext_resource type="Shader" path="res://assets/shaders/ui/health_bar/health_bar.gdshader" id="1_cp53s"] [resource] render_priority = 0 diff --git a/assets/shaders/ui/weapon_status/weapon_status.gdshader b/assets/shaders/ui/weapon_status/weapon_status.gdshader new file mode 100644 index 0000000..fbe1944 --- /dev/null +++ b/assets/shaders/ui/weapon_status/weapon_status.gdshader @@ -0,0 +1,7 @@ +shader_type canvas_item; + +uniform float progress; + +void fragment() { + COLOR.rgba = vec4(COLOR.rgba - vec4(0.0, 0.0, 0.0, progress)); +} diff --git a/assets/shaders/ui/weapon_status/weapon_status.tres b/assets/shaders/ui/weapon_status/weapon_status.tres new file mode 100644 index 0000000..a13d1e7 --- /dev/null +++ b/assets/shaders/ui/weapon_status/weapon_status.tres @@ -0,0 +1,7 @@ +[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://bqhu74bk858wj"] + +[ext_resource type="Shader" path="res://assets/shaders/ui/weapon_status/weapon_status.gdshader" id="1_bhft8"] + +[resource] +shader = ExtResource("1_bhft8") +shader_parameter/progress = null diff --git a/assets/themes/main.tres b/assets/themes/main.tres new file mode 100644 index 0000000..2588bfa --- /dev/null +++ b/assets/themes/main.tres @@ -0,0 +1,6 @@ +[gd_resource type="Theme" load_steps=2 format=3 uid="uid://7u3vc13ppdl0"] + +[ext_resource type="FontFile" uid="uid://drxtf8blqxxh1" path="res://assets/fonts/pixel_code/PixelCode.otf" id="1_gmgni"] + +[resource] +default_font = ExtResource("1_gmgni") diff --git a/project.godot b/project.godot index 48b8b7a..7171f21 100644 --- a/project.godot +++ b/project.godot @@ -20,7 +20,6 @@ config/icon="res://icon.svg" UI="*res://src/ui.gd" EventBus="*res://src/event_bus.gd" -GameManager="*res://src/game_manager.gd" GameState="*res://src/game_state.gd" EnemyManager="*res://src/enemy_manager.gd" @@ -31,34 +30,57 @@ window/size/viewport_height=1080 [input] -spaceship_left={ +spaceship_strafe_left={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"echo":false,"script":null) ] } -spaceship_right={ +spaceship_strafe_right={ "deadzone": 0.5, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"echo":false,"script":null) ] } spaceship_fire={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"echo":false,"script":null) , Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":1,"position":Vector2(398, 11),"global_position":Vector2(402, 54),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":5,"axis_value":1.0,"script":null) ] } spaceship_forward={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null) ] } -spaceship_break={ +spaceship_backward={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null) +] +} +spaceship_rotate_left={ +"deadzone": 0.5, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":true,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null) +] +} +spaceship_rotate_right={ +"deadzone": 0.5, +"events": [Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":true,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null) +] +} +spaceship_weapons_up={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"echo":false,"script":null) +] +} +spaceship_weapons_down={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"echo":false,"script":null) ] } diff --git a/scenes/items/ammo/ammo.gd b/scenes/items/ammo/ammo.gd new file mode 100644 index 0000000..415be46 --- /dev/null +++ b/scenes/items/ammo/ammo.gd @@ -0,0 +1,19 @@ +class_name Ammo +extends RefCounted + +var projectile_scene: PackedScene +var projectile_type: Projectile.ProjectileType +var projectile_count: int + +var item_icon: Texture2D + +func _init(icon: Texture2D, scene: PackedScene, type: Projectile.ProjectileType, count: int) -> void: + self.item_icon = icon + self.projectile_scene = scene + self.projectile_type = type + self.projectile_count = count + +func consume_ammo(): + if projectile_count < 0: + return + projectile_count -= 1 diff --git a/scenes/items/ammo/ammo_item.gd b/scenes/items/ammo/ammo_item.gd new file mode 100644 index 0000000..6e0a962 --- /dev/null +++ b/scenes/items/ammo/ammo_item.gd @@ -0,0 +1,9 @@ +class_name AmmoItem +extends Item + +@export var projectile_scene: PackedScene +@export var projectile_type: Projectile.ProjectileType +@export var projectile_count: int + +func get_ammo() -> Ammo: + return Ammo.new(item_icon, projectile_scene, projectile_type, projectile_count) diff --git a/scenes/items/ammo/ammo_item.tscn b/scenes/items/ammo/ammo_item.tscn new file mode 100644 index 0000000..bbf8be9 --- /dev/null +++ b/scenes/items/ammo/ammo_item.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=3 format=3 uid="uid://dlwb16yqdtgtb"] + +[ext_resource type="Script" path="res://scenes/items/ammo/ammo_item.gd" id="1_44jtn"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_utth4"] + +[node name="AmmoItem" type="Area2D"] +script = ExtResource("1_44jtn") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("RectangleShape2D_utth4") diff --git a/scenes/items/ammo/missile_ammo/missile_ammo.gd b/scenes/items/ammo/missile_ammo/missile_ammo.gd new file mode 100644 index 0000000..dd60997 --- /dev/null +++ b/scenes/items/ammo/missile_ammo/missile_ammo.gd @@ -0,0 +1 @@ +extends AmmoItem diff --git a/scenes/items/ammo/missile_ammo/missile_ammo.tscn b/scenes/items/ammo/missile_ammo/missile_ammo.tscn new file mode 100644 index 0000000..ab494e9 --- /dev/null +++ b/scenes/items/ammo/missile_ammo/missile_ammo.tscn @@ -0,0 +1,22 @@ +[gd_scene load_steps=5 format=3 uid="uid://c5gppqo8phv1n"] + +[ext_resource type="Script" path="res://scenes/items/ammo/missile_ammo/missile_ammo.gd" id="1_jjift"] +[ext_resource type="PackedScene" uid="uid://be81tbuwykao8" path="res://scenes/projectiles/missile/missile.tscn" id="2_kuvrw"] +[ext_resource type="Texture2D" uid="uid://d3jeifgalw07w" path="res://assets/images/projectile/missile/missile0.webp" id="3_jvm7e"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_x6ijy"] +size = Vector2(60, 10) + +[node name="MissileAmmo" type="Area2D"] +script = ExtResource("1_jjift") +projectile_scene = ExtResource("2_kuvrw") +projectile_type = 1 +projectile_count = 5 +item_icon = ExtResource("3_jvm7e") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(6, 0) +shape = SubResource("RectangleShape2D_x6ijy") + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture = ExtResource("3_jvm7e") diff --git a/scenes/items/ammo/phaser_ammo/phaser_ammo.gd b/scenes/items/ammo/phaser_ammo/phaser_ammo.gd new file mode 100644 index 0000000..80fd846 --- /dev/null +++ b/scenes/items/ammo/phaser_ammo/phaser_ammo.gd @@ -0,0 +1,5 @@ +extends AmmoItem + +func get_ammo() -> Ammo: + var ammo = Ammo.new(item_icon, projectile_scene, projectile_type, projectile_count) + return ammo diff --git a/scenes/items/ammo/phaser_ammo/phaser_ammo.tscn b/scenes/items/ammo/phaser_ammo/phaser_ammo.tscn new file mode 100644 index 0000000..fcfcb83 --- /dev/null +++ b/scenes/items/ammo/phaser_ammo/phaser_ammo.tscn @@ -0,0 +1,22 @@ +[gd_scene load_steps=5 format=3 uid="uid://3muskpfron44"] + +[ext_resource type="Script" path="res://scenes/items/ammo/phaser_ammo/phaser_ammo.gd" id="1_muxvb"] +[ext_resource type="PackedScene" uid="uid://ceyv85t3m1p4r" path="res://scenes/projectiles/phaser/phaser.tscn" id="2_fbv8e"] +[ext_resource type="Texture2D" uid="uid://bo5sa3pbjcufm" path="res://assets/images/projectile/phaser/red_02.png" id="4_yq24i"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_opxcc"] +size = Vector2(17, 156) + +[node name="PhaserAmmo" type="Area2D"] +script = ExtResource("1_muxvb") +projectile_scene = ExtResource("2_fbv8e") +projectile_count = -1 +item_icon = ExtResource("4_yq24i") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(-0.5, 9) +shape = SubResource("RectangleShape2D_opxcc") + +[node name="Sprite2D" type="Sprite2D" parent="."] +rotation = 4.71239 +texture = ExtResource("4_yq24i") diff --git a/scenes/items/helth/health.tscn b/scenes/items/helth/health.tscn index 57907a6..774a0a5 100644 --- a/scenes/items/helth/health.tscn +++ b/scenes/items/helth/health.tscn @@ -8,7 +8,6 @@ size = Vector2(32, 32) [node name="Health" type="Area2D"] script = ExtResource("1_8xorb") -item_weight = 150 item_type = 1 item_icon = ExtResource("2_nw4yg") item_size = Vector2i(32, 32) diff --git a/scenes/items/item.gd b/scenes/items/item.gd index 67214ad..f95d117 100644 --- a/scenes/items/item.gd +++ b/scenes/items/item.gd @@ -1,35 +1,24 @@ class_name Item extends Area2D -enum ItemType {PROJECTILE, HEALTH, POWERUP} +enum ItemType {AMMO, HEALTH, POWERUP} + +@export var item_lifespan: int = 30 -@export var item_weight: int = 100 @export var item_type: ItemType @export var item_icon: Texture2D -@export var item_size: Vector2i -var world := GameState.world -var player := GameState.player +var velocity: Vector2 = Vector2.ZERO -var velocity := Vector2.ZERO +var lifespan_timer: Timer func _ready() -> void: - if world == null: - world = await EventBus.world_ready - if player == null: - player = await EventBus.player_ready - body_entered.connect(self._on_body_entered) - -func _physics_process(delta: float) -> void: - if GameState.paused: - return - - if player == null: - return - - velocity = velocity.move_toward(world.gravity_direction * item_weight, world.gravity) - position += velocity * delta + lifespan_timer = Timer.new() + lifespan_timer.one_shot = true + add_child(lifespan_timer) + lifespan_timer.start(item_lifespan) + lifespan_timer.timeout.connect(self.queue_free) func _on_body_entered(body: Node2D) -> void: if body is Player: diff --git a/scenes/items/item.tscn b/scenes/items/item.tscn index 0a526a1..f15d81c 100644 --- a/scenes/items/item.tscn +++ b/scenes/items/item.tscn @@ -6,8 +6,6 @@ [node name="Item" type="Area2D"] script = ExtResource("1_xwvny") -item_weight = 500 -item_type = 1 [node name="CollisionShape2D" type="CollisionShape2D" parent="."] shape = SubResource("RectangleShape2D_uu2c8") diff --git a/scenes/main.gd b/scenes/main.gd new file mode 100644 index 0000000..7762c58 --- /dev/null +++ b/scenes/main.gd @@ -0,0 +1,39 @@ +extends Node2D + +@export var world_scene: PackedScene +var world: World + +@export var player_scene: PackedScene +var player: Player + +@export var ui_canvas_layer: CanvasLayer + +func _ready() -> void: + EventBus.start_game.connect(self._on_start_game) + EventBus.quit_game.connect(self._on_quit_game) + EventBus.game_over.connect(self._on_game_over) + EventBus.world_ready.connect(self._on_world_ready) + EventBus.reset_game.connect(self._on_reset_game) + UI.set_canvas_layer(ui_canvas_layer) + +func _on_start_game(): + world = world_scene.instantiate() + add_child(world) + UI.change_menu(UI.Menu.INGAME) + +func _on_quit_game(): + world.queue_free() + UI.change_menu(UI.Menu.MAIN) + +func _on_reset_game(): + world.queue_free() + world = world_scene.instantiate() + add_child(world) + UI.change_menu(UI.Menu.INGAME) + +func _on_world_ready(_world: World): + player = player_scene.instantiate() + world.add_object(player) + +func _on_game_over(): + UI.change_menu(UI.Menu.GAMEOVER) diff --git a/scenes/main.tscn b/scenes/main.tscn index 8cbc59a..e49b6f1 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -1,3 +1,14 @@ -[gd_scene format=3 uid="uid://cckbbxx52a1s2"] +[gd_scene load_steps=5 format=3 uid="uid://cckbbxx52a1s2"] -[node name="Main" type="Node2D"] +[ext_resource type="Script" path="res://scenes/main.gd" id="1_rxf7e"] +[ext_resource type="PackedScene" uid="uid://b8106ayuswq6d" path="res://scenes/world/world.tscn" id="2_ge7ev"] +[ext_resource type="PackedScene" uid="uid://tv78msbkxshj" path="res://scenes/ui/canvas_layer.tscn" id="2_sx2r0"] +[ext_resource type="PackedScene" uid="uid://lsk2vj0l0b08" path="res://scenes/ships/player/player.tscn" id="3_10f6b"] + +[node name="Main" type="Node2D" node_paths=PackedStringArray("ui_canvas_layer")] +script = ExtResource("1_rxf7e") +world_scene = ExtResource("2_ge7ev") +player_scene = ExtResource("3_10f6b") +ui_canvas_layer = NodePath("UILayer") + +[node name="UILayer" parent="." instance=ExtResource("2_sx2r0")] diff --git a/scenes/projectiles/missile/missile.gd b/scenes/projectiles/missile/missile.gd index a0d3db4..61c772e 100644 --- a/scenes/projectiles/missile/missile.gd +++ b/scenes/projectiles/missile/missile.gd @@ -6,7 +6,6 @@ extends Projectile func _ready(): animated_sprite.play("startup") - func _on_animated_sprite_animation_finished() -> void: if animated_sprite.animation == "startup": animated_sprite.play("running") diff --git a/scenes/projectiles/missile/missile.tscn b/scenes/projectiles/missile/missile.tscn index 3eb7481..6479648 100644 --- a/scenes/projectiles/missile/missile.tscn +++ b/scenes/projectiles/missile/missile.tscn @@ -62,7 +62,9 @@ scale = Vector2(0.75, 0.75) script = ExtResource("1_ukt6e") animated_sprite = NodePath("AnimatedSprite") size = Vector2i(12, 60) +life_span = 20 speed = 100 +projectile_type = 1 [node name="CollisionShape2D" type="CollisionShape2D" parent="."] position = Vector2(0, -6) diff --git a/scenes/projectiles/phaser/phaser.gd b/scenes/projectiles/phaser/phaser.gd new file mode 100644 index 0000000..ef9bdc3 --- /dev/null +++ b/scenes/projectiles/phaser/phaser.gd @@ -0,0 +1 @@ +extends Projectile diff --git a/scenes/projectiles/phaser/phaser.tscn b/scenes/projectiles/phaser/phaser.tscn new file mode 100644 index 0000000..4542027 --- /dev/null +++ b/scenes/projectiles/phaser/phaser.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=4 format=3 uid="uid://ceyv85t3m1p4r"] + +[ext_resource type="Script" path="res://scenes/projectiles/phaser/phaser.gd" id="1_4enjs"] +[ext_resource type="Texture2D" uid="uid://hvuku7lpqby1" path="res://assets/images/projectile/phaser/red_01.png" id="2_ykxrp"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_e2p83"] +size = Vector2(18, 32) + +[node name="Phaser" type="Area2D"] +script = ExtResource("1_4enjs") +damage = 25 +speed = 1000 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(0, 1) +shape = SubResource("RectangleShape2D_e2p83") + +[node name="Sprite" type="Sprite2D" parent="."] +modulate = Color(1.5, 1.5, 1.5, 1) +rotation = 4.71239 +texture = ExtResource("2_ykxrp") diff --git a/scenes/projectiles/projectile.gd b/scenes/projectiles/projectile.gd index 0731798..050e299 100644 --- a/scenes/projectiles/projectile.gd +++ b/scenes/projectiles/projectile.gd @@ -1,23 +1,25 @@ class_name Projectile extends Area2D +enum ProjectileType {PHASER, MISSILE} + @export var size: Vector2i = Vector2i(5, 20) @export var damage: int = 50 @export var life_span: int = 5 @export var speed: int = 500 +@export var cooldown: int = 100 +@export var projectile_type: ProjectileType var velocity: Vector2 = Vector2.ZERO -var exited: bool = false - +var origin: Ship func _ready() -> void: var timer = Timer.new() timer.timeout.connect(self._on_timer_timeout) + timer.one_shot = true add_child(timer) - timer.start() - - await body_exited - exited = true + timer.start(life_span) + body_entered.connect(self._on_body_entered) func _physics_process(delta: float) -> void: if GameState.paused: @@ -27,10 +29,7 @@ func _physics_process(delta: float) -> void: position += velocity * delta func _on_body_entered(body: Node2D) -> void: - if not exited: - return - - if body is Ship: + if body is Ship && body != origin: body.apply_damage(damage) queue_free() diff --git a/scenes/projectiles/projectile.tscn b/scenes/projectiles/projectile.tscn index 9eaef72..4198e64 100644 --- a/scenes/projectiles/projectile.tscn +++ b/scenes/projectiles/projectile.tscn @@ -1,23 +1,15 @@ -[gd_scene load_steps=4 format=3 uid="uid://b8hutg83m0g2j"] +[gd_scene load_steps=3 format=3 uid="uid://b8hutg83m0g2j"] [ext_resource type="Script" path="res://scenes/projectiles/projectile.gd" id="1_4jsq1"] -[ext_resource type="Texture2D" uid="uid://c0si2aupdqaof" path="res://assets/images/projectile/projectile.webp" id="2_yxp5k"] -[sub_resource type="RectangleShape2D" id="RectangleShape2D_2tjep"] -size = Vector2(5, 20) +[sub_resource type="RectangleShape2D" id="RectangleShape2D_0ks1w"] [node name="Projectile" type="Area2D"] z_index = 1 -scale = Vector2(0.5, 2) script = ExtResource("1_4jsq1") -speed = 1000 [node name="CollisionShape2D" type="CollisionShape2D" parent="."] -shape = SubResource("RectangleShape2D_2tjep") - -[node name="Sprite" type="Sprite2D" parent="."] -modulate = Color(1.5, 1.5, 1.5, 1) -texture = ExtResource("2_yxp5k") +shape = SubResource("RectangleShape2D_0ks1w") [connection signal="area_entered" from="." to="." method="_on_area_entered"] [connection signal="body_entered" from="." to="." method="_on_body_entered"] diff --git a/scenes/ships/enemy/enemy.gd b/scenes/ships/enemy/enemy.gd index 00d5978..2458f75 100644 --- a/scenes/ships/enemy/enemy.gd +++ b/scenes/ships/enemy/enemy.gd @@ -1,32 +1,16 @@ class_name Enemy extends Ship -var direction_change_timer: Timer +var ai: EnemyAI -func _ready() -> void: +func _ready(): super._ready() - projectile_cooldown_timer.start(projectile_cooldown*0.001) - projectile_cooldown_timer.timeout.connect(self._on_projectile_cooldown_timeout) - movement_direction = randi_range(-1, 1) - - direction_change_timer = Timer.new() - add_child(direction_change_timer) - direction_change_timer.timeout.connect(self._on_direction_change_timeout) - direction_change_timer.one_shot = false - direction_change_timer.wait_time = 1.0 - direction_change_timer.start() + ai = EnemyAI.new(self) + input = ai.get_input() -func _on_direction_change_timeout() -> void: - if GameState.paused: - return - - movement_direction = randi_range(-1, 1) - -func _on_projectile_cooldown_timeout() -> void: - if GameState.paused: - return - - fire() +func _process(delta: float) -> void: + super._process(delta) + ai.update() func ship_destroyed() -> void: EventBus.enemy_destroyed.emit(self) diff --git a/scenes/ships/enemy/enemy.tscn b/scenes/ships/enemy/enemy.tscn index 94d5334..74fd9bf 100644 --- a/scenes/ships/enemy/enemy.tscn +++ b/scenes/ships/enemy/enemy.tscn @@ -1,19 +1,20 @@ -[gd_scene load_steps=5 format=3 uid="uid://cqfgwqqpwrg51"] +[gd_scene load_steps=6 format=3 uid="uid://cqfgwqqpwrg51"] [ext_resource type="Script" path="res://scenes/ships/enemy/enemy.gd" id="1_w35kq"] [ext_resource type="Texture2D" uid="uid://dua5c2gcufo14" path="res://assets/images/ships/enemy/enemy_ship.webp" id="2_fsep2"] [ext_resource type="PackedScene" uid="uid://de3ymp8rbho6h" path="res://scenes/ui/ui_elements/health_bar/health_bar.tscn" id="3_rgjex"] +[ext_resource type="PackedScene" uid="uid://dsg7rv0et4eym" path="res://scenes/ships/hardpoint.tscn" id="4_asdpv"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_n63kb"] size = Vector2(33, 39) -[node name="Enemy" type="CharacterBody2D" node_paths=PackedStringArray("health_bar", "sprite")] +[node name="Enemy" type="CharacterBody2D" node_paths=PackedStringArray("health_bar", "sprite", "weapon_hardpoints")] z_index = 10 scale = Vector2(2, 2) script = ExtResource("1_w35kq") health_bar = NodePath("HealthBar") sprite = NodePath("Sprite2D") -ship_direction = 1 +weapon_hardpoints = [NodePath("LeftPhaser"), NodePath("RightPhaser"), NodePath("CenterMissile")] [node name="CollisionShape2D" type="CollisionShape2D" parent="."] shape = SubResource("RectangleShape2D_n63kb") @@ -25,3 +26,13 @@ texture = ExtResource("2_fsep2") offset_left = -13.0 offset_top = -22.0 offset_bottom = -20.0 + +[node name="LeftPhaser" parent="." instance=ExtResource("4_asdpv")] +position = Vector2(-8, 0) + +[node name="RightPhaser" parent="." instance=ExtResource("4_asdpv")] +position = Vector2(8, 0) + +[node name="CenterMissile" parent="." instance=ExtResource("4_asdpv")] +position = Vector2(0, 2.5) +hardpoint_type = 1 diff --git a/scenes/ships/hardpoint.gd b/scenes/ships/hardpoint.gd new file mode 100644 index 0000000..2005850 --- /dev/null +++ b/scenes/ships/hardpoint.gd @@ -0,0 +1,18 @@ +class_name Hardpoint +extends Node2D + +@export var hardpoint_type: Projectile.ProjectileType + +func fire(ship: Ship) -> float: + if GameState.paused or GameState.world == null: + return 0 + + var projectile : Projectile = ship.ammo[ship.ammo_index].projectile_scene.instantiate() + if projectile.projectile_type != hardpoint_type: + return 0 + + projectile.position = get_global_transform().origin + projectile.velocity = Vector2(0, -projectile.speed).rotated(ship.rotation) + ship.velocity + projectile.origin = ship + GameState.world.add_object(projectile) + return projectile.cooldown*0.01 diff --git a/scenes/ships/hardpoint.tscn b/scenes/ships/hardpoint.tscn new file mode 100644 index 0000000..32b1ebb --- /dev/null +++ b/scenes/ships/hardpoint.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://dsg7rv0et4eym"] + +[ext_resource type="Script" path="res://scenes/ships/hardpoint.gd" id="1_t6aws"] + +[node name="Hardpoint" type="Node2D"] +script = ExtResource("1_t6aws") diff --git a/scenes/ships/player/camera.gd b/scenes/ships/player/camera.gd deleted file mode 100644 index 7df2727..0000000 --- a/scenes/ships/player/camera.gd +++ /dev/null @@ -1,33 +0,0 @@ -extends Camera2D - -var player: Player - -func _ready(): - EventBus.world_ready.connect(self._on_world_ready) - EventBus.player_ready.connect(self._on_player_ready) - get_viewport().size_changed.connect(update_position) - set_process(false) - -func _on_world_ready(world: World): - limit_left = -int(world.size.x / 2.0) - limit_right = int(world.size.x / 2.0) - -func _on_player_ready(_player: Player): - self.player = _player - set_process(true) - update_position() - -func _physics_process(_delta: float) -> void: - if GameState.paused: - return - - update_position() - -func update_position(): - if player == null: - return - - var viewport_size = get_viewport_rect().size - position.y = (player.position.y - (viewport_size.y/2)) + ((player.size.y*2) + 150) - position.x = player.position.x - diff --git a/scenes/ships/player/player.gd b/scenes/ships/player/player.gd index 7fca19e..cd88a04 100644 --- a/scenes/ships/player/player.gd +++ b/scenes/ships/player/player.gd @@ -5,11 +5,15 @@ func _ready() -> void: super._ready() EventBus.player_ready.emit(self) -func _input(event: InputEvent) -> void: - if event is InputEventKey: - if event.is_action("spaceship_fire"): - fire() - movement_direction = Input.get_axis("spaceship_left", "spaceship_right") +func _input(_event: InputEvent) -> void: + if Input.is_action_just_pressed("spaceship_fire"): + input.fire = true + + input.strafe = Input.get_axis("spaceship_strafe_left", "spaceship_strafe_right") + input.thrust = Input.get_axis("spaceship_forward", "spaceship_backward") + input.yaw = Input.get_axis("spaceship_rotate_left", "spaceship_rotate_right") + input.weapons_up = Input.is_action_pressed("spaceship_weapons_up") + input.weapons_down = Input.is_action_pressed("spaceship_weapons_down") func pickup_item(item: Item) -> void: match item.item_type: @@ -17,3 +21,10 @@ func pickup_item(item: Item) -> void: health += item.heal_amount if health > max_health: health = max_health + + item.ItemType.AMMO: + ammo.append(item.get_ammo()) + +func ship_destroyed() -> void: + EventBus.game_over.emit() + queue_free() diff --git a/scenes/ships/player/player.tscn b/scenes/ships/player/player.tscn index 2a4f7b4..e7f28ca 100644 --- a/scenes/ships/player/player.tscn +++ b/scenes/ships/player/player.tscn @@ -1,18 +1,20 @@ -[gd_scene load_steps=5 format=3 uid="uid://lsk2vj0l0b08"] +[gd_scene load_steps=6 format=3 uid="uid://lsk2vj0l0b08"] [ext_resource type="Script" path="res://scenes/ships/player/player.gd" id="1_mugof"] [ext_resource type="Texture2D" uid="uid://tmwnlgiyrk1f" path="res://assets/images/ships/player/player_ship.webp" id="2_l44dm"] [ext_resource type="PackedScene" uid="uid://de3ymp8rbho6h" path="res://scenes/ui/ui_elements/health_bar/health_bar.tscn" id="3_o1qvl"] +[ext_resource type="PackedScene" uid="uid://dsg7rv0et4eym" path="res://scenes/ships/hardpoint.tscn" id="4_jjf2r"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_tana0"] size = Vector2(43, 33) -[node name="Player" type="CharacterBody2D" node_paths=PackedStringArray("health_bar", "sprite")] +[node name="Player" type="CharacterBody2D" node_paths=PackedStringArray("health_bar", "sprite", "weapon_hardpoints")] z_index = 10 scale = Vector2(2, 2) script = ExtResource("1_mugof") health_bar = NodePath("HealthBar") sprite = NodePath("Sprite2D") +weapon_hardpoints = [NodePath("LeftPhaser"), NodePath("RighPhaser"), NodePath("CenterMissile")] [node name="Sprite2D" type="Sprite2D" parent="."] texture = ExtResource("2_l44dm") @@ -24,3 +26,13 @@ shape = SubResource("RectangleShape2D_tana0") offset_left = -13.0 offset_top = 16.8 offset_bottom = 18.8 + +[node name="LeftPhaser" parent="." instance=ExtResource("4_jjf2r")] +position = Vector2(-6.4, -4.76837e-07) + +[node name="RighPhaser" parent="." instance=ExtResource("4_jjf2r")] +position = Vector2(6, 0) + +[node name="CenterMissile" parent="." instance=ExtResource("4_jjf2r")] +position = Vector2(0, 8.4) +hardpoint_type = 1 diff --git a/scenes/ships/ship.gd b/scenes/ships/ship.gd index d13f5c9..c19a908 100644 --- a/scenes/ships/ship.gd +++ b/scenes/ships/ship.gd @@ -3,23 +3,49 @@ extends CharacterBody2D @export var size: Vector2i = Vector2i(43, 33) -@export var projectile_scene := preload("res://scenes/projectiles/projectile.tscn") +@export var default_ammo_scene := preload("res://scenes/projectiles/phaser/phaser.tscn") +@export var default_ammo_icon := preload("res://assets/images/projectile/phaser/red_02.png") @export var health_bar: HealthBar @export var sprite: Sprite2D -@export var max_speed: int = 500 @export var max_health: int = 100 +@export var max_speed: int = 200 +@export var max_strafe_speed: int = 100 @export var friction: int = 10 @export var acceleration: int = 15 +@export var yaw_acceleration: float = 1 +@export var yaw_max_speed: float = 4.0 -@export var ship_direction: int = -1 -@export var projectile_cooldown: int = 500 - -var projectile_cooldown_timer: Timer +@export var weapon_hardpoints: Array[Node2D] -var movement_direction: float = 0 +signal damage_taken(current_health: int, max_health: int) +signal weapon_fired(ammo: Ammo, cooldown: float, cooldown_timer: Timer) +signal ammo_changed(ammo: Ammo) + +class ShipInput: + var fire: bool = false + var weapons_up: bool = false + var weapons_down: bool = false + + var thrust: float + var yaw: float + var strafe: float + +var input: ShipInput = ShipInput.new() + +var ammo: Array[Ammo] +var ammo_index := 0 : + set(value): + if value >= ammo.size(): + ammo_index = 0 + elif value < 0: + ammo_index = ammo.size() - 1 + else: + ammo_index = value + get: + return ammo_index var health: int = max_health : set(value): @@ -37,41 +63,75 @@ var health: int = max_health : get: return health -signal damage_taken(current_health: int, max_health: int) +var rotation_velocity: float = 0 +var weapon_cooldown: Timer +var last_hardpoint_index := 0 func _ready() -> void: + weapon_cooldown = Timer.new() + weapon_cooldown.one_shot = true + add_child(weapon_cooldown) + health = max_health - projectile_cooldown_timer = Timer.new() - projectile_cooldown_timer.one_shot = true - add_child(projectile_cooldown_timer) + var default_ammo: Ammo = Ammo.new(default_ammo_icon, default_ammo_scene, Projectile.ProjectileType.PHASER, -1) + ammo.append(default_ammo) -func _physics_process(_delta: float) -> void: +func _physics_process(delta: float) -> void: if GameState.paused: return - if movement_direction != 0: - velocity.x = move_toward(velocity.x, movement_direction * max_speed, acceleration) + if input.yaw == 0: + rotation_velocity = move_toward(rotation_velocity, 0, friction) else: - velocity.x = move_toward(velocity.x, 0, friction) + rotation_velocity = move_toward(rotation_velocity, input.yaw * yaw_max_speed, yaw_acceleration) - var fake_velocity = Vector2(velocity.x, max_speed * ship_direction) - sprite.rotation = fake_velocity.angle() + PI / 2 + rotation += rotation_velocity * delta + + if input.thrust == 0 and input.strafe == 0: + velocity = velocity.move_toward(Vector2(0, 0), friction) + else: + velocity = velocity.move_toward(Vector2(input.strafe * max_strafe_speed, input.thrust * max_speed).rotated(rotation), acceleration) move_and_slide() +func _process(_delta: float) -> void: + if input.weapons_up: + ammo_index += 1 + ammo_changed.emit(ammo[ammo_index]) + input.weapons_up = false + if input.weapons_down: + ammo_index -= 1 + ammo_changed.emit(ammo[ammo_index]) + input.weapons_down = false + + if input.fire: + fire() + input.fire = false + func fire() -> void: - if GameState.paused: + if weapon_cooldown.time_left > 0: return - if projectile_cooldown_timer.time_left > 0: - return + var ammo_to_fire: Ammo = ammo[ammo_index] + + var i: int = last_hardpoint_index + 1 + if i >= weapon_hardpoints.size(): i = 0 + while weapon_hardpoints[i].hardpoint_type != ammo_to_fire.projectile_type: + i += 1 + if i >= weapon_hardpoints.size(): i = 0 - var projectile := projectile_scene.instantiate() - projectile.position = position - projectile.velocity = Vector2(0, -projectile.speed).rotated(sprite.rotation) - get_parent().add_child(projectile) + var cooldown: float = weapon_hardpoints[i].fire(self) + ammo_to_fire.consume_ammo() + last_hardpoint_index = i + weapon_cooldown.start(cooldown) + weapon_fired.emit(ammo_to_fire, cooldown, weapon_cooldown) + if ammo_to_fire.projectile_count == 0: + ammo.erase(ammo_to_fire) + ammo_index -= 1 + ammo_changed.emit(ammo[ammo_index]) - projectile_cooldown_timer.start(projectile_cooldown*0.001) +func get_input() -> ShipInput: + return input func apply_damage(damage: int) -> void: health -= damage diff --git a/scenes/ships/ship.tscn b/scenes/ships/ship.tscn index 1eb72af..42a5970 100644 --- a/scenes/ships/ship.tscn +++ b/scenes/ships/ship.tscn @@ -1,18 +1,17 @@ -[gd_scene load_steps=5 format=3 uid="uid://6eia23dluwgi"] +[gd_scene load_steps=6 format=3 uid="uid://6eia23dluwgi"] [ext_resource type="Script" path="res://scenes/ships/ship.gd" id="1_2npxx"] [ext_resource type="Texture2D" uid="uid://tmwnlgiyrk1f" path="res://assets/images/ships/player/player_ship.webp" id="2_un1nr"] [ext_resource type="PackedScene" uid="uid://de3ymp8rbho6h" path="res://scenes/ui/ui_elements/health_bar/health_bar.tscn" id="3_knxy5"] +[ext_resource type="PackedScene" uid="uid://dsg7rv0et4eym" path="res://scenes/ships/hardpoint.tscn" id="4_b6mqh"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_l3enx"] size = Vector2(43, 33) -[node name="Ship" type="CharacterBody2D" node_paths=PackedStringArray("health_bar", "sprite")] +[node name="Ship" type="CharacterBody2D"] z_index = 10 scale = Vector2(2.5, 2.5) script = ExtResource("1_2npxx") -health_bar = NodePath("HealthBar") -sprite = NodePath("Sprite") [node name="CollisionShape2D" type="CollisionShape2D" parent="."] shape = SubResource("RectangleShape2D_l3enx") @@ -24,3 +23,12 @@ texture = ExtResource("2_un1nr") offset_left = -13.0 offset_top = 16.8 offset_bottom = 18.8 + +[node name="LeftPhaser" parent="." instance=ExtResource("4_b6mqh")] +position = Vector2(-6.4, -4.76837e-07) + +[node name="RighPhaser" parent="." instance=ExtResource("4_b6mqh")] +position = Vector2(6, 0) + +[node name="CenterMissile" parent="." instance=ExtResource("4_b6mqh")] +position = Vector2(0, 8.4) diff --git a/scenes/ui/canvas_layer.tscn b/scenes/ui/canvas_layer.tscn index e2bf3d4..3012ef6 100644 --- a/scenes/ui/canvas_layer.tscn +++ b/scenes/ui/canvas_layer.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=5 format=3 uid="uid://tv78msbkxshj"] +[gd_scene load_steps=7 format=3 uid="uid://tv78msbkxshj"] [ext_resource type="PackedScene" uid="uid://crgtj35jqtj6u" path="res://scenes/ui/main_menu/main_menu.tscn" id="1_1kb53"] [ext_resource type="Script" path="res://scenes/ui/canvas_layer.gd" id="1_h1kgl"] +[ext_resource type="Theme" uid="uid://7u3vc13ppdl0" path="res://assets/themes/main.tres" id="2_6ii1c"] [ext_resource type="PackedScene" uid="uid://bdffr8ebg4185" path="res://scenes/ui/ingame_menu/in_game_menu.tscn" id="3_v6532"] [ext_resource type="PackedScene" uid="uid://dcetag1pxogfk" path="res://scenes/ui/pause_menu/pause_menu.tscn" id="4_uikms"] +[ext_resource type="PackedScene" uid="uid://cr6om5kgw7xi7" path="res://scenes/ui/gameover_menu/game_over_menu.tscn" id="6_k28s6"] [node name="CanvasLayer" type="CanvasLayer" node_paths=PackedStringArray("menus")] script = ExtResource("1_h1kgl") @@ -16,6 +18,7 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +theme = ExtResource("2_6ii1c") [node name="MainMenu" parent="Menus" instance=ExtResource("1_1kb53")] layout_mode = 1 @@ -27,3 +30,8 @@ layout_mode = 1 [node name="PauseMenu" parent="Menus" instance=ExtResource("4_uikms")] visible = false layout_mode = 1 + +[node name="GameOverMenu" parent="Menus" instance=ExtResource("6_k28s6")] +visible = false +layout_mode = 1 +is_paused = true diff --git a/scenes/ui/gameover_menu/game_over_menu.gd b/scenes/ui/gameover_menu/game_over_menu.gd new file mode 100644 index 0000000..dbd46e1 --- /dev/null +++ b/scenes/ui/gameover_menu/game_over_menu.gd @@ -0,0 +1,7 @@ +extends MenuPanel + +func _on_quit_pressed() -> void: + EventBus.quit_game.emit() + +func _on_play_again_pressed() -> void: + EventBus.reset_game.emit() diff --git a/scenes/ui/gameover_menu/game_over_menu.tscn b/scenes/ui/gameover_menu/game_over_menu.tscn new file mode 100644 index 0000000..1fe43f8 --- /dev/null +++ b/scenes/ui/gameover_menu/game_over_menu.tscn @@ -0,0 +1,68 @@ +[gd_scene load_steps=4 format=3 uid="uid://cr6om5kgw7xi7"] + +[ext_resource type="Material" uid="uid://7k1j70yyf3bv" path="res://assets/shaders/effects/blur.tres" id="1_8hoeb"] +[ext_resource type="Script" path="res://scenes/ui/gameover_menu/game_over_menu.gd" id="1_36dbm"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rp52n"] +bg_color = Color(0.180392, 0.180392, 0.180392, 0.639216) + +[node name="GameOverMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_36dbm") +menu_type = 3 + +[node name="ColorRect" type="ColorRect" parent="."] +material = ExtResource("1_8hoeb") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Panel" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -156.0 +offset_top = -226.5 +offset_right = 156.0 +offset_bottom = 226.5 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_rp52n") + +[node name="VBoxContainer" type="VBoxContainer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/separation = 20 +alignment = 1 + +[node name="Label" type="Label" parent="Panel/VBoxContainer"] +layout_mode = 2 +text = "Game Over! +" +horizontal_alignment = 1 + +[node name="PlayAgain" type="Button" parent="Panel/VBoxContainer"] +layout_mode = 2 +text = "Play Again" + +[node name="Quit" type="Button" parent="Panel/VBoxContainer"] +layout_mode = 2 +text = "Quit" + +[connection signal="pressed" from="Panel/VBoxContainer/PlayAgain" to="." method="_on_play_again_pressed"] +[connection signal="pressed" from="Panel/VBoxContainer/Quit" to="." method="_on_quit_pressed"] diff --git a/scenes/ui/ingame_menu/in_game_menu.gd b/scenes/ui/ingame_menu/in_game_menu.gd index 35b2259..c509fe6 100644 --- a/scenes/ui/ingame_menu/in_game_menu.gd +++ b/scenes/ui/ingame_menu/in_game_menu.gd @@ -1 +1,54 @@ extends MenuPanel + +@export var weapon_status: TextureRect +@export var ammo_count: Label + +var progress_last: float = 1.0 + +var cooldown: float = 0.0 +var cooldown_timer: Timer + +var player: Player +var weapon_shader: ShaderMaterial + + +func _ready(): + EventBus.player_ready.connect(self._on_player_ready) + weapon_shader = weapon_status.material + weapon_shader.set_shader_parameter("progress", 0.0) + +func _on_player_ready(_player: Player) -> void: + player = _player + player.weapon_fired.connect(self._on_player_weapon_fired) + player.ammo_changed.connect(self._on_player_ammo_changed) + var ammo: Ammo = player.ammo[player.ammo_index] + weapon_status.texture = ammo.item_icon + update_ammo_count(ammo) + + +func _on_player_weapon_fired(ammo: Ammo, _cooldown: float, _cooldown_timer: Timer) -> void: + cooldown_timer = _cooldown_timer + cooldown = _cooldown + update_ammo_count(ammo) + +func _on_player_ammo_changed(ammo: Ammo): + weapon_status.texture = ammo.item_icon + update_ammo_count(ammo) + +func _process(_delta: float) -> void: + var progress: float = get_progress() + if progress != progress_last: + weapon_shader.set_shader_parameter("progress", progress) + progress_last = progress + +func update_ammo_count(ammo: Ammo) -> void: + if ammo.projectile_count < 0: + ammo_count.text = "INF" + else: + ammo_count.text = "%03d" % ammo.projectile_count + + +func get_progress() -> float: + if cooldown == 0 || cooldown_timer == null || cooldown_timer.time_left == 0: + return 0.0 + return cooldown_timer.time_left/cooldown diff --git a/scenes/ui/ingame_menu/in_game_menu.tscn b/scenes/ui/ingame_menu/in_game_menu.tscn index 8235447..8a86a7b 100644 --- a/scenes/ui/ingame_menu/in_game_menu.tscn +++ b/scenes/ui/ingame_menu/in_game_menu.tscn @@ -1,15 +1,21 @@ -[gd_scene load_steps=2 format=3 uid="uid://bdffr8ebg4185"] +[gd_scene load_steps=5 format=3 uid="uid://bdffr8ebg4185"] +[ext_resource type="Theme" uid="uid://7u3vc13ppdl0" path="res://assets/themes/main.tres" id="1_8huc8"] [ext_resource type="Script" path="res://scenes/ui/ingame_menu/in_game_menu.gd" id="1_i16p7"] +[ext_resource type="Material" uid="uid://bqhu74bk858wj" path="res://assets/shaders/ui/weapon_status/weapon_status.tres" id="2_5so6c"] +[ext_resource type="Texture2D" uid="uid://d3jeifgalw07w" path="res://assets/images/projectile/missile/missile0.webp" id="3_2xonv"] -[node name="InGameMenu" type="Control"] +[node name="InGameMenu" type="Control" node_paths=PackedStringArray("weapon_status", "ammo_count")] layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +theme = ExtResource("1_8huc8") script = ExtResource("1_i16p7") +weapon_status = NodePath("VBoxContainer/HBoxContainer/WeaponStatus") +ammo_count = NodePath("VBoxContainer/HBoxContainer/AmmoCount") menu_type = 1 is_paused = false @@ -25,16 +31,36 @@ alignment = 2 [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] layout_mode = 2 -theme_override_constants/separation = 40 -[node name="Control" type="Control" parent="VBoxContainer/HBoxContainer"] +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/margin_left = 45 +theme_override_constants/margin_top = 45 + +[node name="WeaponStatus" type="TextureRect" parent="VBoxContainer/HBoxContainer"] +material = ExtResource("2_5so6c") +custom_minimum_size = Vector2(128, 128) layout_mode = 2 +texture = ExtResource("3_2xonv") +expand_mode = 2 +stretch_mode = 5 -[node name="Lifes" type="Label" parent="VBoxContainer/HBoxContainer"] +[node name="X" type="Label" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(1, 1, 0.501961, 1) +theme_override_font_sizes/font_size = 32 +text = "X" + +[node name="AmmoCount" type="Label" parent="VBoxContainer/HBoxContainer"] layout_mode = 2 -size_flags_vertical = 1 theme_override_font_sizes/font_size = 64 -text = "Lifes: 4" +text = "003" + +[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 45 +theme_override_constants/margin_top = 45 [node name="Bottom" type="Control" parent="VBoxContainer"] layout_mode = 2 diff --git a/scenes/ui/main_menu/main_menu.tscn b/scenes/ui/main_menu/main_menu.tscn index 27ccd46..9da8787 100644 --- a/scenes/ui/main_menu/main_menu.tscn +++ b/scenes/ui/main_menu/main_menu.tscn @@ -27,31 +27,39 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 +[node name="MarginContainer" type="MarginContainer" parent="MainVBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_top = 64 + [node name="GameTitle" type="Label" parent="MainVBoxContainer"] layout_mode = 2 theme_override_font_sizes/font_size = 64 text = "Moon Invaders" horizontal_alignment = 1 -[node name="MarginContainer" type="MarginContainer" parent="MainVBoxContainer"] +[node name="MarginContainer2" type="MarginContainer" parent="MainVBoxContainer"] layout_mode = 2 size_flags_vertical = 3 theme_override_constants/margin_left = 710 theme_override_constants/margin_right = 710 -[node name="VBoxContainer" type="VBoxContainer" parent="MainVBoxContainer/MarginContainer"] +[node name="VBoxContainer" type="VBoxContainer" parent="MainVBoxContainer/MarginContainer2"] layout_mode = 2 theme_override_constants/separation = 32 alignment = 1 -[node name="Play" type="Button" parent="MainVBoxContainer/MarginContainer/VBoxContainer"] +[node name="Play" type="Button" parent="MainVBoxContainer/MarginContainer2/VBoxContainer"] layout_mode = 2 theme_override_font_sizes/font_size = 32 text = "Play" -[node name="Something" type="Button" parent="MainVBoxContainer/MarginContainer/VBoxContainer"] +[node name="Something" type="Button" parent="MainVBoxContainer/MarginContainer2/VBoxContainer"] layout_mode = 2 theme_override_font_sizes/font_size = 32 text = "Something" -[connection signal="pressed" from="MainVBoxContainer/MarginContainer/VBoxContainer/Play" to="." method="_on_play_pressed"] +[node name="MarginContainer" type="MarginContainer" parent="MainVBoxContainer/MarginContainer2/VBoxContainer"] +layout_mode = 2 +theme_override_constants/margin_bottom = 256 + +[connection signal="pressed" from="MainVBoxContainer/MarginContainer2/VBoxContainer/Play" to="." method="_on_play_pressed"] diff --git a/scenes/ui/pause_menu/pause_menu.gd b/scenes/ui/pause_menu/pause_menu.gd index 2da14d4..7eb5491 100644 --- a/scenes/ui/pause_menu/pause_menu.gd +++ b/scenes/ui/pause_menu/pause_menu.gd @@ -10,7 +10,6 @@ func _on_resume_pressed() -> void: func _on_quit_pressed() -> void: EventBus.quit_game.emit() - func _input(_event): if Input.is_action_just_pressed("ui_cancel"): if GameState.paused: diff --git a/scenes/ui/ui_elements/health_bar/health_bar.tscn b/scenes/ui/ui_elements/health_bar/health_bar.tscn index a31357a..70b25e3 100644 --- a/scenes/ui/ui_elements/health_bar/health_bar.tscn +++ b/scenes/ui/ui_elements/health_bar/health_bar.tscn @@ -1,10 +1,10 @@ [gd_scene load_steps=3 format=3 uid="uid://de3ymp8rbho6h"] -[ext_resource type="Material" uid="uid://gybsnxxx6x3f" path="res://assets/shaders/health_bar/health_bar.tres" id="1_1cfk2"] [ext_resource type="Script" path="res://scenes/ui/ui_elements/health_bar/health_bar.gd" id="1_qfcmn"] +[ext_resource type="Material" uid="uid://gybsnxxx6x3f" path="res://assets/shaders/ui/health_bar/health_bar.tres" id="1_u2iuq"] [node name="HealthBar" type="ColorRect"] -material = ExtResource("1_1cfk2") +material = ExtResource("1_u2iuq") offset_left = -12.0 offset_top = -1.0 offset_right = 13.0 diff --git a/scenes/world/camera.gd b/scenes/world/camera.gd new file mode 100644 index 0000000..728961f --- /dev/null +++ b/scenes/world/camera.gd @@ -0,0 +1,24 @@ +extends Camera2D + +@export var speed := 5 + +var player: Player + +func _ready(): + EventBus.world_ready.connect(self._on_world_ready) + EventBus.player_ready.connect(self._on_player_ready) + +func _on_world_ready(world: World): + limit_left = -int(world.size.x / 2.0) + limit_right = int(world.size.x / 2.0) + limit_top = -int(world.size.y / 2.0) + limit_bottom = int(world.size.y / 2.0) + +func _on_player_ready(_player: Player): + self.player = _player + +func _physics_process(delta: float) -> void: + if GameState.paused || player == null: + return + + position = lerp(position, player.position, speed * delta) diff --git a/scenes/world/world.gd b/scenes/world/world.gd index c547209..504c15d 100644 --- a/scenes/world/world.gd +++ b/scenes/world/world.gd @@ -11,3 +11,6 @@ func _ready() -> void: func add_object(object: CollisionObject2D): objects.add_child.call_deferred(object) + +func remove_object(object: CollisionObject2D): + objects.remove_child.call_deferred(object) diff --git a/scenes/world/world.tscn b/scenes/world/world.tscn index ee2b3ff..3790bdd 100644 --- a/scenes/world/world.tscn +++ b/scenes/world/world.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=6 format=3 uid="uid://b8106ayuswq6d"] +[gd_scene load_steps=7 format=3 uid="uid://b8106ayuswq6d"] [ext_resource type="Script" path="res://scenes/world/world.gd" id="1_qcaai"] [ext_resource type="Texture2D" uid="uid://dj8u3soodffrd" path="res://assets/images/world/background.png" id="2_jk8gw"] [ext_resource type="Script" path="res://scenes/world/objects.gd" id="3_1s44g"] [ext_resource type="PackedScene" uid="uid://bxs7udg4v8uyq" path="res://scenes/items/helth/health.tscn" id="4_ik2jr"] -[ext_resource type="Script" path="res://scenes/ships/player/camera.gd" id="5_foa7n"] +[ext_resource type="Script" path="res://scenes/world/camera.gd" id="5_8hv6i"] +[ext_resource type="PackedScene" uid="uid://c5gppqo8phv1n" path="res://scenes/items/ammo/missile_ammo/missile_ammo.tscn" id="5_i3sb7"] [node name="World" type="Node2D" node_paths=PackedStringArray("objects")] script = ExtResource("1_qcaai") @@ -24,8 +25,8 @@ texture = ExtResource("2_jk8gw") [node name="Objects" type="Node2D" parent="."] script = ExtResource("3_1s44g") -items = Array[PackedScene]([ExtResource("4_ik2jr")]) +items = Array[PackedScene]([ExtResource("4_ik2jr"), ExtResource("5_i3sb7")]) [node name="PlayerCamera" type="Camera2D" parent="."] position = Vector2(0, -150) -script = ExtResource("5_foa7n") +script = ExtResource("5_8hv6i") diff --git a/src/enemy_ai/enemy_ai.gd b/src/enemy_ai/enemy_ai.gd new file mode 100644 index 0000000..1e3e59b --- /dev/null +++ b/src/enemy_ai/enemy_ai.gd @@ -0,0 +1,17 @@ +class_name EnemyAI +extends RefCounted + +var enemy: Enemy +var input: Ship.ShipInput + +func _init(_enemy: Enemy) -> void: + input = Ship.ShipInput.new() + enemy = _enemy + +func get_input() -> Ship.ShipInput: + return input + +func update() -> void: + input.thrust = 1 + input.strafe = 1 + input.fire = true diff --git a/src/enemy_manager.gd b/src/enemy_manager.gd index 6e20193..28bd42f 100644 --- a/src/enemy_manager.gd +++ b/src/enemy_manager.gd @@ -12,6 +12,7 @@ func _on_world_ready(_world: World) -> void: func _on_player_ready(_player: Player) -> void: self.player = _player + next_wave() func _on_enemy_destroyed(enemy: Enemy) -> void: enemies.erase(enemy) @@ -34,7 +35,6 @@ func _ready() -> void: func set_starting_position(enemy: Enemy, n: int, count: int) -> void: enemy.position.y = player.position.y - 500 enemy.position.x = player.position.x + (get_viewport().size.x / count) * n - func next_wave() -> void: wave += 1 diff --git a/src/event_bus.gd b/src/event_bus.gd index 9af2a64..195377c 100644 --- a/src/event_bus.gd +++ b/src/event_bus.gd @@ -1,7 +1,9 @@ extends Node signal start_game() +signal reset_game() signal quit_game() +signal game_over() signal enemy_manager_ready() signal world_ready(world: World) diff --git a/src/game_manager.gd b/src/game_manager.gd deleted file mode 100644 index 59d591f..0000000 --- a/src/game_manager.gd +++ /dev/null @@ -1,36 +0,0 @@ -extends Node - -var world_scene: PackedScene = preload("res://scenes/world/world.tscn") -var world: World - -var player_scene: PackedScene = preload("res://scenes/ships/player/player.tscn") -var player - -func _ready() -> void: - EventBus.start_game.connect(self._start_game) - EventBus.quit_game.connect(self._quit_game) - EventBus.world_ready.connect(self._world_ready) - EventBus.enemy_manager_ready.connect(self._on_enemy_manager_ready) - -func _start_game() -> void: - world = world_scene.instantiate() - add_child(world) - UI.change_menu(UI.Menu.INGAME) - -func _quit_game() -> void: - if world != null: - world.queue_free() - UI.change_menu(UI.Menu.MAIN) - -func _world_ready(_world: World): - player = player_scene.instantiate() - _world.add_object(player) - -func _on_enemy_manager_ready(): - EnemyManager.next_wave() - -func pause_game() -> void: - GameState.paused = true - -func resume_game() -> void: - GameState.paused = false diff --git a/src/game_state.gd b/src/game_state.gd index c5672db..92a1b4b 100644 --- a/src/game_state.gd +++ b/src/game_state.gd @@ -14,3 +14,9 @@ func _world_ready(_world: World): func _player_ready(_player: Player): self.player = _player + +func pause_game() -> void: + paused = true + +func resume_game() -> void: + paused = false diff --git a/src/ui.gd b/src/ui.gd index 44b1f6e..b37c140 100644 --- a/src/ui.gd +++ b/src/ui.gd @@ -1,30 +1,27 @@ extends Node -@onready var canvas_layer = preload("res://scenes/ui/canvas_layer.tscn").instantiate() - -enum Menu { MAIN, INGAME, PAUSE } +enum Menu { MAIN, INGAME, PAUSE, GAMEOVER } var current_menu = Menu.MAIN -var _menus: Dictionary +var menus: Dictionary var previous_menu = Menu.MAIN -func _ready() -> void: - add_child(canvas_layer) +func set_canvas_layer(canvas_layer: CanvasLayer) -> void: for menu in canvas_layer.menus.get_children(): - _menus[menu.menu_type] = menu as MenuPanel + menus[menu.menu_type] = menu as MenuPanel func change_menu(new_menu: Menu) -> void: previous_menu = current_menu current_menu = new_menu - _menus[previous_menu].hide() - _menus[current_menu].show() - if _menus[current_menu].is_paused: - GameManager.pause_game() + menus[previous_menu].hide() + menus[current_menu].show() + if menus[current_menu].is_paused: + GameState.pause_game() else: - GameManager.resume_game() + GameState.resume_game() func revert_menu() -> void: change_menu(previous_menu) func get_current_menu() -> MenuPanel: - return _menus[current_menu] + return menus[current_menu]