Compare commits

..

14 Commits

Author SHA1 Message Date
sijcdu 8b6941fb9a Add pickup/gameplay tweaks and ignore packaged zips 2026-10-07 23:41:22 +08:00
sijcdu 13b5a8edc7 Level 3: double the bullet rain density and bring the patrolling walkers back
Rain interval went from 0.4s +- 0.25 to 0.2s +- 0.12, which measures 28 drops in 6 seconds instead of 13 and keeps about 24 bullets in the air at once. Three walkers are placed back in level 3, on the corridor at cells (10,11) and (17,11) and on the left ledge at (5,10); they only patrol and hurt on contact, since their firing was removed earlier.
2026-10-07 23:24:00 +08:00
sijcdu 4a6f4862de Bring the jump back so the key warp can reach high ledges
Space is bound to jump again, exactly as before: same action name, same physical_keycode 32, same jump_velocity -190 and gravity 980, so the measured jump height is 20.07 px, identical to the reading taken before the binding was removed. Single jump only, no double jump. The debug flight goes back to Space for up and S for down like it used to, with the arrow keys kept as an extra. No gameplay content touched.
2026-10-07 23:14:52 +08:00
sijcdu f549c03533 Hide the debug fly and level skip behind a 房★主★有★神★器★ easter-egg panel
cheat_panel replaces the level_skip autoload: a single small button in the top-right opens a panel holding the fly toggle and the level jump buttons, so nothing that looks like leftover debug UI is on screen. The fly is no longer gated behind OS.is_debug_build, so the easter egg works in exported builds too, and the 1/2/3 shortcuts only fire while the panel is open so they cannot be hit by accident. Space stays unbound and no gameplay content was touched.
2026-10-07 23:10:41 +08:00
sijcdu 44e4c3a751 Add a debug-only level skip overlay so testing level 3 does not require beating levels 1 and 2
Registered as an autoload so every scene gets it for free. Top-right shows a row of buttons generated from the levels array (main, level2, level3) plus keyboard shortcuts 1/2/3, and it runs with PROCESS_MODE_ALWAYS at layer 30 so it still works while the tree is paused on a death or level-clear screen. Hidden in exported release builds, same rule as the debug flight.
2026-10-07 23:02:57 +08:00
sijcdu 8127c787f8 Bullet rain is denser and its bullets are no longer stopped by anything
Interval dropped from 1.0s +- 0.6 to 0.4s +- 0.25 (about 2.5 bullets per second instead of 1), which measured 13 drops in 6 seconds versus 6 before. bullet.gd gained stop_on_obstacle (default false): the probe raycast is disabled, so rain bullets fall straight through terrain and ropes and are only recycled by their lifetime or by dropping past despawn_y. Verified a bullet falling from y=190 goes through the floor at y=225 and despawns at y=340. Also picks up the editor changes: the rope tiles were removed from level 3 and a few nodes gained unique ids.
2026-10-07 23:01:00 +08:00
sijcdu acc7901c2a Add level 3: bullet rain from the sky, and ropes that only block bullets
The tileset gained a second physics layer (collision_layer 2) and the rope tile (11,4) now carries its 18x6 polygon on that layer instead of layer 0, so it is invisible to anything that does not ask for layer 2. The player's mask stays 1 and the key copies it, so both fall straight through ropes; the bullet probe mask went from 1 to 3 so falling bullets are stopped by them. New bullet_rain prefab drops bullets at random x across the level on a jittered timer, sparse by default (1.0s +- 0.6s), and level 3 places one spanning the whole width. Level 3 has no lasers, and level 2's clear screen now leads into it.
2026-10-07 22:51:56 +08:00
sijcdu 8391e1c5da Remove the two patrolling enemies from level 2
The user asked for them to be gone: level 2 was unwinnable and the walkers only added to the frustration. The walker prefab and script stay in the project so they can be placed again later.
2026-10-07 22:41:49 +08:00
sijcdu defdf202e6 Bullet field: pierce-through and damaging, rings spaced 3 cells apart
Solid bullets were blocking the patrolling enemies, so the field now defaults to solid = false: each bullet is a plain StaticBody2D with no collision layer, plus an Area2D that only detects the player, so nothing is physically obstructed while contact still costs a heart. Ring radii changed from [5, 10, 15] to [3, 6, 9]. Damage is now applied every frame to whatever is overlapping, not only on body_entered, so standing inside a bullet keeps hurting instead of only the first touch. Also picks up the editor change that removed Walker_03.
2026-10-07 22:38:07 +08:00
sijcdu 35174b67ab Replace the firing bullet emitter with static bullet obstacles ringed around (12,12)
The user did not want anything firing: the bullets should simply exist in the air as fixed obstacles. bullet_ring (the emitter) is deleted and bullet_field takes its place. bullet_field generates concentric rings of frozen bullets centred on itself, radii given in cells (default 5/10/15), and skips bullets that fall outside the level bounds or land inside solid tiles so nothing is buried in the terrain. Each bullet is a StaticBody2D on the world layer, so it blocks the player and can be stood on; damage defaults to 0 (pure obstacle) and can be raised to turn them into hazards.
2026-10-07 22:29:12 +08:00
sijcdu 1b3d61793d Enemies no longer attack; add a fixed bullet-ring emitter and make lasers permanent obstacles
walker.gd lost its firing code entirely: monsters now only patrol and hurt on contact, so the only way past is footwork or the key throw + W warp. New bullet_ring prefab fires a set number of bullets evenly spread over a fixed arc on a timer, with directions derived only from its own exports, so every volley is identical and the pattern can be memorised; the level places one at cell (12,12) aimed upward with the 12 bullets spread over 180 degrees. laser.gd gained always_on, defaulting to true, so the five copied lasers are now static hazards that are always lit and damage anything overlapping them rather than blinking on and off. Bullets now stop when they spawn inside terrain.
2026-10-07 22:23:18 +08:00
sijcdu 0d2bccc3a9 Monsters are now unkillable and fire a fixed bullet pattern; add periodic lasers
Removed the stomp-kill from walker.gd: landing on an enemy now just costs a heart like any other contact, so the only way past is to weave through the pattern or use the key throw + W warp. Enemies fire a fan of bullets every fire_interval whose directions are computed only from the facing and the exported spread, never from the player, so every volley traces the identical path and can be memorised. Bullets are code-generated circles (no art needed), die on hitting terrain via a probe raycast, and are freed after their lifetime. New laser prefab cycles wait -> blinking warmup -> firing, and only damages during the firing phase.
2026-10-07 22:12:14 +08:00
sijcdu 74e4bd1e97 Add patrolling enemies and link level 1 to level 2
New walker prefab (mushroom_red sprite) patrols a platform, turns at walls and ledges, drains a heart on contact and dies when stomped, bouncing the player up. Three are placed in level2. The level 2 spawn point is set to cell (7,11) so the player starts standing on the floor instead of falling in from mid-air, and level 1's LevelClear now loads level2.tscn.
2026-10-07 22:02:46 +08:00
sijcdu 84020864cc Remove the level 2 scaffold at the user's request
The user will build level 2 by hand in the Godot editor. main.tscn's LevelClear no longer points at a next scene, so the clear button falls back to restarting level 1. The scaffold is still in history at 81ffe98 and can be restored with: git checkout 81ffe98 -- scenes/level_2.tscn
2026-10-07 21:25:07 +08:00
27 changed files with 1305 additions and 106 deletions
+4
View File
@@ -4,3 +4,7 @@
# JetBrains Rider project files (they contain machine-specific paths)
.idea/
# 打包提交用的 zip:放在项目根目录,但不要进版本库
/*.zip
/build/
+6 -4
View File
@@ -23,7 +23,7 @@
| `S` 松开 | 投出钥匙 |
| `W` | 传送到扔出去的钥匙位置,并把它捡回来 |
| `Z` | 消耗一个回血道具:回一颗心 + 5 秒无敌盾 |
| `F` | 切换飞行(**仅编辑器 / 调试版可用**,见下方说明) |
| `F` | 切换飞行(**隐藏技能**,藏在右上角「房★主★有★神★器★」面板里,见下方说明) |
| `R` / `Enter` | 死亡后重新开始 |
---
@@ -92,10 +92,12 @@
- 按 `W`:瞬移到钥匙所在位置并收回它
- 因为预测和实际飞行调用的是**同一个 `simulate_trajectory()` 函数**,"弧线显示能落到的地方"就等于"钥匙真正落到的地方"(实测偏差 0.000 px)
**技能 B —— 飞行**(`player_controller.gd`)
**技能 B —— 飞行(隐藏技能)**(`player_controller.gd`)
- 按 `F` 切换飞行状态,`W` / `S` 控制升降,`A` / `D` 平移
- 为了不影响正式玩法的难度平衡,**只在编辑器 / 调试版生效**(`OS.is_debug_build()`),导出正式版按键无效
- 按 `F` 切换飞行状态,`空格` / `S` 控制升降(方向键 ↑ / ↓ 也可以),`A` / `D` 平移
- 屏幕上不会有任何提示,它是藏在右上角 **「房★主★有★神★器★」** 按钮里的彩蛋:
点开面板就能看到飞行开关和跳关按钮(键盘 `1` / `2` / `3` 跳关,`Esc` 关闭面板)
- 这个面板由自动加载单例 `scripts/cheat_panel.gd` 提供,**三个关卡都有,正式版里也能用**
- 如果想在导出版里也开放,把 `fly_available()` 里的 `OS.is_debug_build()` 去掉即可:
```gdscript
func fly_available() -> bool:
@@ -132,6 +132,7 @@ use_texture_padding = false
9:4/0 = 0
10:4/0 = 0
11:4/0 = 0
11:4/0/physics_layer_1/polygon_0/points = PackedVector2Array(-9, -3, 9, -3, 9, 3, -9, 3)
12:4/0 = 0
13:4/0 = 0
14:4/0 = 0
@@ -264,4 +265,5 @@ use_texture_padding = false
[resource]
tile_size = Vector2i(18, 18)
physics_layer_0/collision_layer = 1
physics_layer_1/collision_layer = 2
sources/0 = SubResource("TileSetAtlasSource_afvjv")
+4
View File
@@ -15,6 +15,10 @@ run/main_scene="res://scenes/main.tscn"
config/features=PackedStringArray("4.7", "Forward Plus")
config/icon="res://icon.svg"
[autoload]
CheatPanel="*res://scenes/cheat_panel.tscn"
[display]
window/size/viewport_width=468
+77
View File
@@ -0,0 +1,77 @@
[gd_scene load_steps=4 format=3]
[ext_resource type="Script" path="res://scripts/cheat_panel.gd" id="1_cheat"]
[sub_resource type="SystemFont" id="SystemFont_cjk"]
font_names = PackedStringArray("Microsoft YaHei UI", "Microsoft YaHei", "SimHei", "Noto Sans CJK SC", "sans-serif")
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_panel"]
content_margin_left = 10.0
content_margin_top = 10.0
content_margin_right = 10.0
content_margin_bottom = 10.0
bg_color = Color(0.07, 0.08, 0.13, 0.92)
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
border_color = Color(1, 0.82, 0.35, 0.85)
corner_radius_top_left = 5
corner_radius_top_right = 5
corner_radius_bottom_right = 5
corner_radius_bottom_left = 5
[node name="CheatPanel" type="CanvasLayer"]
process_mode = 3
layer = 40
script = ExtResource("1_cheat")
[node name="OpenButton" type="Button" parent="."]
anchor_left = 1.0
anchor_right = 1.0
offset_left = -178.0
offset_top = 6.0
offset_right = -6.0
offset_bottom = 28.0
theme_override_colors/font_color = Color(1, 0.88, 0.5, 1)
theme_override_fonts/font = SubResource("SystemFont_cjk")
theme_override_font_sizes/font_size = 11
text = "房★主★有★神★器★"
[node name="Panel" type="PanelContainer" parent="."]
anchor_left = 1.0
anchor_right = 1.0
offset_left = -178.0
offset_top = 32.0
offset_right = -6.0
offset_bottom = 168.0
theme_override_styles/panel = SubResource("StyleBoxFlat_panel")
[node name="VBox" type="VBoxContainer" parent="Panel"]
theme_override_constants/separation = 6
[node name="Title" type="Label" parent="Panel/VBox"]
theme_override_colors/font_color = Color(1, 0.85, 0.4, 1)
theme_override_fonts/font = SubResource("SystemFont_cjk")
theme_override_font_sizes/font_size = 12
text = "神 器"
horizontal_alignment = 1
[node name="FlyButton" type="Button" parent="Panel/VBox"]
theme_override_fonts/font = SubResource("SystemFont_cjk")
theme_override_font_sizes/font_size = 11
text = "飞行:关(F)"
[node name="Row" type="HBoxContainer" parent="Panel/VBox"]
theme_override_constants/separation = 5
[node name="JumpHint" type="Label" parent="Panel/VBox/Row"]
theme_override_fonts/font = SubResource("SystemFont_cjk")
theme_override_font_sizes/font_size = 11
text = "跳关"
vertical_alignment = 1
[node name="CloseButton" type="Button" parent="Panel/VBox"]
theme_override_fonts/font = SubResource("SystemFont_cjk")
theme_override_font_sizes/font_size = 11
text = "关闭(Esc)"
+279
View File
@@ -0,0 +1,279 @@
[gd_scene format=4 uid="uid://b14qjya7pedi1"]
[ext_resource type="PackedScene" path="res://scenes/hud.tscn" id="1_g8vcy"]
[ext_resource type="PackedScene" path="res://scenes/death_screen.tscn" id="2_cqal4"]
[ext_resource type="PackedScene" path="res://scenes/level_clear.tscn" id="3_o3sye"]
[ext_resource type="Texture2D" uid="uid://djmk0srfgyafe" path="res://assets/game/backgrounds/cloud_left.png" id="4_24ks5"]
[ext_resource type="Texture2D" uid="uid://dptqmtay4plwp" path="res://assets/game/backgrounds/cloud_trees.png" id="5_s4es8"]
[ext_resource type="Texture2D" uid="uid://dys7320ghojh6" path="res://assets/game/backgrounds/cloud_hills.png" id="6_63e4f"]
[ext_resource type="Texture2D" uid="uid://1b8hp0rm864l" path="res://assets/game/backgrounds/cloud_right.png" id="7_y6lc1"]
[ext_resource type="Script" uid="uid://dxwa7fxghp3a2" path="res://scripts/hidden_passages.gd" id="8_ekhvm"]
[ext_resource type="TileSet" uid="uid://dkowcf1ljh41j" path="res://assets/game/tilesets/kenney_terrain_tileset.tres" id="9_nflru"]
[ext_resource type="PackedScene" uid="uid://c1u8k6ny02t4y" path="res://scenes/player.tscn" id="10_ikw2v"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazards/spikes_double.tscn" id="11_cgqhq"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazards/spikes_grey.tscn" id="12_ttvjw"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/interactables/door.tscn" id="14_qo7hg"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/collectibles/coin_gold_round.tscn" id="15_odav0"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/collectibles/key_gold.tscn" id="16_3xffc"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/collectibles/diamond.tscn" id="17_icuov"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemies/laser.tscn" id="19_laser"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemies/bullet_field.tscn" id="20_field"]
[node name="Main2" type="Node2D" unique_id=2061129075]
[node name="HUD" parent="." unique_id=215179112 instance=ExtResource("1_g8vcy")]
[node name="DeathScreen" parent="." unique_id=1952687359 instance=ExtResource("2_cqal4")]
[node name="LevelClear" parent="." unique_id=1589634220 instance=ExtResource("3_o3sye")]
next_level_scene = "res://scenes/level3.tscn"
[node name="Level" type="Node2D" parent="." unique_id=1678372922]
[node name="Background" type="Node2D" parent="Level" unique_id=702011751]
[node name="SkyColor" type="Polygon2D" parent="Level/Background" unique_id=22763597]
z_index = -30
color = Color(0.8509804, 0.9411765, 0.9372549, 1)
polygon = PackedVector2Array(0, 0, 468, 0, 468, 130.0972, 468, 270, 0, 270)
[node name="CloudBand_01" type="Sprite2D" parent="Level/Background" unique_id=589633713]
z_index = -20
texture_filter = 1
position = Vector2(12, 150)
texture = ExtResource("4_24ks5")
[node name="CloudBand_02" type="Sprite2D" parent="Level/Background" unique_id=225493882]
z_index = -20
texture_filter = 1
position = Vector2(36, 150)
texture = ExtResource("5_s4es8")
[node name="CloudBand_03" type="Sprite2D" parent="Level/Background" unique_id=646834403]
z_index = -20
texture_filter = 1
position = Vector2(60, 150)
texture = ExtResource("6_63e4f")
[node name="CloudBand_04" type="Sprite2D" parent="Level/Background" unique_id=1012727532]
z_index = -20
texture_filter = 1
position = Vector2(84, 150)
texture = ExtResource("7_y6lc1")
[node name="CloudBand_05" type="Sprite2D" parent="Level/Background" unique_id=1685434781]
z_index = -20
texture_filter = 1
position = Vector2(108, 150)
texture = ExtResource("4_24ks5")
[node name="CloudBand_06" type="Sprite2D" parent="Level/Background" unique_id=1425986424]
z_index = -20
texture_filter = 1
position = Vector2(132, 150)
texture = ExtResource("5_s4es8")
[node name="CloudBand_07" type="Sprite2D" parent="Level/Background" unique_id=1333305041]
z_index = -20
texture_filter = 1
position = Vector2(156, 150)
texture = ExtResource("6_63e4f")
[node name="CloudBand_08" type="Sprite2D" parent="Level/Background" unique_id=55356860]
z_index = -20
texture_filter = 1
position = Vector2(180, 150)
texture = ExtResource("7_y6lc1")
[node name="CloudBand_09" type="Sprite2D" parent="Level/Background" unique_id=379152848]
z_index = -20
texture_filter = 1
position = Vector2(204, 150)
texture = ExtResource("4_24ks5")
[node name="CloudBand_10" type="Sprite2D" parent="Level/Background" unique_id=325990577]
z_index = -20
texture_filter = 1
position = Vector2(228, 150)
texture = ExtResource("5_s4es8")
[node name="CloudBand_11" type="Sprite2D" parent="Level/Background" unique_id=1505438200]
z_index = -20
texture_filter = 1
position = Vector2(252, 150)
texture = ExtResource("6_63e4f")
[node name="CloudBand_12" type="Sprite2D" parent="Level/Background" unique_id=190709163]
z_index = -20
texture_filter = 1
position = Vector2(276, 150)
texture = ExtResource("7_y6lc1")
[node name="CloudBand_13" type="Sprite2D" parent="Level/Background" unique_id=349156114]
z_index = -20
texture_filter = 1
position = Vector2(300, 150)
texture = ExtResource("4_24ks5")
[node name="CloudBand_14" type="Sprite2D" parent="Level/Background" unique_id=1838903106]
z_index = -20
texture_filter = 1
position = Vector2(324, 150)
texture = ExtResource("5_s4es8")
[node name="CloudBand_15" type="Sprite2D" parent="Level/Background" unique_id=169474301]
z_index = -20
texture_filter = 1
position = Vector2(348, 150)
texture = ExtResource("6_63e4f")
[node name="CloudBand_16" type="Sprite2D" parent="Level/Background" unique_id=156763289]
z_index = -20
texture_filter = 1
position = Vector2(372, 150)
texture = ExtResource("7_y6lc1")
[node name="CloudBand_17" type="Sprite2D" parent="Level/Background" unique_id=161240142]
z_index = -20
texture_filter = 1
position = Vector2(396, 150)
texture = ExtResource("4_24ks5")
[node name="CloudBand_18" type="Sprite2D" parent="Level/Background" unique_id=1823235224]
z_index = -20
texture_filter = 1
position = Vector2(420, 150)
texture = ExtResource("5_s4es8")
[node name="CloudBand_19" type="Sprite2D" parent="Level/Background" unique_id=2092676382]
z_index = -20
texture_filter = 1
position = Vector2(444, 150)
texture = ExtResource("6_63e4f")
[node name="CloudBand_20" type="Sprite2D" parent="Level/Background" unique_id=124375716]
z_index = -20
texture_filter = 1
position = Vector2(468, 150)
texture = ExtResource("7_y6lc1")
[node name="TerrainTileMaps" type="Node2D" parent="Level" unique_id=652327492]
script = ExtResource("8_ekhvm")
[node name="TerrainBase" type="TileMapLayer" parent="Level/TerrainTileMaps" unique_id=91115499]
position = Vector2(9, 9)
tile_map_data = PackedByteArray("AAD//wkAAAACAAEAAAD//woAAAAEAAAAAAABAA0AAAACAAYAAAACAA0AAAAEAAUAAAACAA4AAAAEAAUAAAADAA0AAAACAAYAAAABAAoAAAACAAYAAAAAAAoAAAAEAAUAAAAAAAkAAAACAAEAAAACAAsAAAAEAAUAAAAAAA0AAAAEAAUAAAD//w0AAAAEAAEAAAAFAA0AAAAEAAUAAAAEAA4AAAAEAAUAAAADAA4AAAACAAYAAAAAAA4AAAACAAYAAAABAA4AAAACAAYAAAAHAAwAAAACAAEAAAALAAwAAAACAAEAAAAKAA0AAAAEAAUAAAAIAA4AAAAEAAUAAAALAA4AAAAEAAUAAAAMAA4AAAACAAYAAAANAA0AAAACAAYAAAAOAA0AAAAEAAUAAAAPAA0AAAAEAAUAAAAQAA0AAAAEAAUAAAARAAwAAAACAAUAAAATAAwAAAACAAUAAAAVAAsAAAACAAYAAAAWAAsAAAAEAAUAAAAUAAwAAAACAAYAAAAQAAwAAAACAAMAAAAKAA4AAAAEAAUAAAAJAA4AAAAEAAUAAAAFAA4AAAACAAYAAAAGAA4AAAAEAAUAAAAHAA4AAAAEAAUAAAANAA4AAAACAAYAAAAOAA4AAAACAAYAAAAPAA4AAAACAAYAAAASAA0AAAACAAYAAAATAA0AAAAEAAUAAAAUAA0AAAACAAYAAAAVAA0AAAACAAYAAAAWAA0AAAACAAYAAAAXAA0AAAAEAAUAAAALAA0AAAAEAAUAAAAMAA0AAAACAAYAAAAPAAwAAAACAAMAAAAXAAsAAAAEAAUAAAAYAAsAAAAEAAUAAAAZAAsAAAACAAYAAAAZAAwAAAACAAYAAAAYAAwAAAAEAAUAAAAZAA4AAAAEAAUAAAAXAA4AAAAEAAUAAAAWAA4AAAAEAAUAAAAVAA4AAAACAAYAAAAUAA4AAAACAAYAAAATAA4AAAACAAYAAAASAA4AAAACAAYAAAAYAA4AAAACAAYAAAARAA4AAAACAAYAAAAEAA0AAAACAAYAAAD//w4AAAAEAAEAAAABAAkAAAACAAEAAAAIAAwAAAACAAEAAAAOAAwAAAACAAMAAAANAAwAAAACAAMAAAAMAAwAAAACAAMAAAAUAAkAAAABAAUAAAAUAAoAAAABAAYAAAAPAAgAAAASAAQAABAWAAoAAAACAAYAAAAXAAcAAAACAAUAAAAYAAkAAAACAAYAAAAYAAoAAAACAAYAAAAXAAoAAAACAAYAAAAXAAkAAAACAAYAAAAZAAkAAAACAAYAAAAZAAoAAAACAAYAAAAQAA4AAAAEAAUAAAARAA0AAAACAAYAAAASAAwAAAACAAUAAAAVAAoAAAACAAYAAAAWAAkAAAACAAUAAAAXAAgAAAAEAAEAAAAUAAsAAAAFAAEAAAAVAAwAAAAEAAUAAAAWAAwAAAACAAYAAAAXAAwAAAAEAAUAAAAYAA0AAAACAAYAAAAZAA0AAAAEAAUAAAAAAAwAAAACAAYAAAACAAwAAAACAAYAAAADAAwAAAAEAAUAAAAEAAwAAAACAAYAAAAFAAwAAAAEAAUAAAAGAAwAAAAEAAUAAAAGAA0AAAACAAYAAAAHAA0AAAACAAYAAAAIAA0AAAAEAAUAAAAJAA0AAAACAAYAAAAJAAwAAAACAAEAAAAKAAwAAAACAAEAAAD//wwAAAAFAAAAAAD//wsAAAAEAAAAAAAAAAsAAAAEAAUAAAABAAsAAAACAAYAAAABAAwAAAACAAYAAAAYAAcAAAACAAUAAAAZAAcAAAADAAUAAAAYAAgAAAACAAYAAAAZAAgAAAACAAYAAAAVAAkAAAACAAUAAAADAAsAAAACAAYAAAAEAAsAAAACAAEAAAAFAAsAAAACAAEAAAAGAAsAAAACAAEAAAACAAoAAAACAAEAAAADAAoAAAACAAEAAAARAAcAAAAOAAcAAAASAAcAAAAOAAcAAAAQAAcAAAAOAAcAAAAPAAcAAAANAAcAAAAIAAYAAAANAAcAAAATAAcAAAAPAAcAAAAMAAYAAAAPAAcAAAALAAYAAAAOAAcAAAAKAAYAAAAOAAcAAAABAAgAAAARAAYAAAABAAcAAAAQAAUAAAABAAYAAAARAAUAAAABAAUAAAAQAAUAAAAAAAQAAAASAAIAAAABAAQAAAASAAIAAAACAAQAAAASAAIAAAD//wQAAAARAAIAAAAEAAQAAAATAAIAAAADAAQAAAASAAIAAAAAAAMAAAASAAEAAAABAAMAAAASAAEAAAACAAMAAAASAAEAAAADAAMAAAASAAAAAAAEAAMAAAATAAAAAAD//wMAAAARAAEAAAD//wIAAAARAAEAAAD//wEAAAARAAEAAAAAAAEAAAASAAEAAAAAAAIAAAASAAEAAAABAAEAAAASAAEAAAABAAIAAAASAAEAAAACAAEAAAASAAEAAAACAAIAAAASAAEAAAACAAYAAAATAAQAAAAJAAYAAAAOAAcAAAA=")
tile_set = ExtResource("9_nflru")
[node name="TerrainDetailsA" type="TileMapLayer" parent="Level/TerrainTileMaps" unique_id=1513560262]
z_index = 1
position = Vector2(9, 9)
tile_map_data = PackedByteArray("AAADAA0AAAADAAYAAAABAAwAAAABAAYAAAABAA0AAAABAAYAAAACAAsAAAACAAEAAAADAAsAAAADAAEAAAACAAwAAAACAAYAAAACAA4AAAACAAYAAAAXAAsAAAADAAUAAAAXAAwAAAADAAYAAAAXAA0AAAADAAYAAAAXAA4AAAADAAYAAAAWAAoAAAAEAAcAAAAWAAsAAAACAAUAAAAWAAwAAAAEAAUAAAAWAA0AAAACAAYAAAAWAA4AAAAEAAUAAAAVAAsAAAABAAUAAAAVAAwAAAABAAYAAAAVAA0AAAABAAYAAAAVAA4AAAABAAYAAAAQAA4AAAADAAYAAAAOAA4AAAAEAAUAAAAPAA4AAAACAAYAAAAQAA0AAAADAAMAAAANAA4AAAABAAYAAAAOAAwAAAAEAAYAAAAPAA0AAAACAAMAAAAOAA0AAAACAAMAAAANAA0AAAABAAMAAAAQAAQAAAATAAIAAAAQAAMAAAATAAEAAAAKAAMAAAARAAAAAAALAAMAAAASAAAAAAAOAAIAAAARAAAAAAAPAAIAAAASAAAAAAADAAwAAAADAAYAAAAQAAIAAAATAAAAAAACAA0AAAAEAAUAAAABAA4AAAABAAYAAAADAA4AAAADAAYAAAABAAsAAAABAAEAAAAUAPz/AAASAAcAAAA=")
tile_set = ExtResource("9_nflru")
[node name="TerrainDetailsB" type="TileMapLayer" parent="Level/TerrainTileMaps" unique_id=478648603]
z_index = 2
position = Vector2(9, 9)
tile_map_data = PackedByteArray("AAAGAAwAAAADAAEAAAAGAA0AAAADAAYAAAAFAA4AAAACAAYAAAAFAAsAAAAFAAYAAAAFAAwAAAACAAEAAAAFAA0AAAAEAAUAAAAGAA4AAAADAAYAAAADAAwAAAABAAEAAAADAA0AAAABAAYAAAADAA4AAAABAAYAAAAEAAwAAAACAAEAAAAEAA0AAAACAAYAAAAEAA4AAAAEAAUAAAA=")
tile_set = ExtResource("9_nflru")
[node name="ActorsAndCharacters" type="Node2D" parent="Level" unique_id=2036761714]
[node name="Player" parent="Level/ActorsAndCharacters" unique_id=1343528246 instance=ExtResource("10_ikw2v")]
z_index = 10
position = Vector2(144, 215)
rotation = -3.1415927
scale = Vector2(1, -1)
[node name="PrefabInstances" type="Node2D" parent="Level" unique_id=2013556771]
[node name="Structures" type="Node2D" parent="Level/PrefabInstances" unique_id=1811607442]
[node name="Hazards" type="Node2D" parent="Level/PrefabInstances" unique_id=888595377]
[node name="Spikes_01" parent="Level/PrefabInstances/Hazards" unique_id=1025977450 instance=ExtResource("11_cgqhq")]
z_index = 5
position = Vector2(270, 216)
[node name="Spikes_02" parent="Level/PrefabInstances/Hazards" unique_id=1262276584 instance=ExtResource("11_cgqhq")]
z_index = 5
position = Vector2(335, 127)
[node name="SpikesGrey_01" parent="Level/PrefabInstances/Hazards" unique_id=214833071 instance=ExtResource("12_ttvjw")]
z_index = 5
position = Vector2(109, 199)
[node name="SpikesGrey_02" parent="Level/PrefabInstances/Hazards" unique_id=458791480 instance=ExtResource("12_ttvjw")]
z_index = 5
position = Vector2(331, 216)
[node name="Enemies" type="Node2D" parent="Level/PrefabInstances" unique_id=93211253]
[node name="Laser_01" parent="Level/PrefabInstances/Enemies" unique_id=1298565970 instance=ExtResource("19_laser")]
position = Vector2(187, 223)
rotation = -1.5707964
[node name="Laser_02" parent="Level/PrefabInstances/Enemies" unique_id=1808104165 instance=ExtResource("19_laser")]
position = Vector2(298, 233)
rotation = -1.5707964
length = 108.0
start_delay = 2.4
[node name="Laser_03" parent="Level/PrefabInstances/Enemies" unique_id=1437324170 instance=ExtResource("19_laser")]
position = Vector2(387, 137)
rotation = -1.5707964
length = 108.0
start_delay = 2.4
[node name="Laser_04" parent="Level/PrefabInstances/Enemies" unique_id=1698684283 instance=ExtResource("19_laser")]
position = Vector2(125, 125)
rotation = -1.5707964
length = 108.0
start_delay = 2.4
[node name="Laser_05" parent="Level/PrefabInstances/Enemies" unique_id=231592857 instance=ExtResource("19_laser")]
position = Vector2(251, 78)
rotation = -1.5707964
length = 108.0
start_delay = 2.4
[node name="BulletField_01" parent="Level/PrefabInstances/Enemies" unique_id=978673745 instance=ExtResource("20_field")]
position = Vector2(234, 234)
[node name="Interactables" type="Node2D" parent="Level/PrefabInstances" unique_id=769532488]
[node name="Door_01" parent="Level/PrefabInstances/Interactables" unique_id=1264725697 instance=ExtResource("14_qo7hg")]
z_index = 5
position = Vector2(288, 37)
[node name="Collectibles" type="Node2D" parent="Level/PrefabInstances" unique_id=2086540192]
[node name="CoinGoldRound_01" parent="Level/PrefabInstances/Collectibles" unique_id=926105584 instance=ExtResource("15_odav0")]
z_index = 5
position = Vector2(437, 127)
[node name="CoinGoldRound_02" parent="Level/PrefabInstances/Collectibles" unique_id=1674416025 instance=ExtResource("15_odav0")]
z_index = 5
position = Vector2(179, 111)
[node name="CoinGoldRound_03" parent="Level/PrefabInstances/Collectibles" unique_id=1087706364 instance=ExtResource("15_odav0")]
z_index = 5
position = Vector2(219, 54)
[node name="KeyGold_01" parent="Level/PrefabInstances/Collectibles" unique_id=2060959680 instance=ExtResource("16_3xffc")]
z_index = 5
position = Vector2(145, 220)
[node name="Diamond_01" parent="Level/PrefabInstances/Collectibles" unique_id=909237898 instance=ExtResource("17_icuov")]
z_index = 5
position = Vector2(399, 161)
[node name="Diamond_02" parent="Level/PrefabInstances/Collectibles" unique_id=234727210 instance=ExtResource("17_icuov")]
z_index = 5
position = Vector2(204, 217)
[node name="Diamond_03" parent="Level/PrefabInstances/Collectibles" unique_id=1261326529 instance=ExtResource("17_icuov")]
z_index = 5
position = Vector2(288, 125)
[node name="Signs" type="Node2D" parent="Level/PrefabInstances" unique_id=113197076]
+272
View File
@@ -0,0 +1,272 @@
[gd_scene format=4 uid="uid://csvqda2jbvh2o"]
[ext_resource type="PackedScene" path="res://scenes/hud.tscn" id="1_07lh1"]
[ext_resource type="PackedScene" path="res://scenes/death_screen.tscn" id="2_6uuvf"]
[ext_resource type="PackedScene" path="res://scenes/level_clear.tscn" id="3_x008a"]
[ext_resource type="Texture2D" uid="uid://djmk0srfgyafe" path="res://assets/game/backgrounds/cloud_left.png" id="4_atr84"]
[ext_resource type="Texture2D" uid="uid://dptqmtay4plwp" path="res://assets/game/backgrounds/cloud_trees.png" id="5_qyu38"]
[ext_resource type="Texture2D" uid="uid://dys7320ghojh6" path="res://assets/game/backgrounds/cloud_hills.png" id="6_8vbrh"]
[ext_resource type="Texture2D" uid="uid://1b8hp0rm864l" path="res://assets/game/backgrounds/cloud_right.png" id="7_d5gix"]
[ext_resource type="Script" uid="uid://dxwa7fxghp3a2" path="res://scripts/hidden_passages.gd" id="8_7hs6g"]
[ext_resource type="TileSet" uid="uid://dkowcf1ljh41j" path="res://assets/game/tilesets/kenney_terrain_tileset.tres" id="9_yeq8b"]
[ext_resource type="PackedScene" uid="uid://c1u8k6ny02t4y" path="res://scenes/player.tscn" id="10_invaa"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazards/spikes_double.tscn" id="11_88b2t"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/hazards/spikes_grey.tscn" id="12_0ydwj"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/interactables/spring_red.tscn" id="15_usk20"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/interactables/door.tscn" id="16_y8u0o"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/collectibles/coin_gold_round.tscn" id="17_16ffm"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/collectibles/key_gold.tscn" id="18_kvrlk"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/collectibles/diamond.tscn" id="19_85fmh"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemies/bullet_rain.tscn" id="20_rain"]
[ext_resource type="PackedScene" path="res://scenes/prefabs/enemies/walker.tscn" id="21_walker"]
[node name="Main" type="Node2D" unique_id=2061129075]
[node name="HUD" parent="." unique_id=215179112 instance=ExtResource("1_07lh1")]
[node name="DeathScreen" parent="." unique_id=1952687359 instance=ExtResource("2_6uuvf")]
[node name="LevelClear" parent="." unique_id=1589634220 instance=ExtResource("3_x008a")]
[node name="Level" type="Node2D" parent="." unique_id=1678372922]
[node name="Background" type="Node2D" parent="Level" unique_id=702011751]
[node name="SkyColor" type="Polygon2D" parent="Level/Background" unique_id=22763597]
z_index = -30
color = Color(0.8509804, 0.9411765, 0.9372549, 1)
polygon = PackedVector2Array(0, 0, 468, 0, 468, 130.0972, 468, 270, 0, 270)
[node name="CloudBand_01" type="Sprite2D" parent="Level/Background" unique_id=589633713]
z_index = -20
texture_filter = 1
position = Vector2(12, 150)
texture = ExtResource("4_atr84")
[node name="CloudBand_02" type="Sprite2D" parent="Level/Background" unique_id=225493882]
z_index = -20
texture_filter = 1
position = Vector2(36, 150)
texture = ExtResource("5_qyu38")
[node name="CloudBand_03" type="Sprite2D" parent="Level/Background" unique_id=646834403]
z_index = -20
texture_filter = 1
position = Vector2(60, 150)
texture = ExtResource("6_8vbrh")
[node name="CloudBand_04" type="Sprite2D" parent="Level/Background" unique_id=1012727532]
z_index = -20
texture_filter = 1
position = Vector2(84, 150)
texture = ExtResource("7_d5gix")
[node name="CloudBand_05" type="Sprite2D" parent="Level/Background" unique_id=1685434781]
z_index = -20
texture_filter = 1
position = Vector2(108, 150)
texture = ExtResource("4_atr84")
[node name="CloudBand_06" type="Sprite2D" parent="Level/Background" unique_id=1425986424]
z_index = -20
texture_filter = 1
position = Vector2(132, 150)
texture = ExtResource("5_qyu38")
[node name="CloudBand_07" type="Sprite2D" parent="Level/Background" unique_id=1333305041]
z_index = -20
texture_filter = 1
position = Vector2(156, 150)
texture = ExtResource("6_8vbrh")
[node name="CloudBand_08" type="Sprite2D" parent="Level/Background" unique_id=55356860]
z_index = -20
texture_filter = 1
position = Vector2(180, 150)
texture = ExtResource("7_d5gix")
[node name="CloudBand_09" type="Sprite2D" parent="Level/Background" unique_id=379152848]
z_index = -20
texture_filter = 1
position = Vector2(204, 150)
texture = ExtResource("4_atr84")
[node name="CloudBand_10" type="Sprite2D" parent="Level/Background" unique_id=325990577]
z_index = -20
texture_filter = 1
position = Vector2(228, 150)
texture = ExtResource("5_qyu38")
[node name="CloudBand_11" type="Sprite2D" parent="Level/Background" unique_id=1505438200]
z_index = -20
texture_filter = 1
position = Vector2(252, 150)
texture = ExtResource("6_8vbrh")
[node name="CloudBand_12" type="Sprite2D" parent="Level/Background" unique_id=190709163]
z_index = -20
texture_filter = 1
position = Vector2(276, 150)
texture = ExtResource("7_d5gix")
[node name="CloudBand_13" type="Sprite2D" parent="Level/Background" unique_id=349156114]
z_index = -20
texture_filter = 1
position = Vector2(300, 150)
texture = ExtResource("4_atr84")
[node name="CloudBand_14" type="Sprite2D" parent="Level/Background" unique_id=1838903106]
z_index = -20
texture_filter = 1
position = Vector2(324, 150)
texture = ExtResource("5_qyu38")
[node name="CloudBand_15" type="Sprite2D" parent="Level/Background" unique_id=169474301]
z_index = -20
texture_filter = 1
position = Vector2(348, 150)
texture = ExtResource("6_8vbrh")
[node name="CloudBand_16" type="Sprite2D" parent="Level/Background" unique_id=156763289]
z_index = -20
texture_filter = 1
position = Vector2(372, 150)
texture = ExtResource("7_d5gix")
[node name="CloudBand_17" type="Sprite2D" parent="Level/Background" unique_id=161240142]
z_index = -20
texture_filter = 1
position = Vector2(396, 150)
texture = ExtResource("4_atr84")
[node name="CloudBand_18" type="Sprite2D" parent="Level/Background" unique_id=1823235224]
z_index = -20
texture_filter = 1
position = Vector2(420, 150)
texture = ExtResource("5_qyu38")
[node name="CloudBand_19" type="Sprite2D" parent="Level/Background" unique_id=2092676382]
z_index = -20
texture_filter = 1
position = Vector2(444, 150)
texture = ExtResource("6_8vbrh")
[node name="CloudBand_20" type="Sprite2D" parent="Level/Background" unique_id=124375716]
z_index = -20
texture_filter = 1
position = Vector2(468, 150)
texture = ExtResource("7_d5gix")
[node name="TerrainTileMaps" type="Node2D" parent="Level" unique_id=652327492]
script = ExtResource("8_7hs6g")
[node name="TerrainBase" type="TileMapLayer" parent="Level/TerrainTileMaps" unique_id=91115499]
position = Vector2(9, 9)
tile_map_data = PackedByteArray("AAD//wkAAAACAAEAAAD//woAAAAEAAAAAAABAA0AAAACAAYAAAACAA0AAAAEAAUAAAACAA4AAAAEAAUAAAADAA0AAAACAAYAAAABAAoAAAACAAYAAAAAAAoAAAAEAAUAAAAAAAkAAAACAAEAAAACAAsAAAAEAAUAAAAAAA0AAAAEAAUAAAD//w0AAAAEAAEAAAAFAA0AAAAEAAUAAAAEAA4AAAAEAAUAAAADAA4AAAACAAYAAAAAAA4AAAACAAYAAAABAA4AAAACAAYAAAAHAAwAAAACAAEAAAALAAwAAAACAAEAAAAKAA0AAAAEAAUAAAAIAA4AAAAEAAUAAAALAA4AAAAEAAUAAAAMAA4AAAACAAYAAAANAA0AAAACAAYAAAAOAA0AAAAEAAUAAAAPAA0AAAAEAAUAAAAQAA0AAAAEAAUAAAARAAwAAAACAAUAAAATAAwAAAACAAUAAAAVAAsAAAACAAYAAAAWAAsAAAAEAAUAAAAUAAwAAAACAAYAAAAQAAwAAAACAAMAAAAKAA4AAAAEAAUAAAAJAA4AAAAEAAUAAAAFAA4AAAACAAYAAAAGAA4AAAAEAAUAAAAHAA4AAAAEAAUAAAANAA4AAAACAAYAAAAOAA4AAAACAAYAAAAPAA4AAAACAAYAAAASAA0AAAACAAYAAAATAA0AAAAEAAUAAAAUAA0AAAACAAYAAAAVAA0AAAACAAYAAAAWAA0AAAACAAYAAAAXAA0AAAAEAAUAAAALAA0AAAAEAAUAAAAMAA0AAAACAAYAAAAPAAwAAAACAAMAAAAXAAsAAAAEAAUAAAAYAAsAAAAEAAUAAAAZAAsAAAACAAYAAAAZAAwAAAACAAYAAAAYAAwAAAAEAAUAAAAZAA4AAAAEAAUAAAAXAA4AAAAEAAUAAAAWAA4AAAAEAAUAAAAVAA4AAAACAAYAAAAUAA4AAAACAAYAAAATAA4AAAACAAYAAAASAA4AAAACAAYAAAAYAA4AAAACAAYAAAARAA4AAAACAAYAAAAEAA0AAAACAAYAAAD//w4AAAAEAAEAAAABAAkAAAACAAEAAAAIAAwAAAACAAEAAAAOAAwAAAACAAMAAAANAAwAAAACAAMAAAAMAAwAAAACAAMAAAAUAAoAAAABAAUAAAAWAAoAAAACAAYAAAAXAAcAAAACAAUAAAAYAAkAAAACAAYAAAAYAAoAAAACAAYAAAAXAAoAAAACAAYAAAAXAAkAAAACAAYAAAAZAAkAAAACAAYAAAAZAAoAAAACAAYAAAAQAA4AAAAEAAUAAAARAA0AAAACAAYAAAASAAwAAAACAAUAAAAVAAoAAAACAAYAAAAWAAkAAAACAAUAAAAXAAgAAAAEAAEAAAAUAAsAAAAFAAEAAAAVAAwAAAAEAAUAAAAWAAwAAAACAAYAAAAXAAwAAAAEAAUAAAAYAA0AAAACAAYAAAAZAA0AAAAEAAUAAAAAAAwAAAACAAYAAAACAAwAAAACAAYAAAADAAwAAAAEAAUAAAAEAAwAAAACAAYAAAAFAAwAAAAEAAUAAAAGAAwAAAAEAAUAAAAGAA0AAAACAAYAAAAHAA0AAAACAAYAAAAIAA0AAAAEAAUAAAAJAA0AAAACAAYAAAAKAAwAAAACAAEAAAD//wwAAAAFAAAAAAD//wsAAAAEAAAAAAAAAAsAAAAEAAUAAAABAAsAAAACAAYAAAABAAwAAAACAAYAAAAYAAcAAAACAAUAAAAZAAcAAAADAAUAAAAYAAgAAAACAAYAAAAZAAgAAAACAAYAAAAVAAkAAAACAAUAAAADAAsAAAACAAYAAAAEAAsAAAACAAEAAAAFAAsAAAACAAEAAAAGAAsAAAACAAEAAAACAAoAAAACAAEAAAADAAoAAAACAAEAAAARAAcAAAAOAAcAAAASAAcAAAAOAAcAAAAQAAcAAAAOAAcAAAAIAAYAAAANAAcAAAATAAcAAAAPAAcAAAAMAAYAAAAPAAcAAAALAAYAAAAOAAcAAAAKAAYAAAAOAAcAAAABAAgAAAARAAYAAAABAAcAAAAQAAUAAAABAAYAAAARAAUAAAABAAUAAAAQAAUAAAAAAAQAAAASAAIAAAABAAQAAAASAAIAAAACAAQAAAASAAIAAAD//wQAAAARAAIAAAAEAAQAAAATAAIAAAADAAQAAAASAAIAAAAAAAMAAAASAAEAAAABAAMAAAASAAEAAAACAAMAAAASAAEAAAADAAMAAAASAAAAAAAEAAMAAAATAAAAAAD//wMAAAARAAEAAAD//wIAAAARAAEAAAD//wEAAAARAAEAAAAAAAEAAAASAAEAAAAAAAIAAAASAAEAAAABAAEAAAASAAEAAAABAAIAAAASAAEAAAACAAEAAAASAAEAAAACAAIAAAASAAEAAAACAAYAAAATAAQAAAAJAAYAAAAOAAcAAAAJAAwAAAACAAEAAAAPAAcAAAANAAcAAAA=")
tile_set = ExtResource("9_yeq8b")
[node name="TerrainBase2" type="TileMapLayer" parent="Level/TerrainTileMaps" unique_id=470196797]
position = Vector2(9, 9)
tile_map_data = PackedByteArray("AAD//wkAAAACAAEAAAD//woAAAAEAAAAAAABAA0AAAACAAYAAAACAA0AAAAEAAUAAAACAA4AAAAEAAUAAAADAA0AAAACAAYAAAABAAoAAAACAAYAAAAAAAoAAAAEAAUAAAAAAAkAAAACAAEAAAACAAsAAAAEAAUAAAAAAA0AAAAEAAUAAAD//w0AAAAEAAEAAAAFAA0AAAAEAAUAAAAEAA4AAAAEAAUAAAADAA4AAAACAAYAAAAAAA4AAAACAAYAAAABAA4AAAACAAYAAAAHAAwAAAACAAEAAAALAAwAAAACAAEAAAAKAA0AAAAEAAUAAAAIAA4AAAAEAAUAAAALAA4AAAAEAAUAAAAMAA4AAAACAAYAAAANAA0AAAACAAYAAAAOAA0AAAAEAAUAAAAPAA0AAAAEAAUAAAAQAA0AAAAEAAUAAAARAAwAAAACAAUAAAATAAwAAAACAAUAAAAVAAsAAAACAAYAAAAWAAsAAAAEAAUAAAAUAAwAAAACAAYAAAAQAAwAAAACAAMAAAAKAA4AAAAEAAUAAAAJAA4AAAAEAAUAAAAFAA4AAAACAAYAAAAGAA4AAAAEAAUAAAAHAA4AAAAEAAUAAAANAA4AAAACAAYAAAAOAA4AAAACAAYAAAAPAA4AAAACAAYAAAASAA0AAAACAAYAAAATAA0AAAAEAAUAAAAUAA0AAAACAAYAAAAVAA0AAAACAAYAAAAWAA0AAAACAAYAAAAXAA0AAAAEAAUAAAALAA0AAAAEAAUAAAAMAA0AAAACAAYAAAAPAAwAAAACAAMAAAAXAAsAAAAEAAUAAAAYAAsAAAAEAAUAAAAZAAsAAAACAAYAAAAZAAwAAAACAAYAAAAYAAwAAAAEAAUAAAAZAA4AAAAEAAUAAAAXAA4AAAAEAAUAAAAWAA4AAAAEAAUAAAAVAA4AAAACAAYAAAAUAA4AAAACAAYAAAATAA4AAAACAAYAAAASAA4AAAACAAYAAAAYAA4AAAACAAYAAAARAA4AAAACAAYAAAAEAA0AAAACAAYAAAD//w4AAAAEAAEAAAABAAkAAAACAAEAAAAIAAwAAAACAAEAAAAOAAwAAAACAAMAAAANAAwAAAACAAMAAAAMAAwAAAACAAMAAAAUAAoAAAABAAUAAAAWAAoAAAACAAYAAAAXAAcAAAACAAUAAAAYAAkAAAACAAYAAAAYAAoAAAACAAYAAAAXAAoAAAACAAYAAAAXAAkAAAACAAYAAAAZAAkAAAACAAYAAAAZAAoAAAACAAYAAAAQAA4AAAAEAAUAAAARAA0AAAACAAYAAAASAAwAAAACAAUAAAAVAAoAAAACAAYAAAAWAAkAAAACAAUAAAAXAAgAAAAEAAEAAAAUAAsAAAAFAAEAAAAVAAwAAAAEAAUAAAAWAAwAAAACAAYAAAAXAAwAAAAEAAUAAAAYAA0AAAACAAYAAAAZAA0AAAAEAAUAAAAAAAwAAAACAAYAAAACAAwAAAACAAYAAAADAAwAAAAEAAUAAAAEAAwAAAACAAYAAAAFAAwAAAAEAAUAAAAGAAwAAAAEAAUAAAAGAA0AAAACAAYAAAAHAA0AAAACAAYAAAAIAA0AAAAEAAUAAAAJAA0AAAACAAYAAAAKAAwAAAACAAEAAAD//wwAAAAFAAAAAAD//wsAAAAEAAAAAAAAAAsAAAAEAAUAAAABAAsAAAACAAYAAAABAAwAAAACAAYAAAAYAAcAAAACAAUAAAAZAAcAAAADAAUAAAAYAAgAAAACAAYAAAAZAAgAAAACAAYAAAAVAAkAAAACAAUAAAADAAsAAAACAAYAAAAEAAsAAAACAAEAAAAFAAsAAAACAAEAAAAGAAsAAAACAAEAAAACAAoAAAACAAEAAAADAAoAAAACAAEAAAARAAcAAAAOAAcAAAASAAcAAAAOAAcAAAAQAAcAAAAOAAcAAAAIAAYAAAANAAcAAAATAAcAAAAPAAcAAAAMAAYAAAAPAAcAAAALAAYAAAAOAAcAAAAKAAYAAAAOAAcAAAABAAgAAAARAAYAAAABAAcAAAAQAAUAAAABAAYAAAARAAUAAAABAAUAAAAQAAUAAAAAAAQAAAASAAIAAAABAAQAAAASAAIAAAACAAQAAAASAAIAAAD//wQAAAARAAIAAAAEAAQAAAATAAIAAAADAAQAAAASAAIAAAAAAAMAAAASAAEAAAABAAMAAAASAAEAAAACAAMAAAASAAEAAAADAAMAAAASAAAAAAAEAAMAAAATAAAAAAD//wMAAAARAAEAAAD//wIAAAARAAEAAAD//wEAAAARAAEAAAAAAAEAAAASAAEAAAAAAAIAAAASAAEAAAABAAEAAAASAAEAAAABAAIAAAASAAEAAAACAAEAAAASAAEAAAACAAIAAAASAAEAAAACAAYAAAATAAQAAAAJAAYAAAAOAAcAAAAJAAwAAAACAAEAAAAPAAcAAAANAAcAAAA=")
tile_set = ExtResource("9_yeq8b")
[node name="TerrainDetailsA" type="TileMapLayer" parent="Level/TerrainTileMaps" unique_id=1513560262]
z_index = 1
position = Vector2(9, 9)
tile_map_data = PackedByteArray("AAADAA0AAAADAAYAAAABAAwAAAABAAYAAAABAA0AAAABAAYAAAACAAsAAAACAAEAAAADAAsAAAADAAEAAAACAAwAAAACAAYAAAACAA4AAAACAAYAAAAXAAsAAAADAAUAAAAXAAwAAAADAAYAAAAXAA0AAAADAAYAAAAXAA4AAAADAAYAAAAWAAoAAAAEAAcAAAAWAAsAAAACAAUAAAAWAAwAAAAEAAUAAAAWAA0AAAACAAYAAAAWAA4AAAAEAAUAAAAVAAsAAAABAAUAAAAVAAwAAAABAAYAAAAVAA0AAAABAAYAAAAVAA4AAAABAAYAAAAQAA4AAAADAAYAAAAOAA4AAAAEAAUAAAAPAA4AAAACAAYAAAAQAA0AAAADAAMAAAANAA4AAAABAAYAAAAOAAwAAAAEAAYAAAAPAA0AAAACAAMAAAAOAA0AAAACAAMAAAANAA0AAAABAAMAAAAKAAMAAAARAAAAAAALAAMAAAASAAAAAAAMAAMAAAATAAAAAAAOAAIAAAARAAAAAAAPAAIAAAASAAAAAAADAAwAAAADAAYAAAAQAAIAAAATAAAAAAACAA0AAAAEAAUAAAABAA4AAAABAAYAAAADAA4AAAADAAYAAAABAAsAAAABAAEAAAAUAPz/AAASAAcAAAA=")
tile_set = ExtResource("9_yeq8b")
[node name="TerrainDetailsB" type="TileMapLayer" parent="Level/TerrainTileMaps" unique_id=478648603]
z_index = 2
position = Vector2(9, 9)
tile_map_data = PackedByteArray("AAAGAAwAAAADAAEAAAAGAA0AAAADAAYAAAAFAA4AAAACAAYAAAAFAAsAAAAFAAYAAAAFAAwAAAACAAEAAAAFAA0AAAAEAAUAAAAGAA4AAAADAAYAAAADAAwAAAABAAEAAAADAA0AAAABAAYAAAADAA4AAAABAAYAAAAEAAwAAAACAAEAAAAEAA0AAAACAAYAAAAEAA4AAAAEAAUAAAA=")
tile_set = ExtResource("9_yeq8b")
[node name="ActorsAndCharacters" type="Node2D" parent="Level" unique_id=2036761714]
[node name="Player" parent="Level/ActorsAndCharacters" unique_id=1343528246 instance=ExtResource("10_invaa")]
z_index = 10
position = Vector2(144, 215)
rotation = -3.1415927
scale = Vector2(1, -1)
[node name="PrefabInstances" type="Node2D" parent="Level" unique_id=2013556771]
[node name="Structures" type="Node2D" parent="Level/PrefabInstances" unique_id=1811607442]
[node name="Hazards" type="Node2D" parent="Level/PrefabInstances" unique_id=888595377]
[node name="Spikes_01" parent="Level/PrefabInstances/Hazards" unique_id=1025977450 instance=ExtResource("11_88b2t")]
z_index = 5
position = Vector2(270, 216)
[node name="Spikes_02" parent="Level/PrefabInstances/Hazards" unique_id=1262276584 instance=ExtResource("11_88b2t")]
z_index = 5
position = Vector2(335, 127)
[node name="SpikesGrey_01" parent="Level/PrefabInstances/Hazards" unique_id=214833071 instance=ExtResource("12_0ydwj")]
z_index = 5
position = Vector2(109, 199)
[node name="SpikesGrey_02" parent="Level/PrefabInstances/Hazards" unique_id=458791480 instance=ExtResource("12_0ydwj")]
z_index = 5
position = Vector2(331, 216)
[node name="Enemies" type="Node2D" parent="Level/PrefabInstances" unique_id=93211253]
[node name="BulletRain_01" parent="Level/PrefabInstances/Enemies" unique_id=1814844955 instance=ExtResource("20_rain")]
position = Vector2(234, 0)
[node name="Walker_01" parent="Level/PrefabInstances/Enemies" unique_id=2126159099 instance=ExtResource("21_walker")]
position = Vector2(216, 216)
[node name="Walker_02" parent="Level/PrefabInstances/Enemies" unique_id=583910022 instance=ExtResource("21_walker")]
position = Vector2(306, 216)
start_direction = 1
[node name="Walker_03" parent="Level/PrefabInstances/Enemies" unique_id=105799738 instance=ExtResource("21_walker")]
position = Vector2(90, 198)
start_direction = 1
[node name="Interactables" type="Node2D" parent="Level/PrefabInstances" unique_id=769532488]
[node name="SpringRed_01" parent="Level/PrefabInstances/Interactables" unique_id=34192139 instance=ExtResource("15_usk20")]
visible = false
z_index = 5
position = Vector2(448, 125)
[node name="Door_01" parent="Level/PrefabInstances/Interactables" unique_id=1264725697 instance=ExtResource("16_y8u0o")]
z_index = 5
position = Vector2(288, 37)
[node name="Collectibles" type="Node2D" parent="Level/PrefabInstances" unique_id=2086540192]
[node name="CoinGoldRound_01" parent="Level/PrefabInstances/Collectibles" unique_id=926105584 instance=ExtResource("17_16ffm")]
z_index = 5
position = Vector2(432, 127)
[node name="CoinGoldRound_02" parent="Level/PrefabInstances/Collectibles" unique_id=1674416025 instance=ExtResource("17_16ffm")]
z_index = 5
position = Vector2(67, 184)
[node name="CoinGoldRound_03" parent="Level/PrefabInstances/Collectibles" unique_id=1087706364 instance=ExtResource("17_16ffm")]
z_index = 5
position = Vector2(182, 109)
[node name="KeyGold_01" parent="Level/PrefabInstances/Collectibles" unique_id=2060959680 instance=ExtResource("18_kvrlk")]
z_index = 5
position = Vector2(145, 220)
[node name="Diamond_01" parent="Level/PrefabInstances/Collectibles" unique_id=909237898 instance=ExtResource("19_85fmh")]
z_index = 5
position = Vector2(399, 161)
[node name="Diamond_02" parent="Level/PrefabInstances/Collectibles" unique_id=234727210 instance=ExtResource("19_85fmh")]
z_index = 5
position = Vector2(204, 217)
[node name="Diamond_03" parent="Level/PrefabInstances/Collectibles" unique_id=1261326529 instance=ExtResource("19_85fmh")]
z_index = 5
position = Vector2(288, 125)
[node name="Signs" type="Node2D" parent="Level/PrefabInstances" unique_id=113197076]
-88
View File
@@ -1,88 +0,0 @@
[gd_scene load_steps=9 format=3]
[ext_resource type="Texture2D" path="res://assets/game/backgrounds/cloud_left.png" id="1_cloud1"]
[ext_resource type="Texture2D" path="res://assets/game/backgrounds/cloud_trees.png" id="2_cloud2"]
[ext_resource type="Texture2D" path="res://assets/game/backgrounds/cloud_hills.png" id="3_cloud3"]
[ext_resource type="Texture2D" path="res://assets/game/backgrounds/cloud_right.png" id="4_cloud4"]
[ext_resource type="TileSet" path="res://assets/game/tilesets/kenney_terrain_tileset.tres" id="5_tileset"]
[ext_resource type="PackedScene" path="res://scenes/player.tscn" id="6_player"]
[ext_resource type="PackedScene" path="res://scenes/hud.tscn" id="7_hud"]
[ext_resource type="PackedScene" path="res://scenes/death_screen.tscn" id="8_death"]
[ext_resource type="PackedScene" path="res://scenes/level_clear.tscn" id="9_clear"]
[ext_resource type="Script" path="res://scripts/hidden_passages.gd" id="10_hidden"]
[node name="Main" type="Node2D"]
[node name="HUD" parent="." instance=ExtResource("7_hud")]
[node name="DeathScreen" parent="." instance=ExtResource("8_death")]
[node name="LevelClear" parent="." instance=ExtResource("9_clear")]
[node name="Level" type="Node2D" parent="."]
[node name="Background" type="Node2D" parent="Level"]
[node name="SkyColor" type="Polygon2D" parent="Level/Background"]
z_index = -30
color = Color(0.8509804, 0.9411765, 0.9372549, 1)
polygon = PackedVector2Array(0, 0, 468, 0, 468, 270, 0, 270)
[node name="CloudBand_01" type="Sprite2D" parent="Level/Background"]
z_index = -20
texture_filter = 1
position = Vector2(12, 150)
texture = ExtResource("1_cloud1")
[node name="CloudBand_02" type="Sprite2D" parent="Level/Background"]
z_index = -20
texture_filter = 1
position = Vector2(36, 150)
texture = ExtResource("2_cloud2")
[node name="CloudBand_03" type="Sprite2D" parent="Level/Background"]
z_index = -20
texture_filter = 1
position = Vector2(60, 150)
texture = ExtResource("3_cloud3")
[node name="CloudBand_04" type="Sprite2D" parent="Level/Background"]
z_index = -20
texture_filter = 1
position = Vector2(84, 150)
texture = ExtResource("4_cloud4")
[node name="TerrainTileMaps" type="Node2D" parent="Level"]
script = ExtResource("10_hidden")
hidden_cells = []
[node name="TerrainBase" type="TileMapLayer" parent="Level/TerrainTileMaps"]
position = Vector2(9, 9)
tile_set = ExtResource("5_tileset")
[node name="TerrainDetailsA" type="TileMapLayer" parent="Level/TerrainTileMaps"]
position = Vector2(9, 9)
z_index = 1
tile_set = ExtResource("5_tileset")
[node name="TerrainDetailsB" type="TileMapLayer" parent="Level/TerrainTileMaps"]
position = Vector2(9, 9)
z_index = 2
tile_set = ExtResource("5_tileset")
[node name="ActorsAndCharacters" type="Node2D" parent="Level"]
[node name="Player" parent="Level/ActorsAndCharacters" instance=ExtResource("6_player")]
position = Vector2(102, 132)
[node name="PrefabInstances" type="Node2D" parent="Level"]
[node name="Structures" type="Node2D" parent="Level/PrefabInstances"]
[node name="Hazards" type="Node2D" parent="Level/PrefabInstances"]
[node name="Interactables" type="Node2D" parent="Level/PrefabInstances"]
[node name="Collectibles" type="Node2D" parent="Level/PrefabInstances"]
[node name="Signs" type="Node2D" parent="Level/PrefabInstances"]
+6 -11
View File
@@ -24,8 +24,8 @@
[node name="DeathScreen" parent="." unique_id=1952687359 instance=ExtResource("22_death")]
[node name="LevelClear" parent="." instance=ExtResource("26_clear")]
next_level_scene = "res://scenes/level_2.tscn"
[node name="LevelClear" parent="." unique_id=573996737 instance=ExtResource("26_clear")]
next_level_scene = "res://scenes/level2.tscn"
[node name="Level" type="Node2D" parent="." unique_id=1678372922]
@@ -212,15 +212,10 @@ position = Vector2(270, 18)
z_index = 5
position = Vector2(325, 108)
[node name="Door_01" parent="Level/PrefabInstances/Interactables" instance=ExtResource("25_door")]
[node name="Door_01" parent="Level/PrefabInstances/Interactables" unique_id=1439478654 instance=ExtResource("25_door")]
z_index = 5
position = Vector2(324, 216)
[node name="Door_02" parent="Level/PrefabInstances/Interactables" instance=ExtResource("25_door")]
z_index = 5
position = Vector2(414, 108)
atlas_region = Rect2(198, 90, 18, 18)
[node name="Collectibles" type="Node2D" parent="Level/PrefabInstances" unique_id=2086540192]
[node name="CoinGoldRound_01" parent="Level/PrefabInstances/Collectibles" unique_id=926105584 instance=ExtResource("17_rmlt8")]
@@ -239,15 +234,15 @@ position = Vector2(315, 63)
z_index = 5
position = Vector2(126, 144)
[node name="Diamond_01" parent="Level/PrefabInstances/Collectibles" instance=ExtResource("24_diamond")]
[node name="Diamond_01" parent="Level/PrefabInstances/Collectibles" unique_id=879831603 instance=ExtResource("24_diamond")]
z_index = 5
position = Vector2(90, 18)
[node name="Diamond_02" parent="Level/PrefabInstances/Collectibles" instance=ExtResource("24_diamond")]
[node name="Diamond_02" parent="Level/PrefabInstances/Collectibles" unique_id=1537151440 instance=ExtResource("24_diamond")]
z_index = 5
position = Vector2(414, 54)
[node name="Diamond_03" parent="Level/PrefabInstances/Collectibles" instance=ExtResource("24_diamond")]
[node name="Diamond_03" parent="Level/PrefabInstances/Collectibles" unique_id=2052535476 instance=ExtResource("24_diamond")]
z_index = 5
position = Vector2(270, 144)
+22
View File
@@ -0,0 +1,22 @@
[gd_scene load_steps=4 format=3]
[ext_resource type="Script" path="res://scripts/bullet.gd" id="1_bullet"]
[sub_resource type="CircleShape2D" id="Shape_bullet"]
radius = 3.0
[node name="Bullet" type="Area2D" groups=["bullet"]]
collision_layer = 4
collision_mask = 2
script = ExtResource("1_bullet")
[node name="Sprite" type="Sprite2D" parent="."]
texture_filter = 1
[node name="HitBox" type="CollisionShape2D" parent="."]
shape = SubResource("Shape_bullet")
[node name="Probe" type="RayCast2D" parent="."]
collision_mask = 3
hit_from_inside = true
target_position = Vector2(-12, 0)
+7
View File
@@ -0,0 +1,7 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/bullet_field.gd" id="1_field"]
[node name="BulletField" type="Node2D"]
z_index = 5
script = ExtResource("1_field")
+6
View File
@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/bullet_rain.gd" id="1_rain"]
[node name="BulletRain" type="Node2D"]
script = ExtResource("1_rain")
+19
View File
@@ -0,0 +1,19 @@
[gd_scene load_steps=3 format=3]
[ext_resource type="Script" path="res://scripts/laser.gd" id="1_laser"]
[sub_resource type="RectangleShape2D" id="Shape_laser"]
size = Vector2(90, 6)
[node name="Laser" type="Area2D" groups=["hazard"]]
collision_layer = 4
collision_mask = 2
script = ExtResource("1_laser")
[node name="Beam" type="Polygon2D" parent="."]
color = Color(1, 0.25, 0.25, 1)
polygon = PackedVector2Array(0, -3, 90, -3, 90, 3, 0, 3)
[node name="HitBox" type="CollisionShape2D" parent="."]
position = Vector2(45, 0)
shape = SubResource("Shape_laser")
+35
View File
@@ -0,0 +1,35 @@
[gd_scene load_steps=5 format=3]
[ext_resource type="Texture2D" path="res://assets/game/vegetation/mushroom_red.png" id="1_tex"]
[ext_resource type="Script" path="res://scripts/walker.gd" id="2_walker"]
[sub_resource type="RectangleShape2D" id="Shape_body"]
size = Vector2(12, 16)
[sub_resource type="RectangleShape2D" id="Shape_touch"]
size = Vector2(16, 16)
[node name="Walker" type="CharacterBody2D" groups=["enemy"]]
z_index = 5
collision_layer = 4
collision_mask = 1
script = ExtResource("2_walker")
[node name="Sprite" type="Sprite2D" parent="."]
texture_filter = 1
texture = ExtResource("1_tex")
[node name="Collision" type="CollisionShape2D" parent="."]
shape = SubResource("Shape_body")
[node name="FloorProbe" type="RayCast2D" parent="."]
position = Vector2(-6, 7)
target_position = Vector2(0, 9)
collision_mask = 1
[node name="TouchArea" type="Area2D" parent="."]
collision_layer = 0
collision_mask = 2
[node name="TouchShape" type="CollisionShape2D" parent="TouchArea"]
shape = SubResource("Shape_touch")
+86
View File
@@ -0,0 +1,86 @@
extends Area2D
## 弹幕子弹:直线飞行,碰到玩家扣血。
## 贴图是代码生成的实心圆,不需要额外美术素材。
@export var speed: float = 70.0
@export var damage: int = 1
@export var lifetime: float = 8.0
@export var radius: int = 3
@export var bullet_color: Color = Color(1.0, 0.35, 0.7)
## 撞到地形/绳子之类的障碍就消失。
## 关掉(默认)的话子弹会**穿过一切**,只靠存活时间和出界来回收
@export var stop_on_obstacle: bool = false
## 掉到这个世界 y 以下就自动回收(默认在关卡底部之外)
@export var despawn_y: float = 340.0
var direction: Vector2 = Vector2.LEFT
var _age: float = 0.0
var _probe: RayCast2D = null
static var _cache: Dictionary = {}
## 生成(并缓存)一个实心圆贴图
static func circle_texture(r: int, color: Color) -> ImageTexture:
var key := "%d|%s" % [r, color.to_html()]
if _cache.has(key):
return _cache[key]
var size: int = r * 2 + 1
var img := Image.create_empty(size, size, false, Image.FORMAT_RGBA8)
img.fill(Color(0.0, 0.0, 0.0, 0.0))
var limit := float(r * r) + float(r) * 0.6
for y in size:
for x in size:
var dx := float(x - r)
var dy := float(y - r)
if dx * dx + dy * dy <= limit:
img.set_pixel(x, y, color)
var tex := ImageTexture.create_from_image(img)
_cache[key] = tex
return tex
func _ready() -> void:
add_to_group("bullet")
var sprite := get_node_or_null("Sprite") as Sprite2D
if sprite != null:
sprite.texture = circle_texture(radius, bullet_color)
var hit := get_node_or_null("HitBox") as CollisionShape2D
if hit != null and hit.shape is CircleShape2D:
var circle := (hit.shape as CircleShape2D).duplicate() as CircleShape2D
circle.radius = float(radius)
hit.shape = circle
_probe = get_node_or_null("Probe") as RayCast2D
if _probe != null:
_probe.enabled = stop_on_obstacle
body_entered.connect(_on_body_entered)
func launch(dir: Vector2, spd: float = -1.0) -> void:
direction = dir.normalized()
if spd > 0.0:
speed = spd
func _physics_process(delta: float) -> void:
_age += delta
if _age >= lifetime or global_position.y > despawn_y:
queue_free()
return
var step := direction * speed * delta
if stop_on_obstacle and _probe != null:
_probe.target_position = step * 1.5
_probe.force_raycast_update()
if _probe.is_colliding():
queue_free()
return
position += step
func _on_body_entered(body: Node2D) -> void:
if body.has_method("hurt"):
body.hurt(damage)
queue_free()
+1
View File
@@ -0,0 +1 @@
uid://c4v4jls42gsyk
+142
View File
@@ -0,0 +1,142 @@
extends Node2D
## 静态弹幕障碍:以自己为圆心,一圈一圈地摆上**固定不动、不会发射**的子弹。
##
## 两种形态:
## - solid = true :实心障碍,挡路、能踩上去(注意:会连小怪一起挡住)
## - solid = false:可以穿透,只做伤害判定 —— 配合 damage > 0 就是普通弹幕
##
## 落在实心地砖里、或跑出关卡范围的子弹会自动跳过。
const BULLET_SCRIPT := preload("res://scripts/bullet.gd")
@export var enabled: bool = true
## 每一圈的半径(单位:格)。默认 3 格、6 格、9 格
@export var radii_cells: Array[int] = [3, 6, 9]
## 每圈摆几颗
@export var per_ring: int = 20
## 一格多少像素(图块是 18)
@export var cell_size: float = 18.0
## 起始角度偏移(度),用来微调子弹摆在哪个方向
@export var angle_offset_deg: float = 0.0
## 子弹半径
@export var bullet_radius: int = 3
@export var bullet_color: Color = Color(1.0, 0.45, 0.85)
## 实心障碍:挡路、能踩上去。关掉的话就是可以穿透的纯弹幕
@export var solid: bool = false
## 碰到扣几颗心。0 = 不扣血
@export var damage: int = 1
## 超出这个范围的子弹不生成(默认整个关卡 468x270)
@export var bounds: Rect2 = Rect2(0, 0, 468, 270)
## 最多铺几圈
@export var max_rings: int = 3
## 跳过埋在地砖里的子弹
@export var skip_solid_ground: bool = true
var made: int = 0
var skipped_out: int = 0
var skipped_ground: int = 0
var _hurt_areas: Array = []
func _ready() -> void:
if not enabled:
return
# 等一帧,确保 TileMapLayer 都已经就绪
_build.call_deferred()
func _build() -> void:
var layers := _tile_layers()
var tex := BULLET_SCRIPT.circle_texture(bullet_radius, bullet_color)
made = 0
skipped_out = 0
skipped_ground = 0
_hurt_areas.clear()
for i in mini(radii_cells.size(), max_rings):
var radius: float = float(radii_cells[i]) * cell_size
for k in per_ring:
var ang := deg_to_rad(angle_offset_deg + 360.0 * float(k) / float(per_ring))
var pos := global_position + Vector2(cos(ang), sin(ang)) * radius
if not bounds.has_point(pos):
skipped_out += 1
continue
if skip_solid_ground and _in_solid(layers, pos):
skipped_ground += 1
continue
_make(pos, tex)
made += 1
## 每帧检查一次:站在子弹里也会持续扣血
## (不能只靠 body_entered —— 玩家本来就在子弹里的话不会触发)
func _physics_process(_delta: float) -> void:
if damage <= 0 or _hurt_areas.is_empty():
return
for area in _hurt_areas:
if not is_instance_valid(area):
continue
for body in area.get_overlapping_bodies():
if body.has_method("hurt"):
body.hurt(damage)
func _tile_layers() -> Array:
var root: Node = owner if owner != null else get_tree().current_scene
if root == null:
return []
return root.find_children("*", "TileMapLayer", true, false)
## 这个点是不是埋在实心地砖里
func _in_solid(layers: Array, pos: Vector2) -> bool:
for l in layers:
var cell: Vector2i = l.local_to_map(l.to_local(pos))
var sid: int = l.get_cell_source_id(cell)
if sid == -1:
continue
var source := l.tile_set.get_source(sid) as TileSetAtlasSource
if source == null:
continue
var data: TileData = source.get_tile_data(l.get_cell_atlas_coords(cell), 0)
if data != null and data.get_collision_polygons_count(0) > 0:
return true
return false
func _make(pos: Vector2, tex: Texture2D) -> void:
var body := StaticBody2D.new()
if solid:
body.collision_layer = 1
body.collision_mask = 1
else:
# 不实心:谁都挡不住,只靠下面的伤害区域起作用
body.collision_layer = 0
body.collision_mask = 0
add_child(body)
body.global_position = pos
var sprite := Sprite2D.new()
sprite.texture = tex
sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
body.add_child(sprite)
if solid:
var shape := CollisionShape2D.new()
var circle := CircleShape2D.new()
circle.radius = float(bullet_radius)
shape.shape = circle
body.add_child(shape)
if damage > 0:
var area := Area2D.new()
area.collision_layer = 4
area.collision_mask = 2
var hit := CollisionShape2D.new()
var circle2 := CircleShape2D.new()
circle2.radius = float(bullet_radius) + 1.0
hit.shape = circle2
area.add_child(hit)
body.add_child(area)
_hurt_areas.append(area)
+1
View File
@@ -0,0 +1 @@
uid://c751srku7fi1h
+64
View File
@@ -0,0 +1,64 @@
extends Node2D
## 子弹雨:从天上随机横坐标往下落子弹,密度可以调。
##
## 落下来的子弹**不会被任何东西挡住**(bullet 的 stop_on_obstacle 默认关掉),
## 会一直穿到关卡底下才回收。
const BULLET := preload("res://scenes/prefabs/enemies/bullet.tscn")
@export var enabled: bool = true
## 平均多少秒落一颗(数值越大越稀疏)
@export var interval: float = 0.2
## 间隔随机浮动范围(0 = 完全固定节奏)
@export var interval_jitter: float = 0.12
## 开场后第一次落下的延迟
@export var start_delay: float = 1.0
## 生成高度(相对自己的 y,负数 = 从屏幕上方外面)
@export var spawn_y: float = -30.0
## 水平撒落范围:以自己的 x 为中心,左右各一半
@export var span: float = 468.0
## 下落速度
@export var fall_speed: float = 70.0
@export var damage: int = 1
@export var bullet_color: Color = Color(0.55, 0.8, 1.0)
## 固定随机种子(0 = 每次运行都不同)
@export var random_seed: int = 0
var _timer: float = 0.0
var _rng := RandomNumberGenerator.new()
var dropped: int = 0
func _ready() -> void:
add_to_group("bullet_rain")
if random_seed != 0:
_rng.seed = random_seed
else:
_rng.randomize()
_timer = start_delay
func _physics_process(delta: float) -> void:
if not enabled:
return
_timer -= delta
if _timer > 0.0:
return
_timer = maxf(interval + _rng.randf_range(-interval_jitter, interval_jitter), 0.05)
drop()
func drop() -> void:
var bullet := BULLET.instantiate()
# 这几个属性必须在 add_child 之前设好
bullet.bullet_color = bullet_color
bullet.speed = fall_speed
bullet.damage = damage
bullet.stop_on_obstacle = false
get_parent().add_child(bullet)
bullet.global_position = Vector2(
global_position.x + _rng.randf_range(-span * 0.5, span * 0.5),
global_position.y + spawn_y)
bullet.launch(Vector2(0.0, 1.0), fall_speed)
dropped += 1
+1
View File
@@ -0,0 +1 @@
uid://dbpe6r76gao3w
+111
View File
@@ -0,0 +1,111 @@
extends CanvasLayer
## 「房★主★有★神★器★」:把两个调试功能(飞行 / 跳关)藏在一个彩蛋按钮里。
##
## 平时屏幕上只有右上角一个小小的「房★主★有★神★器★」按钮,
## 点一下才弹出面板,里面是飞行开关和跳关按钮。
## 面板打开时键盘 1/2/3 也能跳关,Esc 或再点一次按钮关闭。
## 正式版里也能用(这是刻意留的彩蛋,不是调试残留)。
@export var levels: Array[String] = [
"res://scenes/main.tscn",
"res://scenes/level2.tscn",
"res://scenes/level3.tscn",
]
## 是否显示那个彩蛋按钮(关掉的话就只能靠别的方式打开面板了)
@export var show_button: bool = true
var _panel: Control = null
var _open_button: Button = null
var _fly_button: Button = null
var _player: Node = null
var _open: bool = false
func _ready() -> void:
visible = true
_panel = get_node_or_null("Panel")
_open_button = get_node_or_null("OpenButton")
_fly_button = get_node_or_null("Panel/VBox/FlyButton")
if _open_button != null:
_open_button.visible = show_button
_open_button.pressed.connect(toggle_panel)
var close := get_node_or_null("Panel/VBox/CloseButton")
if close != null:
close.pressed.connect(toggle_panel)
if _fly_button != null:
_fly_button.pressed.connect(toggle_fly)
var row := get_node_or_null("Panel/VBox/Row")
if row != null:
var index := 1
for path in levels:
var btn := Button.new()
btn.text = str(index)
btn.custom_minimum_size = Vector2(26.0, 24.0)
btn.focus_mode = Control.FOCUS_NONE
btn.add_theme_font_size_override("font_size", 12)
btn.pressed.connect(jump_to.bind(path))
row.add_child(btn)
index += 1
if _panel != null:
_panel.visible = false
func _process(_delta: float) -> void:
if _fly_button == null:
return
if _player == null or not is_instance_valid(_player):
_player = get_tree().get_first_node_in_group("player")
if _player == null:
_fly_button.text = "飞行(F)"
return
_fly_button.text = "飞行:开(F)" if bool(_player.flying) else "飞行:关(F)"
func _unhandled_input(event: InputEvent) -> void:
if not _open:
return
var key := event as InputEventKey
if key == null or not key.pressed or key.echo:
return
if key.physical_keycode == KEY_ESCAPE:
toggle_panel()
return
var n := 0
match key.physical_keycode:
KEY_1:
n = 1
KEY_2:
n = 2
KEY_3:
n = 3
KEY_4:
n = 4
KEY_5:
n = 5
if n >= 1 and n <= levels.size():
jump_to(levels[n - 1])
func toggle_panel() -> void:
_open = not _open
if _panel != null:
_panel.visible = _open
func toggle_fly() -> void:
if _player == null or not is_instance_valid(_player):
_player = get_tree().get_first_node_in_group("player")
if _player == null or not _player.has_method("set_flying"):
return
_player.set_flying(not bool(_player.flying))
func jump_to(path: String) -> void:
get_tree().paused = false
get_tree().change_scene_to_file(path)
+1
View File
@@ -0,0 +1 @@
uid://bexl5365w3qgq
+90
View File
@@ -0,0 +1,90 @@
extends Area2D
## 激光。两种模式:
## - always_on = true(默认):**固定障碍** —— 一直亮着,碰到就扣血,只能绕开或用钥匙传送穿过去
## - always_on = false:循环「蓄力闪烁 -> 发射 -> 熄灭」,需要卡时间通过
## 光束从自己的原点沿 +X 方向伸出,想改朝向就转节点本身。
enum State { WAIT, WARMUP, FIRING }
## 固定障碍模式(一直亮着)
@export var always_on: bool = true
@export var length: float = 90.0
@export var thickness: float = 6.0
@export var damage: int = 1
## 蓄力(闪烁警告)时长,只有循环模式用
@export var warmup: float = 0.8
## 发射持续时长,只有循环模式用
@export var fire_time: float = 0.35
## 两次发射之间的间隔,只有循环模式用
@export var cooldown: float = 1.8
## 开场后第一次发射前的延迟,只有循环模式用
@export var start_delay: float = 1.0
@export var laser_color: Color = Color(1.0, 0.25, 0.25)
@export var warn_color: Color = Color(1.0, 0.8, 0.4)
var _state: int = State.WAIT
var _timer: float = 0.0
var _first: bool = true
var _beam: Polygon2D = null
func _ready() -> void:
add_to_group("hazard")
_beam = get_node_or_null("Beam") as Polygon2D
var half := thickness * 0.5
if _beam != null:
_beam.polygon = PackedVector2Array([
Vector2(0.0, -half), Vector2(length, -half),
Vector2(length, half), Vector2(0.0, half)])
_beam.color = laser_color
_beam.visible = always_on
var hit := get_node_or_null("HitBox") as CollisionShape2D
if hit != null:
var rect := RectangleShape2D.new()
rect.size = Vector2(length, thickness)
hit.shape = rect
hit.position = Vector2(length * 0.5, 0.0)
func _physics_process(delta: float) -> void:
if always_on:
if _beam != null:
_beam.visible = true
_beam.color = laser_color
_hurt_overlaps()
return
_timer += delta
match _state:
State.WAIT:
var wait: float = start_delay if _first else cooldown
if _timer >= wait:
_first = false
_state = State.WARMUP
_timer = 0.0
State.WARMUP:
if _beam != null:
_beam.color = warn_color
_beam.visible = fmod(_timer, 0.16) < 0.08
if _timer >= warmup:
_state = State.FIRING
_timer = 0.0
if _beam != null:
_beam.color = laser_color
_beam.visible = true
State.FIRING:
_hurt_overlaps()
if _timer >= fire_time:
_state = State.WAIT
_timer = 0.0
if _beam != null:
_beam.visible = false
## 每帧检查压在光柱里的东西(不能只靠 body_entered,
## 因为玩家可能本来就在光柱里,那样不会触发 entered)
func _hurt_overlaps() -> void:
for body in get_overlapping_bodies():
if body.has_method("hurt"):
body.hurt(damage)
+1
View File
@@ -0,0 +1 @@
uid://de3i3dmyi8bjw
+7 -3
View File
@@ -357,7 +357,8 @@ func _build_throw_visuals() -> void:
# ---------------------------------------------------------------- 测试用飞行
func fly_available() -> bool:
return debug_fly and OS.is_debug_build()
# 这是藏在「房★主★有★神★器★」面板里的隐藏技能,正式版里也能用
return debug_fly
func set_flying(on: bool) -> void:
@@ -368,10 +369,13 @@ func set_flying(on: bool) -> void:
func handle_fly_movement() -> void:
# get_axis(负方向, 正方向),屏幕坐标里 y 向下为正,所以空格(跳跃)要放在负方向
# 和以前一样:空格(跳跃键)上升、S 下降。方向键上下也顺便能用
var vertical := Input.get_axis("jump", "squat")
if is_zero_approx(vertical):
vertical = Input.get_axis("ui_up", "ui_down")
var direction := Vector2(
Input.get_axis("move_left", "move_right"),
Input.get_axis("jump", "squat")
vertical
)
if direction == Vector2.ZERO:
velocity = Vector2.ZERO
+60
View File
@@ -0,0 +1,60 @@
extends CharacterBody2D
## 小怪:只在平台上左右巡逻,**不发射任何东西**,也**打不死**。
## 作用纯粹是挡路 + 碰到扣血,逼玩家走位。
## 弹幕交给专门的「弹幕源」节点负责。
@export_category("巡逻")
@export var speed: float = 28.0
## 巡逻方向:-1 向左,1 向右
@export var start_direction: int = -1
## 掉头后的冷却时间:防止贴着墙时每帧都掉头、结果原地抖动
@export var turn_cooldown: float = 0.25
## 速度设成 0 就变成待在原地不动的障碍
@export var damage: int = 1
var direction: int = -1
var _turn_timer: float = 0.0
@onready var sprite: Sprite2D = $Sprite
@onready var floor_probe: RayCast2D = $FloorProbe
@onready var touch_area: Area2D = $TouchArea
func _ready() -> void:
add_to_group("enemy")
direction = -1 if start_direction < 0 else 1
_apply_direction()
touch_area.body_entered.connect(_on_touch)
func _physics_process(delta: float) -> void:
if _turn_timer > 0.0:
_turn_timer -= delta
if not is_on_floor():
velocity += get_gravity() * delta
velocity.x = float(direction) * speed
# 撞到墙 或 前方脚下没有地板 -> 掉头(带冷却,掉头后先走一段再说)
if _turn_timer <= 0.0:
if is_on_wall() or (is_on_floor() and not floor_probe.is_colliding()):
direction = -direction
_turn_timer = turn_cooldown
_apply_direction()
move_and_slide()
func _apply_direction() -> void:
if sprite != null:
sprite.flip_h = direction > 0
if floor_probe != null:
floor_probe.position.x = float(direction) * 6.0
func _on_touch(body: Node2D) -> void:
# 打不死:踩上去也一样只是扣血
if body.has_method("hurt"):
body.hurt(damage)
+1
View File
@@ -0,0 +1 @@
uid://b0lnhlnkjhxug