Compare commits
23 Commits
c397aff026
...
1429b879e8
Author | SHA1 | Date | |
---|---|---|---|
1429b879e8 | |||
c1a3bfa266 | |||
1ef0f08e2f | |||
5b0803f85f | |||
38c0bdff2a | |||
4f16214885 | |||
d3f2076b11 | |||
cff0507d25 | |||
2a5629fc86 | |||
8c02a0548c | |||
5d9643476f | |||
451d6d0849 | |||
3b2ba7440c | |||
ef58f3885b | |||
05ac2dec60 | |||
e10e22e3bd | |||
56d3ae4964 | |||
cceda0bd1f | |||
4459b6a88b | |||
bac2ae1992 | |||
e54b62cb82 | |||
074f11e971 | |||
219f207472 |
3
.gitignore
vendored
|
@ -1,8 +1,9 @@
|
|||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
export_presets.cfg
|
||||
.idea/
|
||||
.vs/
|
||||
.vscode/
|
||||
export_presets.cfg
|
||||
*.translation
|
||||
*.user
|
||||
AppConfig.yaml
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Godot.NET.Sdk/4.3.0-rc.1">
|
||||
<Project Sdk="Godot.NET.Sdk/4.3.0">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework>
|
||||
|
@ -11,5 +11,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="15.1.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="6.0.29" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.8" />
|
||||
</ItemGroup>
|
||||
</Project>
|
30
README.md
|
@ -11,7 +11,7 @@ A pixel cross-platform roguelite game.
|
|||
## Recent Development progress
|
||||
|
||||
| Task | status |
|
||||
| ----------------------------------------------------------- | ------------------ |
|
||||
|------------------------------------------|-------------|
|
||||
| Randomly generated map | complete |
|
||||
| loot | complete |
|
||||
| Support still out of the knapsack system | complete |
|
||||
|
@ -30,12 +30,20 @@ Level graph editor
|
|||
## Run the project locally
|
||||
|
||||
#### Download engine
|
||||
|
||||
1. Download [Godot Engine .Net](https://godotengine.org/).
|
||||
|
||||
After downloading the engine, you will need to download an additional export template to export as an executable program.
|
||||
After downloading the engine, you will need to download an additional export template to export as an executable
|
||||
program.
|
||||
|
||||
2. Download [.NetSDK](https://dotnet.microsoft.com/download).
|
||||
|
||||
Ubuntu or Linux Mint install the .net 8.0 Sdk.
|
||||
|
||||
```
|
||||
apt install dotnet-sdk-8.0
|
||||
```
|
||||
|
||||
#### Clone project
|
||||
|
||||
Enter the following command in your working directory:
|
||||
|
@ -55,7 +63,17 @@ data/*
|
|||
#### Custom feature
|
||||
|
||||
- **disableVersionIsolation** Disable version isolation.
|
||||
- **enableMod** Experimental feature, the game loads dll files and pck files in the mod directory when the mod is enabled. Due to the isolation of AssemblyLoadContext, the main game content cannot be accessed from within the Mod for the time being.
|
||||
- **enableMod** Experimental feature, the game loads dll files and pck files in the mod directory when the mod is
|
||||
enabled. Due to the isolation of AssemblyLoadContext, the main game content cannot be accessed from within the Mod for
|
||||
the time being.
|
||||
|
||||
#### Run the console on Linux
|
||||
|
||||
Enter the following command in the directory where the game is located:
|
||||
|
||||
```
|
||||
./Traveler.sh
|
||||
```
|
||||
|
||||
## Configuring Openobserve
|
||||
|
||||
|
@ -87,13 +105,15 @@ After you have set up an openobserve server, follow the following steps to confi
|
|||
|
||||
## Participate in translation
|
||||
|
||||
The project is prepared for localization at the beginning of writing. You can edit the csv file in the locals directory. To modify and add new translations.
|
||||
The project is prepared for localization at the beginning of writing. You can edit the csv file in the locals directory.
|
||||
To modify and add new translations.
|
||||
|
||||
## License
|
||||
|
||||
[GPL-3.0 license](LICENSE)
|
||||
|
||||
Support commercial, anyone can modify, build, and sell or distribute for free. For all derivative versions of this project, under the GPL, you shall **retain the author copyright** and **publish the modified source code**.
|
||||
Support commercial, anyone can modify, build, and sell or distribute for free. For all derivative versions of this
|
||||
project, under the GPL, you shall **retain the author copyright** and **publish the modified source code**.
|
||||
|
||||
> Note: You have the right to sell the modified version, but not the original.
|
||||
>
|
||||
|
|
15
README_JA.md
|
@ -34,6 +34,13 @@
|
|||
|
||||
2. ダウンロード [.NetSDK](https://dotnet.microsoft.com/download).
|
||||
|
||||
UbuntuやLinux Mintに。net8.0Sdkをインストールします。
|
||||
|
||||
```
|
||||
apt install dotnet-sdk-8.0
|
||||
```
|
||||
|
||||
|
||||
#### クローンプロジェクトです
|
||||
|
||||
作業リストに次の指示を入力します。
|
||||
|
@ -55,6 +62,14 @@ data/*
|
|||
- **disableVersionIsolation** 版孤立を無効化する。
|
||||
- **enableMod**実験的な機能、modが有効になっている場合、ゲームはmodディレクトリにdllファイルとpckファイルをロードします。assemblyloadcontextが分離されているため、当面の間、mod内からメインゲームのコンテンツにアクセスすることはできません。
|
||||
|
||||
#### Linux上でコンソールを動かします
|
||||
|
||||
ゲームの存在するディレクトリに以下のコマンドを入力します:
|
||||
|
||||
```
|
||||
./Traveler.sh
|
||||
```
|
||||
|
||||
## はいちOpenobserve
|
||||
|
||||
> これはオプションなので、Openobserveを設定しなくてもゲームは正常に動作します。
|
||||
|
|
20
README_ZH.md
|
@ -11,7 +11,7 @@
|
|||
## 近期研发进度
|
||||
|
||||
| 任务 | 状态 |
|
||||
| ----------------------------------------------------------- |----|
|
||||
|-----------|-----|
|
||||
| 随机生成地图 | 完成 |
|
||||
| 战利品 | 完成 |
|
||||
| 支持仍出的背包系统 | 完成 |
|
||||
|
@ -30,12 +30,19 @@
|
|||
## 在本地运行项目
|
||||
|
||||
#### 下载引擎
|
||||
|
||||
1. 下载[Godot Engine .Net](https://godotengine.org/)。
|
||||
|
||||
下载引擎后,您需要额外下载导出模板才能导出为可执行程序。
|
||||
|
||||
2. 下载 [.NetSDK](https://dotnet.microsoft.com/download).
|
||||
|
||||
Ubuntu或Linux Mint安装.net8.0Sdk。
|
||||
|
||||
```
|
||||
apt install dotnet-sdk-8.0
|
||||
```
|
||||
|
||||
#### 克隆项目
|
||||
|
||||
在您的工作目录输入以下指令:
|
||||
|
@ -57,6 +64,14 @@ data/*
|
|||
- **disableVersionIsolation** 禁用版本隔离。
|
||||
- **enableMod** 实验性功能,当启用模组时游戏会在mod目录加载dll文件和pck文件。由于AssemblyLoadContext的隔离性,暂时不能从Mod内访问主游戏内容。
|
||||
|
||||
#### 在Linux上运行控制台
|
||||
|
||||
在游戏所在目录输入以下命令:
|
||||
|
||||
```
|
||||
./Traveler.sh
|
||||
```
|
||||
|
||||
## 配置Openobserve
|
||||
|
||||
> 这是可选的操作,即使您不配置Openobserve,游戏也能正常运行。
|
||||
|
@ -95,7 +110,8 @@ openobserve用于在游戏发布后,持续收集日志和报警信息。
|
|||
|
||||
查看协议的中文翻译版本:[GPL-3.0 license 简体中文](LICENSE_ZH)
|
||||
|
||||
支持商用,任何人可修改,构建,并用于售卖或免费发布。对于此项目的所有衍生版本,根据GPL协议,您应当**保留作者版权**,以及**公开修改后的源代码**。
|
||||
支持商用,任何人可修改,构建,并用于售卖或免费发布。对于此项目的所有衍生版本,根据GPL协议,您应当**保留作者版权**,以及*
|
||||
*公开修改后的源代码**。
|
||||
|
||||
> 注意:您有权售卖修改后的版本,但不能售卖原版。
|
||||
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
- id: packsack
|
||||
scene_path: res://prefab/packsacks/packsack.tscn
|
||||
icon_path: res://sprites/packsack.png
|
||||
#Register an item container here.
|
||||
#在这里注册物品容器。
|
||||
#Note: The id must be the same as the item id in the scene. Otherwise, an ArgumentException will be thrown.
|
||||
#备注:id必须和场景内的物品id保持一致。否则会抛出ArgumentException。
|
||||
#After you declare the id of the item, add the corresponding localized text to the csv file in the locals' folder. For example, if the id is a corresponding name is item_a and the corresponding description is item_a_desc.
|
||||
#当您声明物品的id后,请在locals文件夹中的csv文件中添加相应的本地化文本。例如:id为a,则对应的名称为item_a,对应的描述为item_a_desc。
|
||||
|
||||
#portable_backpacks
|
||||
#便携式背包
|
||||
- id: portable_backpacks
|
||||
scene_path: res://prefab/packsacks/PortableBackpacks.tscn
|
||||
icon_path: res://sprites/PortableBackpacks.png
|
||||
max_stack_value: 1
|
|
@ -1,20 +1,17 @@
|
|||
- id: staff_of_the_undead
|
||||
scene_path: res://prefab/weapons/staffOfTheUndead.tscn
|
||||
icon_path: res://sprites/weapon/staffOfTheUndead_icon.png
|
||||
max_stack_value: 1
|
||||
custom_args:
|
||||
- name: UniqueName
|
||||
type: string
|
||||
value: 爱你!
|
||||
#Register your weapon here.
|
||||
#在这里注册你的武器。
|
||||
#Note: The id must be the same as the item id in the scene. Otherwise, an ArgumentException will be thrown.
|
||||
#备注:id必须和场景内的物品id保持一致。否则会抛出ArgumentException。
|
||||
#After you declare the id of the item, add the corresponding localized text to the csv file in the locals' folder. For example, if the id is a corresponding name is item_a and the corresponding description is item_a_desc.
|
||||
#当您声明物品的id后,请在locals文件夹中的csv文件中添加相应的本地化文本。例如:id为a,则对应的名称为item_a,对应的描述为item_a_desc。
|
||||
|
||||
- id: degraded_staff_of_the_undead
|
||||
scene_path: res://prefab/weapons/staffOfTheUndead.tscn
|
||||
icon_path: res://sprites/weapon/staffOfTheUndead_icon.png
|
||||
#staff_necromancy
|
||||
#死灵法杖
|
||||
# The Necromancy Staff will act differently on different difficulties.
|
||||
#死灵法杖在不同的难度下会有不同的行为。
|
||||
#The probability of generating monsters is as follows:Hard mode 100%, Normal mode 75%, easy mode 5%
|
||||
#以下是在击杀敌人后生成怪物的概率:困难模式100%,普通模式75%,简单模式5%
|
||||
- id: staff_necromancy
|
||||
scene_path: res://prefab/weapons/StaffNecromancy.tscn
|
||||
icon_path: res://sprites/weapon/StaffNecromancy_Icon.png
|
||||
max_stack_value: 1
|
||||
custom_args:
|
||||
- name: FiringIntervalAsMillisecond
|
||||
type: int
|
||||
value: 1000
|
||||
- name: UniqueName
|
||||
type: string
|
||||
value: item_staff_of_the_undead_desc
|
||||
|
|
|
@ -15,6 +15,7 @@ dest_files=["res://.godot/imported/ark-pixel-12px-proportional-zh_cn.ttf-478ad47
|
|||
Rendering=null
|
||||
antialiasing=1
|
||||
generate_mipmaps=false
|
||||
disable_embedded_bitmaps=true
|
||||
multichannel_signed_distance_field=false
|
||||
msdf_pixel_range=8
|
||||
msdf_size=48
|
||||
|
|
3
locals/Character.csv
Normal file
|
@ -0,0 +1,3 @@
|
|||
id,zh,en,ja
|
||||
character_necromancer,死灵法师,Necromancer,ネクロマンサー
|
||||
character_evil_crow,邪恶的乌鸦先生,Evil Mr. Raven,悪のミスター・レイヴン
|
|
17
locals/Character.csv.import
Normal file
|
@ -0,0 +1,17 @@
|
|||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://dwrygxaehoybb"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://locals/Character.zh.translation", "res://locals/Character.en.translation", "res://locals/Character.ja.translation"]
|
||||
|
||||
source_file="res://locals/Character.csv"
|
||||
dest_files=["res://locals/Character.zh.translation", "res://locals/Character.en.translation", "res://locals/Character.ja.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=true
|
||||
delimiter=0
|
|
@ -1,5 +1,5 @@
|
|||
id,zh,en,ja
|
||||
item_staff_of_the_undead,死灵法杖,StaffOfTheUndead,ネクロポリスの杖です
|
||||
item_staff_of_the_undead_desc,发射诅咒,可将敌人转化为邪恶的怪物。,Cast a curse that transforms enemies into evil monsters.,呪いを発射して、敵を邪悪な怪物に変えることができます。
|
||||
item_packsack,背包,Packsack,背嚢
|
||||
item_packsack_desc,可以装载更多物品的背包。,A backpack that can hold more items.,より多くのアイテムを保持できるバックパックです。
|
||||
item_staff_necromancy,死灵法杖,staffNecromancy,ネクロポリスの杖です
|
||||
item_staff_necromancy_desc,发射诅咒,可将敌人转化为邪恶的怪物。,Cast a curse that transforms enemies into evil monsters.,呪いを発射して、敵を邪悪な怪物に変えることができます。
|
||||
item_portable_backpacks,便携式背包,PortableBackpacks,ポータブルバックパック
|
||||
item_portable_backpacks_desc,为玩家提供9个物品槽。,Provides 9 item slots for the player.,プレイヤーに9つのアイテムスロットを提供します。
|
|
|
@ -110,3 +110,11 @@ log_dll_does_not_register_lifecycle_processor,位于{0}的dll文件,未注册
|
|||
log_mod_lifecycle_handler_not_implement_interface,位于{0}的dll文件,生命周期处理器未实现IModLifecycleHandler接口。,"The DLL file located at {0}, the lifecycle processor does not implement the IModLifecycleHandler interface.",{0}にあるDLLファイル、ライフサイクルプロセッサはIModLifecycleHandlerインターフェースを実装していません。
|
||||
log_dll_no_parameterless_constructor,位于{0}的dll文件,没有无参构造函数。,"The DLL file located at {0} does not have a parameterless constructor.",{0}にあるDLLファイルにはパラメータのないコンストラクタがありません。
|
||||
log_dll_type_length,位于{0}的dll文件内,共包含{1}个类型。,"The DLL file located at {0} contains a total of {1} types.",{0}にあるDLLファイルには合計{1}個のタイプが含まれています。
|
||||
log_item_pickup_disables_collision_damage,物品捡起禁用碰撞伤害。,Item pickup disables collision damage.,アイテムのピックアップが衝突によるダメージを無効にする
|
||||
log_item_thrown_restore_collision_damage,物品扔出恢复碰撞伤害。,Item Throw restores collision damage.,アイテム投げは衝突ダメージを回復する。
|
||||
log_after_no_longer_in_contact_with_any_tiles,不再与任何瓦片接触后,可以造成伤害。,"After no longer coming into contact with any tiles, it can cause damage",タイルと接触しなくなった後、損傷を引き起こす可能性があります。
|
||||
log_contact_with_tiles_disables_damage,与瓦片接触禁用伤害。,"Disabling damage on contact with tiles",タイルとの接触によるダメージの無効化
|
||||
log_hide_all_node,隐藏{0}个节点。,Hide {0} nodes.,{0}ノードを非表示にします。
|
||||
log_show_all_node,显示{0}个节点。,Show {0} nodes.,{0}ノードが表示されます。
|
||||
log_enter_the_screen,进入屏幕。,Enter screen,画面に移動。
|
||||
log_exit_the_screen,退出屏幕。,Exit screen,画面を終了します。
|
|
|
@ -1,4 +1,3 @@
|
|||
id,zh,en,ja
|
||||
de,的,"'s",の
|
||||
default_player_name,白纸,blankPaper,しらかみ
|
||||
item_prompt_debug,ID:{0}\n名称:{1}\n数量:{2}\n最大叠加数量:{3}\n数据类型:{4}\n描述:{5},ID: {0}\nName: {1}\nQuantity: {2}\nMaximum stacking quantity: {3}\nData type: {4}\nDescription:{5},id:{0}\n名称:{1}\nの数は最大{2}\nシナジー数:{3}\nデータタイプ:{4}\n述べ表わす:{5}
|
|
17
locals/Slogan.csv.import
Normal file
|
@ -0,0 +1,17 @@
|
|||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://m1i5vqn3aq7e"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://locals/Slogan.zh.translation", "res://locals/Slogan.en.translation", "res://locals/Slogan.ja.translation"]
|
||||
|
||||
source_file="res://locals/Slogan.csv"
|
||||
dest_files=["res://locals/Slogan.zh.translation", "res://locals/Slogan.en.translation", "res://locals/Slogan.ja.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=true
|
||||
delimiter=0
|
3
prefab/MainCamera.tscn
Normal file
|
@ -0,0 +1,3 @@
|
|||
[gd_scene format=3 uid="uid://briehpw3eg1i6"]
|
||||
|
||||
[node name="Camera2D" type="Camera2D"]
|
103
prefab/entitys/BlackenedAboriginalWarrior.tscn
Normal file
|
@ -0,0 +1,103 @@
|
|||
[gd_scene load_steps=11 format=3 uid="uid://dxx1vqpecig7i"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/character/AiCharacter.cs" id="1_ubaid"]
|
||||
[ext_resource type="Texture2D" uid="uid://c1yxhtiqrfvvp" path="res://sprites/character/BlackenedAboriginalWarrior.png" id="2_pcmq1"]
|
||||
[ext_resource type="Script" path="res://scripts/damage/DamageNumberNodeSpawn.cs" id="3_kiam3"]
|
||||
[ext_resource type="PackedScene" uid="uid://sqqfrmikmk5v" path="res://prefab/ui/HealthBar.tscn" id="4_gt388"]
|
||||
[ext_resource type="Script" path="res://scripts/bubble/BubbleMarker.cs" id="5_y2fh5"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_f8frw"]
|
||||
size = Vector2(40, 46)
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_a3myh"]
|
||||
size = Vector2(46, 65)
|
||||
|
||||
[sub_resource type="SpriteFrames" id="SpriteFrames_qumby"]
|
||||
animations = [{
|
||||
"frames": [{
|
||||
"duration": 1.0,
|
||||
"texture": ExtResource("2_pcmq1")
|
||||
}],
|
||||
"loop": true,
|
||||
"name": &"default",
|
||||
"speed": 5.0
|
||||
}]
|
||||
|
||||
[sub_resource type="CircleShape2D" id="CircleShape2D_c61vr"]
|
||||
radius = 153.0
|
||||
|
||||
[sub_resource type="CircleShape2D" id="CircleShape2D_fowd5"]
|
||||
radius = 172.29
|
||||
|
||||
[node name="BlackenedAboriginalWarrior" type="CharacterBody2D"]
|
||||
collision_layer = 64
|
||||
collision_mask = 38
|
||||
script = ExtResource("1_ubaid")
|
||||
CharacterName = "character_evil_crow"
|
||||
CanMutateAfterDeath = false
|
||||
MaxHp = 50
|
||||
CampId = "Mazoku"
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
position = Vector2(2, 10)
|
||||
shape = SubResource("RectangleShape2D_f8frw")
|
||||
|
||||
[node name="Area2DPickingArea" type="Area2D" parent="."]
|
||||
collision_layer = 0
|
||||
collision_mask = 8
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2DPickingArea"]
|
||||
position = Vector2(0, 5.5)
|
||||
shape = SubResource("RectangleShape2D_a3myh")
|
||||
|
||||
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
|
||||
sprite_frames = SubResource("SpriteFrames_qumby")
|
||||
|
||||
[node name="ItemMarker2D" type="Marker2D" parent="."]
|
||||
position = Vector2(15, 20)
|
||||
|
||||
[node name="AttackObstacleDetection" type="RayCast2D" parent="ItemMarker2D"]
|
||||
collision_mask = 2
|
||||
|
||||
[node name="DamageNumber" type="Marker2D" parent="."]
|
||||
position = Vector2(0, -32)
|
||||
script = ExtResource("3_kiam3")
|
||||
|
||||
[node name="HealthBar" parent="." instance=ExtResource("4_gt388")]
|
||||
visible = false
|
||||
offset_left = -46.0
|
||||
offset_top = 41.0
|
||||
offset_right = 50.0
|
||||
offset_bottom = 53.0
|
||||
|
||||
[node name="WallDetection" type="RayCast2D" parent="."]
|
||||
position = Vector2(3, -1)
|
||||
target_position = Vector2(50, 0)
|
||||
collision_mask = 6
|
||||
|
||||
[node name="AttackArea2D" type="Area2D" parent="."]
|
||||
collision_layer = 0
|
||||
collision_mask = 68
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="AttackArea2D"]
|
||||
shape = SubResource("CircleShape2D_c61vr")
|
||||
|
||||
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."]
|
||||
|
||||
[node name="BubbleMarker" type="Marker2D" parent="."]
|
||||
position = Vector2(0, -79)
|
||||
script = ExtResource("5_y2fh5")
|
||||
|
||||
[node name="ScoutArea2D" type="Area2D" parent="."]
|
||||
collision_layer = 0
|
||||
collision_mask = 76
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ScoutArea2D"]
|
||||
shape = SubResource("CircleShape2D_fowd5")
|
||||
|
||||
[node name="TipLabel" type="Label" parent="."]
|
||||
offset_left = -20.0
|
||||
offset_top = 46.0
|
||||
offset_right = 15.0
|
||||
offset_bottom = 71.0
|
||||
horizontal_alignment = 1
|
|
@ -23,19 +23,19 @@ animations = [{
|
|||
}]
|
||||
|
||||
[node name="Player" type="CharacterBody2D"]
|
||||
light_mask = 2
|
||||
collision_layer = 4
|
||||
collision_mask = 34
|
||||
collision_mask = 162
|
||||
platform_floor_layers = 4294967042
|
||||
platform_wall_layers = 128
|
||||
script = ExtResource("1_1dlls")
|
||||
metadata/CampId = "Default"
|
||||
metadata/MaxHp = 32
|
||||
MaxHp = 32
|
||||
CampId = "Default"
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource("CapsuleShape2D_bb8wt")
|
||||
debug_color = Color(0.886275, 0, 0.803922, 0.419608)
|
||||
|
||||
[node name="Camera2D" type="Camera2D" parent="."]
|
||||
position_smoothing_enabled = true
|
||||
|
||||
[node name="Area2DPickingArea" type="Area2D" parent="."]
|
||||
collision_layer = 0
|
||||
collision_mask = 8
|
||||
|
|
|
@ -34,18 +34,17 @@ radius = 172.29
|
|||
collision_layer = 64
|
||||
collision_mask = 38
|
||||
script = ExtResource("1_ubaid")
|
||||
InitWeaponRes = null
|
||||
CharacterName = "character_necromancer"
|
||||
MaxHp = 50
|
||||
CampId = "Mazoku"
|
||||
LootListId = "test"
|
||||
metadata/CampId = "Mazoku"
|
||||
metadata/MaxHp = 50
|
||||
metadata/Name = "死灵法师"
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
position = Vector2(0, 4)
|
||||
shape = SubResource("CapsuleShape2D_bb8wt")
|
||||
|
||||
[node name="Area2DPickingArea" type="Area2D" parent="."]
|
||||
collision_layer = 0
|
||||
collision_layer = 64
|
||||
collision_mask = 8
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2DPickingArea"]
|
||||
|
@ -90,13 +89,20 @@ shape = SubResource("CircleShape2D_c61vr")
|
|||
position = Vector2(0, -79)
|
||||
script = ExtResource("5_y2fh5")
|
||||
|
||||
[node name="VisibleOnScreenEnabler2D" type="VisibleOnScreenEnabler2D" parent="."]
|
||||
position = Vector2(0, 5.5)
|
||||
scale = Vector2(2.04, 3.05)
|
||||
|
||||
[node name="ScoutArea2D" type="Area2D" parent="."]
|
||||
collision_layer = 0
|
||||
collision_mask = 76
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="ScoutArea2D"]
|
||||
shape = SubResource("CircleShape2D_fowd5")
|
||||
|
||||
[node name="TipLabel" type="Label" parent="."]
|
||||
anchors_preset = 5
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
offset_left = -57.0
|
||||
offset_top = 57.0
|
||||
offset_right = 61.0
|
||||
offset_bottom = 82.0
|
||||
grow_horizontal = 2
|
||||
horizontal_alignment = 1
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://cn10fimoem04m"]
|
||||
[gd_scene load_steps=5 format=3 uid="uid://bq5d2w22wnxrf"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/inventory/Packsack.cs" id="1_slakl"]
|
||||
[ext_resource type="Texture2D" uid="uid://dvx10dfjctn7t" path="res://sprites/packsack.png" id="2_40jca"]
|
||||
[ext_resource type="Texture2D" uid="uid://di0sw4rgd26y0" path="res://sprites/PortableBackpacks.png" id="2_l0fbh"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_brthl"]
|
||||
size = Vector2(21, 29)
|
||||
|
@ -14,7 +14,6 @@ collision_layer = 8
|
|||
collision_mask = 38
|
||||
script = ExtResource("1_slakl")
|
||||
NumberSlots = 30
|
||||
Id = "packsack"
|
||||
|
||||
[node name="DamageArea2D" type="Area2D" parent="."]
|
||||
collision_layer = 8
|
||||
|
@ -30,4 +29,9 @@ shape = SubResource("RectangleShape2D_xqyue")
|
|||
|
||||
[node name="Packsack" type="Sprite2D" parent="."]
|
||||
scale = Vector2(0.5, 0.5)
|
||||
texture = ExtResource("2_40jca")
|
||||
texture = ExtResource("2_l0fbh")
|
||||
|
||||
[node name="TipLabel" type="Label" parent="."]
|
||||
offset_top = 31.0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 56.0
|
36
prefab/projectile/Catapult.tscn
Normal file
|
@ -0,0 +1,36 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://bdxgx5vcof8em"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/projectile/Projectile.cs" id="1_ib3qh"]
|
||||
[ext_resource type="Texture2D" uid="uid://b1twcink38sh0" path="res://sprites/Player.png" id="2_dxg46"]
|
||||
|
||||
[sub_resource type="CircleShape2D" id="CircleShape2D_dgro2"]
|
||||
|
||||
[sub_resource type="CircleShape2D" id="CircleShape2D_8117d"]
|
||||
radius = 11.0
|
||||
|
||||
[node name="curseOfTheUndead" type="CharacterBody2D"]
|
||||
collision_layer = 16
|
||||
collision_mask = 102
|
||||
slide_on_ceiling = false
|
||||
platform_floor_layers = 4294967042
|
||||
platform_wall_layers = 32
|
||||
script = ExtResource("1_ib3qh")
|
||||
Speed = 300.0
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource("CircleShape2D_dgro2")
|
||||
|
||||
[node name="CollisionDetectionArea" type="Area2D" parent="."]
|
||||
collision_layer = 16
|
||||
collision_mask = 78
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="CollisionDetectionArea"]
|
||||
shape = SubResource("CircleShape2D_8117d")
|
||||
|
||||
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."]
|
||||
bus = &"SoundEffect"
|
||||
area_mask = 16
|
||||
|
||||
[node name="Player" type="Sprite2D" parent="."]
|
||||
scale = Vector2(0.3, 0.3)
|
||||
texture = ExtResource("2_dxg46")
|
|
@ -1,22 +1,23 @@
|
|||
[gd_scene load_steps=5 format=3 uid="uid://c01av43yk1q71"]
|
||||
[gd_scene load_steps=4 format=3 uid="uid://c01av43yk1q71"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/projectile/Projectile.cs" id="1_ib3qh"]
|
||||
[ext_resource type="Texture2D" uid="uid://bbcjkyrsx88av" path="res://sprites/projectile/curseOfTheUndead.png" id="1_k8el6"]
|
||||
|
||||
[sub_resource type="CircleShape2D" id="CircleShape2D_dgro2"]
|
||||
|
||||
[sub_resource type="CircleShape2D" id="CircleShape2D_8117d"]
|
||||
radius = 11.0
|
||||
|
||||
[node name="curseOfTheUndead" type="CharacterBody2D"]
|
||||
collision_layer = 0
|
||||
collision_mask = 0
|
||||
script = ExtResource("1_ib3qh")
|
||||
metadata/Speed = 500.0
|
||||
metadata/Durability = 1.0
|
||||
metadata/DamageType = 2
|
||||
metadata/Knockback = Vector2(2, -3)
|
||||
metadata/Life = 5000
|
||||
_life = 5000
|
||||
_durability = 1.0
|
||||
_maxDamage = 10
|
||||
_minDamage = 1
|
||||
_damageType = 2
|
||||
Speed = 500.0
|
||||
_enableTracking = true
|
||||
_targetDiesDestroyProjectile = true
|
||||
_repelStrength = 2.5
|
||||
|
||||
[node name="CurseOfTheUndead" type="Sprite2D" parent="."]
|
||||
texture = ExtResource("1_k8el6")
|
||||
|
@ -24,13 +25,6 @@ texture = ExtResource("1_k8el6")
|
|||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource("CircleShape2D_dgro2")
|
||||
|
||||
[node name="CollisionDetectionArea" type="Area2D" parent="."]
|
||||
collision_layer = 16
|
||||
collision_mask = 78
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="CollisionDetectionArea"]
|
||||
shape = SubResource("CircleShape2D_8117d")
|
||||
|
||||
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="."]
|
||||
bus = &"SoundEffect"
|
||||
area_mask = 16
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
[gd_scene load_steps=8 format=4 uid="uid://b0uurp551pku"]
|
||||
[gd_scene load_steps=9 format=4 uid="uid://b0uurp551pku"]
|
||||
|
||||
[ext_resource type="TileSet" uid="uid://c4wpp12rr44hi" path="res://tileSets/dungeon.tres" id="1_a15hy"]
|
||||
[ext_resource type="Script" path="res://scripts/map/AiCharacterSpawn.cs" id="2_wamhd"]
|
||||
[ext_resource type="Texture2D" uid="uid://drw45jlmfo0su" path="res://sprites/light/White_100.png" id="3_0mlbb"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"]
|
||||
size = Vector2(443, 118)
|
||||
size = Vector2(505, 186)
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_o85u0"]
|
||||
size = Vector2(20, 48)
|
||||
|
@ -16,8 +17,8 @@ size = Vector2(20, 46)
|
|||
size = Vector2(53, 24)
|
||||
|
||||
[sub_resource type="NavigationPolygon" id="NavigationPolygon_rh1gx"]
|
||||
vertices = PackedVector2Array(501, 150, 9, 151, 11, 107, 43, 107, 170, 41, 169, 11, 217, 11, 219, 42, 42, 44, 470, 107, 499, 106, 469, 43)
|
||||
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 5, 6, 7), PackedInt32Array(0, 3, 8, 4, 7, 9), PackedInt32Array(9, 10, 0), PackedInt32Array(9, 7, 11)])
|
||||
vertices = PackedVector2Array(499.469, 106.328, 501.523, 150.039, 9.3125, 151.953, 470.156, 107.352, 10.7031, 107, 43.1562, 107, 170.328, 40.6797, 169.344, 11, 216.617, 11, 218.617, 41.9297, 469.148, 43.9219, 42.1484, 44.6875)
|
||||
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(3, 2, 4, 5), PackedInt32Array(6, 7, 8, 9), PackedInt32Array(10, 3, 5, 9), PackedInt32Array(6, 9, 5, 11)])
|
||||
outlines = Array[PackedVector2Array]([PackedVector2Array(479, 34, 228, 32, 226, 1, 159, 1, 160, 31, 32, 35, 33, 97, 1, 97, -1, 162, 512, 160, 509, 96, 480, 97)])
|
||||
source_geometry_group_name = &"navigation_polygon_source_group"
|
||||
|
||||
|
@ -27,7 +28,7 @@ source_geometry_group_name = &"navigation_polygon_source_group"
|
|||
collision_mask = 0
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="RoomArea"]
|
||||
position = Vector2(256.5, 95)
|
||||
position = Vector2(255.5, 97)
|
||||
shape = SubResource("RectangleShape2D_kiih8")
|
||||
|
||||
[node name="RoomSlotList" type="Node2D" parent="."]
|
||||
|
@ -53,7 +54,7 @@ shape = SubResource("RectangleShape2D_7tsse")
|
|||
[node name="Marker2D" type="Marker2D" parent="."]
|
||||
position = Vector2(260, 87)
|
||||
script = ExtResource("2_wamhd")
|
||||
metadata/ResPath = "res://prefab/entitys/DelivererOfDarkMagic.tscn"
|
||||
ResPath = "res://prefab/entitys/DelivererOfDarkMagic.tscn"
|
||||
|
||||
[node name="NavigationRegion2D" type="NavigationRegion2D" parent="."]
|
||||
navigation_polygon = SubResource("NavigationPolygon_rh1gx")
|
||||
|
@ -74,3 +75,9 @@ tile_set = ExtResource("1_a15hy")
|
|||
use_parent_material = true
|
||||
tile_map_data = PackedByteArray("AAAAAAAAAQAAAAMAAAAAAAEAAQACAAEAAAAAAAIAAQACAAEAAAABAAAAAQABAAIAAAACAAAAAQABAAIAAAADAAAAAQABAAIAAAAEAAAAAQABAAIAAAAHAAAAAQABAAIAAAAIAAAAAQABAAIAAAAJAAAAAQABAAIAAAAKAAAAAQABAAIAAAALAAAAAQABAAIAAAAMAAAAAQABAAIAAAANAAAAAQABAAIAAAAOAAAAAQABAAMAAAAPAAAAAQACAAMAAAAPAAEAAQACAAQAAAAPAAIAAQACAAQAAAABAAUAAQABAAAAAAACAAUAAQABAAAAAAADAAUAAQABAAAAAAAEAAUAAQABAAAAAAAFAAUAAQABAAAAAAAGAAUAAQABAAAAAAAHAAUAAQABAAAAAAAIAAUAAQABAAAAAAAJAAUAAQABAAAAAAAKAAUAAQABAAAAAAALAAUAAQABAAAAAAAMAAUAAQABAAAAAAANAAUAAQABAAAAAAAOAAUAAQABAAAAAAAAAAUAAQAAAAUAAAAPAAUAAQACAAUAAAAFAAAAAQAEAAQAAAAGAAAAAQAEAAQAAAA=")
|
||||
tile_set = ExtResource("1_a15hy")
|
||||
|
||||
[node name="PointLight2D" type="PointLight2D" parent="."]
|
||||
visible = false
|
||||
position = Vector2(256, 96.5)
|
||||
scale = Vector2(15.875, 5.96875)
|
||||
texture = ExtResource("3_0mlbb")
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
[gd_scene load_steps=8 format=4 uid="uid://dslr5tdbp4noq"]
|
||||
[gd_scene load_steps=9 format=4 uid="uid://dslr5tdbp4noq"]
|
||||
|
||||
[ext_resource type="TileSet" uid="uid://c4wpp12rr44hi" path="res://tileSets/dungeon.tres" id="1_rn2om"]
|
||||
[ext_resource type="Script" path="res://scripts/map/AiCharacterSpawn.cs" id="2_7q101"]
|
||||
[ext_resource type="Texture2D" uid="uid://drw45jlmfo0su" path="res://sprites/light/White_100.png" id="3_m3aye"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"]
|
||||
size = Vector2(441, 122)
|
||||
size = Vector2(505, 188)
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_o85u0"]
|
||||
size = Vector2(20, 48)
|
||||
|
@ -16,8 +17,8 @@ size = Vector2(46, 20)
|
|||
size = Vector2(20, 54)
|
||||
|
||||
[sub_resource type="NavigationPolygon" id="NavigationPolygon_db40i"]
|
||||
vertices = PackedVector2Array(499, 108, 501, 150, 246, 149, 471, 108, 12, 149, 11, 107, 41, 107, 203, 149, 245, 180, 203, 178, 43, 43, 470, 44)
|
||||
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 5, 6, 7), PackedInt32Array(2, 8, 9, 7), PackedInt32Array(10, 11, 3, 2, 7, 6)])
|
||||
vertices = PackedVector2Array(499.461, 108, 501.5, 150.906, 246.312, 148.922, 471.156, 108, 245.312, 180.508, 203, 178.469, 203, 149, 11.8438, 149, 11.1562, 107.656, 40.5625, 106.688, 43.5391, 43.0391, 470.148, 44.9531)
|
||||
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(2, 4, 5, 6), PackedInt32Array(7, 8, 9, 6), PackedInt32Array(10, 11, 3, 2, 6, 9)])
|
||||
outlines = Array[PackedVector2Array]([PackedVector2Array(34, 33, 31, 97, 1, 98, 2, 159, 193, 159, 193, 188, 255, 191, 256, 159, 512, 161, 509, 98, 481, 98, 480, 35)])
|
||||
source_geometry_group_name = &"navigation_polygon_source_group"
|
||||
|
||||
|
@ -27,7 +28,7 @@ source_geometry_group_name = &"navigation_polygon_source_group"
|
|||
collision_mask = 0
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="RoomArea"]
|
||||
position = Vector2(255.5, 97)
|
||||
position = Vector2(256.5, 95)
|
||||
shape = SubResource("RectangleShape2D_kiih8")
|
||||
|
||||
[node name="RoomSlotList" type="Node2D" parent="."]
|
||||
|
@ -75,3 +76,9 @@ tile_set = ExtResource("1_rn2om")
|
|||
use_parent_material = true
|
||||
tile_map_data = PackedByteArray("AAAAAAAAAQAAAAMAAAAAAAEAAQACAAEAAAAAAAIAAQACAAEAAAABAAAAAQABAAIAAAACAAAAAQABAAIAAAADAAAAAQABAAIAAAAEAAAAAQABAAIAAAAFAAAAAQABAAIAAAAGAAAAAQABAAIAAAAHAAAAAQABAAIAAAAIAAAAAQABAAIAAAAJAAAAAQABAAIAAAAKAAAAAQABAAIAAAALAAAAAQABAAIAAAAMAAAAAQABAAIAAAANAAAAAQABAAIAAAAOAAAAAQABAAMAAAAPAAAAAQACAAMAAAAPAAEAAQACAAQAAAAPAAIAAQACAAQAAAABAAUAAQABAAAAAAACAAUAAQABAAAAAAADAAUAAQABAAAAAAAEAAUAAQABAAAAAAAFAAUAAQABAAAAAAAIAAUAAQABAAAAAAAJAAUAAQABAAAAAAAKAAUAAQABAAAAAAALAAUAAQABAAAAAAAMAAUAAQABAAAAAAANAAUAAQABAAAAAAAOAAUAAQABAAAAAAAAAAUAAQAAAAUAAAAPAAUAAQACAAUAAAAGAAUAAQAEAAQAAAAHAAUAAQAEAAQAAAA=")
|
||||
tile_set = ExtResource("1_rn2om")
|
||||
|
||||
[node name="PointLight2D" type="PointLight2D" parent="."]
|
||||
visible = false
|
||||
position = Vector2(256.5, 94)
|
||||
scale = Vector2(15.9688, 5.9375)
|
||||
texture = ExtResource("3_m3aye")
|
||||
|
|
|
@ -3,18 +3,18 @@
|
|||
[ext_resource type="TileSet" uid="uid://c4wpp12rr44hi" path="res://tileSets/dungeon.tres" id="1_rn2om"]
|
||||
[ext_resource type="Script" path="res://scripts/map/PlayerSpawn.cs" id="2_6p8mv"]
|
||||
[ext_resource type="Script" path="res://scripts/map/ItemSpawn.cs" id="3_v1tlc"]
|
||||
[ext_resource type="Texture2D" uid="uid://b2blj0yf4ohx3" path="res://icon.svg" id="4_psvpu"]
|
||||
[ext_resource type="Texture2D" uid="uid://drw45jlmfo0su" path="res://sprites/light/White_100.png" id="5_4pssd"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"]
|
||||
size = Vector2(450, 191)
|
||||
size = Vector2(507, 251)
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_jxmys"]
|
||||
size = Vector2(18, 57.75)
|
||||
|
||||
[sub_resource type="NavigationPolygon" id="NavigationPolygon_064c7"]
|
||||
vertices = PackedVector2Array(468, 174, 500, 174, 499, 214, 41, 214, 45, 45, 468, 45)
|
||||
vertices = PackedVector2Array(467.773, 174, 499.828, 174, 499.156, 214.969, 40.9375, 214.023, 40.0469, 41.0625, 470.758, 43.9297)
|
||||
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(0, 3, 4, 5)])
|
||||
outlines = Array[PackedVector2Array]([PackedVector2Array(35, 35, 31, 224, 509, 225, 510, 164, 478, 164, 478, 35)])
|
||||
outlines = Array[PackedVector2Array]([PackedVector2Array(30, 31, 31, 224, 509, 225, 510, 164, 478, 164, 481, 34)])
|
||||
source_geometry_group_name = &"navigation_polygon_source_group"
|
||||
|
||||
[node name="InitialRoom" type="Node2D"]
|
||||
|
@ -22,7 +22,7 @@ source_geometry_group_name = &"navigation_polygon_source_group"
|
|||
[node name="RoomArea" type="Area2D" parent="."]
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="RoomArea"]
|
||||
position = Vector2(253, 130.5)
|
||||
position = Vector2(254.5, 129.5)
|
||||
shape = SubResource("RectangleShape2D_kiih8")
|
||||
|
||||
[node name="RoomSlotList" type="Node2D" parent="."]
|
||||
|
@ -42,11 +42,7 @@ script = ExtResource("2_6p8mv")
|
|||
[node name="ItemMarker2D" type="Marker2D" parent="."]
|
||||
position = Vector2(142, 84)
|
||||
script = ExtResource("3_v1tlc")
|
||||
ItemId = "staff_of_the_undead"
|
||||
|
||||
[node name="Icon" type="Sprite2D" parent="ItemMarker2D"]
|
||||
scale = Vector2(0.3, 0.3)
|
||||
texture = ExtResource("4_psvpu")
|
||||
ItemId = "staff_necromancy"
|
||||
|
||||
[node name="NavigationRegion2D" type="NavigationRegion2D" parent="."]
|
||||
navigation_polygon = SubResource("NavigationPolygon_064c7")
|
||||
|
@ -68,3 +64,9 @@ tile_set = ExtResource("1_rn2om")
|
|||
use_parent_material = true
|
||||
tile_map_data = PackedByteArray("AAAAAAAAAQAAAAMAAAAAAAEAAQACAAEAAAAAAAIAAQACAAEAAAAAAAMAAQACAAEAAAAAAAQAAQACAAEAAAAAAAcAAQAAAAUAAAAPAAAAAQACAAMAAAAPAAEAAQACAAQAAAAPAAIAAQACAAQAAAAPAAMAAQACAAQAAAAPAAQAAQACAAQAAAAPAAcAAQACAAUAAAAAAAUAAQACAAEAAAAAAAYAAQACAAEAAAABAAAAAQABAAIAAAACAAAAAQABAAIAAAADAAAAAQABAAIAAAAEAAAAAQABAAIAAAAFAAAAAQABAAIAAAAGAAAAAQABAAIAAAAHAAAAAQABAAIAAAAIAAAAAQABAAIAAAAJAAAAAQABAAIAAAAKAAAAAQABAAIAAAALAAAAAQABAAIAAAAMAAAAAQABAAIAAAANAAAAAQABAAIAAAAOAAAAAQABAAMAAAABAAcAAQABAAAAAAACAAcAAQABAAAAAAADAAcAAQABAAAAAAAEAAcAAQABAAAAAAAFAAcAAQABAAAAAAAGAAcAAQABAAAAAAAHAAcAAQABAAAAAAAIAAcAAQABAAAAAAAJAAcAAQABAAAAAAAKAAcAAQABAAAAAAALAAcAAQABAAAAAAAMAAcAAQABAAAAAAANAAcAAQABAAAAAAAOAAcAAQABAAAAAAA=")
|
||||
tile_set = ExtResource("1_rn2om")
|
||||
|
||||
[node name="PointLight2D" type="PointLight2D" parent="."]
|
||||
visible = false
|
||||
position = Vector2(259.5, 128.5)
|
||||
scale = Vector2(16.0312, 7.96875)
|
||||
texture = ExtResource("5_4pssd")
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
[gd_scene load_steps=7 format=4 uid="uid://c57cc1tyreybb"]
|
||||
[gd_scene load_steps=8 format=4 uid="uid://c57cc1tyreybb"]
|
||||
|
||||
[ext_resource type="TileSet" uid="uid://c4wpp12rr44hi" path="res://tileSets/dungeon.tres" id="1_rn2om"]
|
||||
[ext_resource type="Texture2D" uid="uid://drw45jlmfo0su" path="res://sprites/light/White_100.png" id="2_1ctsj"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kiih8"]
|
||||
size = Vector2(669, 442)
|
||||
size = Vector2(728, 509)
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_o85u0"]
|
||||
size = Vector2(20, 48)
|
||||
|
@ -15,7 +16,7 @@ size = Vector2(22, 46)
|
|||
size = Vector2(11.875, 51)
|
||||
|
||||
[sub_resource type="NavigationPolygon" id="NavigationPolygon_1qloc"]
|
||||
vertices = PackedVector2Array(696, 425, 720, 425, 722, 467, 12, 470, 10, 426, 46, 424, 691, 84, 45, 48, 721, 44, 723, 80)
|
||||
vertices = PackedVector2Array(696.148, 425, 719.617, 425, 722.328, 467.047, 12.5391, 470.938, 10.4531, 426.43, 46.0312, 424.461, 690.859, 84.1562, 45.0234, 48.9219, 721.523, 44.0625, 723.5, 80.1875)
|
||||
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3, 4), PackedInt32Array(0, 4, 5), PackedInt32Array(6, 0, 5, 7), PackedInt32Array(6, 7, 8, 9)])
|
||||
outlines = Array[PackedVector2Array]([PackedVector2Array(35, 39, 36, 415, 0, 417, 3, 481, 733, 477, 729, 415, 706, 415, 701, 93, 734, 89, 731, 34)])
|
||||
source_geometry_group_name = &"navigation_polygon_source_group"
|
||||
|
@ -26,7 +27,7 @@ source_geometry_group_name = &"navigation_polygon_source_group"
|
|||
collision_mask = 0
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="RoomArea"]
|
||||
position = Vector2(367.5, 256)
|
||||
position = Vector2(365, 255.5)
|
||||
shape = SubResource("RectangleShape2D_kiih8")
|
||||
|
||||
[node name="RoomSlotList" type="Node2D" parent="."]
|
||||
|
@ -70,3 +71,9 @@ tile_set = ExtResource("1_rn2om")
|
|||
use_parent_material = true
|
||||
tile_map_data = PackedByteArray("AAAAAAAAAQAAAAMAAAAAAAEAAQACAAEAAAAAAAIAAQACAAEAAAAAAAMAAQACAAEAAAAAAAQAAQACAAEAAAAAAAUAAQACAAEAAAAAAAYAAQACAAEAAAABAAAAAQABAAIAAAACAAAAAQABAAIAAAADAAAAAQABAAIAAAAEAAAAAQABAAIAAAAFAAAAAQABAAIAAAAGAAAAAQABAAIAAAAHAAAAAQABAAIAAAAIAAAAAQABAAIAAAAJAAAAAQABAAIAAAAKAAAAAQABAAIAAAALAAAAAQABAAIAAAAMAAAAAQABAAIAAAANAAAAAQABAAIAAAAOAAAAAQABAAIAAAAPAAAAAQABAAIAAAAQAAAAAQABAAIAAAARAAAAAQABAAIAAAASAAAAAQABAAIAAAATAAAAAQABAAIAAAAUAAAAAQABAAIAAAAVAAAAAQABAAIAAAAWAAAAAQACAAMAAAAWAAMAAQAAAAEAAAAWAAQAAQAAAAEAAAAWAAUAAQAAAAEAAAAWAAYAAQAAAAEAAAAWAAcAAQAAAAEAAAAWAAgAAQAAAAEAAAAWAAkAAQAAAAEAAAAWAAoAAQAAAAEAAAAWAAsAAQAAAAEAAAAWAAwAAQAAAAEAAAAAAAcAAQACAAEAAAAAAAgAAQACAAEAAAAAAAkAAQACAAEAAAAAAAoAAQACAAEAAAAAAAsAAQACAAEAAAAAAAwAAQACAAEAAAAVAA8AAQABAAAAAAAUAA8AAQABAAAAAAATAA8AAQABAAAAAAASAA8AAQABAAAAAAARAA8AAQABAAAAAAAQAA8AAQABAAAAAAAPAA8AAQABAAAAAAAOAA8AAQABAAAAAAANAA8AAQABAAAAAAAMAA8AAQABAAAAAAALAA8AAQABAAAAAAAKAA8AAQABAAAAAAAJAA8AAQABAAAAAAAIAA8AAQABAAAAAAAHAA8AAQABAAAAAAAGAA8AAQABAAAAAAAFAA8AAQABAAAAAAAEAA8AAQABAAAAAAADAA8AAQABAAAAAAACAA8AAQABAAAAAAABAA8AAQABAAAAAAAAAA8AAQAAAAUAAAAWAA8AAQACAAUAAAASAAsAAQAEAAQAAAATAAsAAQAEAAQAAAAUAAsAAQAEAAQAAAAVAAsAAQAFAAQAAAABAAsAAQADAAQAAAACAAsAAQAEAAQAAAADAAsAAQAEAAQAAAAEAAsAAQAEAAQAAAAFAAsAAQAEAAQAAAAGAAsAAQAEAAQAAAAHAAsAAQAEAAQAAAAIAAsAAQAEAAQAAAAOAAsAAQAEAAQAAAAPAAsAAQAEAAQAAAAQAAsAAQAEAAQAAAARAAsAAQAEAAQAAAABAAoAAQAEAAMAAAAVAAoAAQAEAAMAAAACAAoAAQAEAAIAAAAUAAoAAQAEAAIAAAANAAQAAQAEAAQAAAANAAwAAQAEAAQAAAAMAA0AAQAEAAQAAAALAA4AAQAEAAQAAAAJAAsAAQAEAAQAAAAGAAoAAQAEAAQAAAAHAAkAAQAEAAQAAAAIAAgAAQAEAAQAAAAJAAcAAQAEAAQAAAAKAAcAAQAEAAQAAAALAAcAAQAEAAQAAAAMAAcAAQAEAAQAAAANAAcAAQAEAAQAAAAOAAcAAQAEAAQAAAAPAAcAAQAEAAQAAAAQAAcAAQAEAAQAAAARAAcAAQAEAAQAAAASAAcAAQAEAAQAAAATAAcAAQAEAAQAAAAUAAcAAQAEAAQAAAAVAAcAAQAEAAQAAAABAAcAAQAEAAQAAAACAAcAAQAEAAQAAAADAAcAAQAEAAQAAAAEAAcAAQAEAAQAAAAFAAcAAQAEAAQAAAALAAYAAQAEAAQAAAAMAAUAAQAEAAQAAAAPAAMAAQAEAAQAAAAQAAMAAQAEAAQAAAARAAMAAQAEAAQAAAASAAMAAQAEAAQAAAATAAMAAQAEAAQAAAAUAAMAAQAEAAQAAAAVAAMAAQAEAAQAAAABAAMAAQAEAAQAAAACAAMAAQAEAAQAAAADAAMAAQAEAAQAAAAEAAMAAQAEAAQAAAAFAAMAAQAEAAQAAAAGAAMAAQAEAAQAAAAHAAMAAQAEAAQAAAAIAAMAAQAEAAQAAAAJAAMAAQAEAAQAAAA=")
|
||||
tile_set = ExtResource("1_rn2om")
|
||||
|
||||
[node name="PointLight2D" type="PointLight2D" parent="."]
|
||||
visible = false
|
||||
position = Vector2(367.5, 258)
|
||||
scale = Vector2(23.0938, 16.25)
|
||||
texture = ExtResource("2_1ctsj")
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
[gd_scene format=3 uid="uid://b7se73tsnlpd6"]
|
||||
|
||||
[node name="FloatLabel" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 23.0
|
|
@ -12,8 +12,3 @@ texture_under = ExtResource("1_sc0v3")
|
|||
texture_over = ExtResource("2_ay5vh")
|
||||
texture_progress = ExtResource("2_s0gle")
|
||||
script = ExtResource("4_84gre")
|
||||
|
||||
[node name="Label" type="Label" parent="."]
|
||||
layout_mode = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 23.0
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://dnnn2xyayiehk"]
|
||||
[gd_scene load_steps=8 format=3 uid="uid://dnnn2xyayiehk"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://e6670ykyq145" path="res://sprites/weapon/staffOfTheUndead.png" id="1_ms3us"]
|
||||
[ext_resource type="Texture2D" uid="uid://wt50kx6bup51" path="res://sprites/weapon/StaffNecromancy.png" id="1_ms3us"]
|
||||
[ext_resource type="Script" path="res://scripts/weapon/ProjectileWeapon.cs" id="1_w8hhv"]
|
||||
[ext_resource type="PackedScene" uid="uid://c01av43yk1q71" path="res://prefab/projectile/curseOfTheUndead.tscn" id="2_34250"]
|
||||
[ext_resource type="PackedScene" uid="uid://c01av43yk1q71" path="res://prefab/projectile/curseOfTheUndead.tscn" id="2_mwli5"]
|
||||
[ext_resource type="Texture2D" uid="uid://dg5vwprt66w4j" path="res://sprites/weapon/StaffNecromancy_Icon.png" id="3_31iau"]
|
||||
[ext_resource type="AudioStream" uid="uid://cak6chjjsu7wo" path="res://sounds/fire.wav" id="4_ffr2k"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_obcq2"]
|
||||
|
@ -14,14 +15,18 @@ size = Vector2(49, 5.25)
|
|||
[node name="StaffOfTheUndead" type="RigidBody2D"]
|
||||
collision_layer = 8
|
||||
collision_mask = 34
|
||||
angular_damp = -1.0
|
||||
script = ExtResource("1_w8hhv")
|
||||
ProjectileScenes = [ExtResource("2_34250")]
|
||||
OffsetAngle = 0.087
|
||||
ProjectileScenes = [ExtResource("2_mwli5")]
|
||||
Sequentially = true
|
||||
FiringIntervalAsMillisecond = 300
|
||||
Id = "staff_of_the_undead"
|
||||
_recoilStrength = 5
|
||||
UniqueIcon = ExtResource("3_31iau")
|
||||
|
||||
[node name="DamageArea2D" type="Area2D" parent="."]
|
||||
collision_layer = 8
|
||||
collision_mask = 70
|
||||
collision_mask = 102
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="DamageArea2D"]
|
||||
position = Vector2(25.5, 0.5)
|
||||
|
@ -41,3 +46,9 @@ position = Vector2(65, 0)
|
|||
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Marker2D"]
|
||||
stream = ExtResource("4_ffr2k")
|
||||
bus = &"SoundEffect"
|
||||
|
||||
[node name="TipLabel" type="Label" parent="."]
|
||||
offset_left = 6.0
|
||||
offset_top = 48.0
|
||||
offset_right = 46.0
|
||||
offset_bottom = 73.0
|
|
@ -76,6 +76,7 @@ ui_down={
|
|||
pick_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":69,"physical_keycode":0,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":9,"pressure":0.0,"pressed":true,"script":null)
|
||||
]
|
||||
}
|
||||
throw={
|
||||
|
@ -85,7 +86,8 @@ throw={
|
|||
}
|
||||
use_item={
|
||||
"deadzone": 0.5,
|
||||
"events": [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(211, 16),"global_position":Vector2(215, 57),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
|
||||
"events": [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(206, 22),"global_position":Vector2(215, 68),"factor":1.0,"button_index":1,"canceled":false,"pressed":true,"double_click":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":10,"pressure":0.0,"pressed":true,"script":null)
|
||||
]
|
||||
}
|
||||
hotbar_1={
|
||||
|
@ -135,28 +137,31 @@ hotbar_9={
|
|||
}
|
||||
hotbar_next={
|
||||
"deadzone": 0.5,
|
||||
"events": [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":16,"position":Vector2(241, 17),"global_position":Vector2(245, 58),"factor":1.0,"button_index":5,"canceled":false,"pressed":true,"double_click":false,"script":null)
|
||||
"events": [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":16,"position":Vector2(257, 22),"global_position":Vector2(266, 68),"factor":1.0,"button_index":5,"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)
|
||||
]
|
||||
}
|
||||
hotbar_previous={
|
||||
"deadzone": 0.5,
|
||||
"events": [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":8,"position":Vector2(169, 17),"global_position":Vector2(173, 58),"factor":1.0,"button_index":4,"canceled":false,"pressed":true,"double_click":false,"script":null)
|
||||
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":4,"axis_value":1.0,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[internationalization]
|
||||
|
||||
locale/translations=PackedStringArray("res://locals/DeathInfo.en.translation", "res://locals/DeathInfo.ja.translation", "res://locals/DeathInfo.zh.translation", "res://locals/InputMapping.en.translation", "res://locals/InputMapping.ja.translation", "res://locals/InputMapping.zh.translation", "res://locals/Log.en.translation", "res://locals/Log.ja.translation", "res://locals/Log.zh.translation", "res://locals/Slogan.en.translation", "res://locals/Slogan.ja.translation", "res://locals/Slogan.zh.translation", "res://locals/UI.en.translation", "res://locals/UI.ja.translation", "res://locals/UI.zh.translation", "res://locals/Item.en.translation", "res://locals/Item.ja.translation", "res://locals/Item.zh.translation", "res://locals/Action.en.translation", "res://locals/Action.ja.translation", "res://locals/Action.zh.translation", "res://locals/Misc.en.translation", "res://locals/Misc.ja.translation", "res://locals/Misc.zh.translation")
|
||||
locale/translations=PackedStringArray("res://locals/DeathInfo.en.translation", "res://locals/DeathInfo.ja.translation", "res://locals/DeathInfo.zh.translation", "res://locals/InputMapping.en.translation", "res://locals/InputMapping.ja.translation", "res://locals/InputMapping.zh.translation", "res://locals/Log.en.translation", "res://locals/Log.ja.translation", "res://locals/Log.zh.translation", "res://locals/Slogan.en.translation", "res://locals/Slogan.ja.translation", "res://locals/Slogan.zh.translation", "res://locals/UI.en.translation", "res://locals/UI.ja.translation", "res://locals/UI.zh.translation", "res://locals/Item.en.translation", "res://locals/Item.ja.translation", "res://locals/Item.zh.translation", "res://locals/Action.en.translation", "res://locals/Action.ja.translation", "res://locals/Action.zh.translation", "res://locals/Misc.en.translation", "res://locals/Misc.ja.translation", "res://locals/Misc.zh.translation", "res://locals/Character.en.translation", "res://locals/Character.ja.translation", "res://locals/Character.zh.translation")
|
||||
|
||||
[layer_names]
|
||||
|
||||
2d_physics/layer_1="RoomArea"
|
||||
2d_physics/layer_2="Ground"
|
||||
2d_physics/layer_2="Floor"
|
||||
2d_physics/layer_3="Player"
|
||||
2d_physics/layer_4="PickAbleItem"
|
||||
2d_physics/layer_5="Projectile"
|
||||
2d_physics/layer_6="Platform"
|
||||
2d_physics/layer_7="Mob"
|
||||
2d_physics/layer_8="Wall"
|
||||
|
||||
[physics]
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
[gd_scene load_steps=7 format=3 uid="uid://bnftvkj2cido7"]
|
||||
[gd_scene load_steps=8 format=3 uid="uid://bnftvkj2cido7"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/loader/sceneLoader/GameSceneLoader.cs" id="1_mqdgt"]
|
||||
[ext_resource type="Texture2D" uid="uid://cs6e0af876ss5" path="res://sprites/ui/HeartEmpty.png" id="2_n1yht"]
|
||||
[ext_resource type="Script" path="res://scripts/inventory/HotBar.cs" id="2_owrhq"]
|
||||
[ext_resource type="Script" path="res://scripts/HealthBarUi.cs" id="2_xrm3v"]
|
||||
[ext_resource type="Script" path="res://scripts/FpsLabel.cs" id="5_dis4v"]
|
||||
[ext_resource type="PackedScene" uid="uid://c74180dtf7j7a" path="res://scenes/mapContainer.tscn" id="6_ljdj4"]
|
||||
[ext_resource type="PackedScene" uid="uid://bb188382q7btp" path="res://scenes/gameOverMenu.tscn" id="6_yjmrv"]
|
||||
|
||||
[node name="Game" type="Node2D"]
|
||||
|
@ -21,6 +22,7 @@ anchor_right = 1.0
|
|||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/Control"]
|
||||
layout_mode = 1
|
||||
|
@ -34,25 +36,31 @@ offset_right = -20.0
|
|||
offset_bottom = -20.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="HealthBarUi" type="HBoxContainer" parent="CanvasLayer/Control/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
script = ExtResource("2_xrm3v")
|
||||
|
||||
[node name="TextureRect3" type="TextureRect" parent="CanvasLayer/Control/VBoxContainer/HealthBarUi"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
texture = ExtResource("2_n1yht")
|
||||
|
||||
[node name="HotBar" type="HBoxContainer" parent="CanvasLayer/Control/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
script = ExtResource("2_owrhq")
|
||||
|
||||
[node name="TextureRect3" type="TextureRect" parent="CanvasLayer/Control/VBoxContainer/HotBar"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
texture = ExtResource("2_n1yht")
|
||||
|
||||
[node name="OperationTip" type="RichTextLabel" parent="CanvasLayer/Control/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
bbcode_enabled = true
|
||||
fit_content = true
|
||||
|
||||
|
@ -83,6 +91,9 @@ offset_right = 20.0
|
|||
offset_bottom = 25.0
|
||||
grow_horizontal = 2
|
||||
|
||||
[node name="MapContainer" parent="CanvasLayer/Control" instance=ExtResource("6_ljdj4")]
|
||||
layout_mode = 1
|
||||
|
||||
[node name="BackpackUIContainer" type="Control" parent="CanvasLayer"]
|
||||
visible = false
|
||||
layout_mode = 3
|
||||
|
@ -107,3 +118,6 @@ visible = false
|
|||
[node name="AICharacterContainer" type="Node2D" parent="."]
|
||||
|
||||
[node name="PacksackContainer" type="Node2D" parent="."]
|
||||
|
||||
[node name="CanvasModulate" type="CanvasModulate" parent="."]
|
||||
color = Color(0, 0, 0, 1)
|
||||
|
|
|
@ -9,6 +9,7 @@ anchor_right = 1.0
|
|||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
script = ExtResource("1_vj6du")
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="."]
|
||||
|
@ -18,6 +19,7 @@ anchor_right = 1.0
|
|||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
color = Color(0.941176, 0.243137, 0.243137, 0.258824)
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="."]
|
||||
|
@ -27,12 +29,15 @@ anchor_right = 1.0
|
|||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="GameOverLabel" type="Label" parent="CenterContainer/VBoxContainer/CenterContainer"]
|
||||
layout_mode = 2
|
||||
|
@ -41,10 +46,12 @@ text = "ui_game_over_title"
|
|||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_top = 18
|
||||
|
||||
[node name="CenterContainer2" type="CenterContainer" parent="CenterContainer/VBoxContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="DeathInfoLabel" type="Label" parent="CenterContainer/VBoxContainer/MarginContainer/CenterContainer2"]
|
||||
layout_mode = 2
|
||||
|
@ -52,6 +59,7 @@ text = "ui_death_info_describe"
|
|||
|
||||
[node name="MarginContainer2" type="MarginContainer" parent="CenterContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_top = 10
|
||||
theme_override_constants/margin_bottom = 150
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://ckl23uwxrjat4"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/loader/uiLoader/LevelGraphEditorLoader.cs" id="1_g1axq"]
|
||||
[ext_resource type="Script" path="res://scripts/loader/uiLoader/LevelGraphEditorLoader.cs" id="1_qgo7w"]
|
||||
|
||||
[node name="LevelGraphEditor" type="Control"]
|
||||
layout_mode = 3
|
||||
|
@ -9,7 +9,7 @@ anchor_right = 1.0
|
|||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_g1axq")
|
||||
script = ExtResource("1_qgo7w")
|
||||
|
||||
[node name="GraphEdit" type="GraphEdit" parent="."]
|
||||
layout_mode = 1
|
43
scenes/mapContainer.tscn
Normal file
|
@ -0,0 +1,43 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://c74180dtf7j7a"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://c35bsle7thcnh" path="res://sprites/ui/MiniMapBg.png" id="1_h88bi"]
|
||||
|
||||
[node name="MapContainer" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="."]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="MiniMap" type="NinePatchRect" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -174.0
|
||||
offset_top = -177.0
|
||||
offset_right = -24.0
|
||||
offset_bottom = -27.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
texture = ExtResource("1_h88bi")
|
||||
patch_margin_left = 7
|
||||
patch_margin_top = 7
|
||||
patch_margin_right = 7
|
||||
patch_margin_bottom = 7
|
||||
|
||||
[node name="RoomPreviewContainer" type="Node2D" parent="MiniMap"]
|
23
scripts/AssetHolder.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts;
|
||||
|
||||
/// <summary>
|
||||
/// <para>AssetHolder</para>
|
||||
/// <para>资产持有者</para>
|
||||
/// </summary>
|
||||
public static class AssetHolder
|
||||
{
|
||||
public static Texture2D? White25;
|
||||
public static Texture2D? White100;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Loading the game's static assets</para>
|
||||
/// <para>加载游戏的静态资产</para>
|
||||
/// </summary>
|
||||
public static void LoadStaticAsset()
|
||||
{
|
||||
White25 = ResourceLoader.Load<Texture2D>("res://sprites/light/White_25.png");
|
||||
White100 = ResourceLoader.Load<Texture2D>("res://sprites/light/White_100.png");
|
||||
}
|
||||
}
|
|
@ -21,6 +21,29 @@ public static class Config
|
|||
public const string Test = "test";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Difficulty</para>
|
||||
/// <para>游戏难度</para>
|
||||
/// </summary>
|
||||
public static class Difficulty
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Simple mode</para>
|
||||
/// <para>简单模式</para>
|
||||
/// </summary>
|
||||
public const int Easy = 0;
|
||||
/// <summary>
|
||||
/// <para>Normal mode</para>
|
||||
/// <para>正常模式</para>
|
||||
/// </summary>
|
||||
public const int Normal = 1;
|
||||
/// <summary>
|
||||
/// <para>Hard mode</para>
|
||||
/// <para>困难模式</para>
|
||||
/// </summary>
|
||||
public const int Hard = 2;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Camp ID</para>
|
||||
|
@ -115,12 +138,6 @@ public static class Config
|
|||
public const int HotBarSize = 9;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>UserID</para>
|
||||
/// <para>用户ID</para>
|
||||
/// </summary>
|
||||
public const string UserId = "DefaultUser";
|
||||
|
||||
/// <summary>
|
||||
/// <para>Whether version isolation is enabled</para>
|
||||
/// <para>是否启用版本隔离</para>
|
||||
|
@ -299,17 +316,27 @@ public static class Config
|
|||
if (EnableVersionIsolation())
|
||||
{
|
||||
return Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName,
|
||||
ProjectSettings.GetSetting("application/config/name").AsString(), UserId,
|
||||
ProjectSettings.GetSetting("application/config/name").AsString(),
|
||||
ProjectSettings.GetSetting("application/config/version").AsString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName,
|
||||
ProjectSettings.GetSetting("application/config/name").AsString(), UserId,
|
||||
ProjectSettings.GetSetting("application/config/name").AsString(),
|
||||
DefaultVersionName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>GetDataBaseDirectory</para>
|
||||
/// <para>获取数据库文件夹</para>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetDataBaseDirectory()
|
||||
{
|
||||
return Path.Join(GetGameDataDirectory(), "Databases");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>GetModDataDirectory</para>
|
||||
/// <para>获取模组文件夹</para>
|
||||
|
@ -391,6 +418,30 @@ public static class Config
|
|||
public const int VerticalVelocityOfDamageNumbers = 5;
|
||||
|
||||
|
||||
public static class OffsetAngleMode
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// <para>Random(Default)</para>
|
||||
/// <para>随机的(默认)</para>
|
||||
/// </summary>
|
||||
public const int Random = 0;
|
||||
|
||||
/// <summary>
|
||||
/// <para>AlwaysSame</para>
|
||||
/// <para>永远不变的偏移角度</para>
|
||||
/// </summary>
|
||||
public const int AlwaysSame = 1;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Cross</para>
|
||||
/// <para>交叉变换</para>
|
||||
/// </summary>
|
||||
public const int Cross = 2;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Physical collision layer number</para>
|
||||
/// <para>物理碰撞层 序号</para>
|
||||
|
@ -398,12 +449,13 @@ public static class Config
|
|||
public static class LayerNumber
|
||||
{
|
||||
public const int RoomArea = 1;
|
||||
public const int Ground = 2;
|
||||
public const int Floor = 2;
|
||||
public const int Player = 3;
|
||||
public const int PickAbleItem = 4;
|
||||
public const int Projectile = 5;
|
||||
public const int Platform = 6;
|
||||
public const int Mob = 7;
|
||||
public const int Wall = 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -8,11 +8,17 @@ using Godot;
|
|||
namespace ColdMint.scripts;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The node holder within the game scene</para>
|
||||
/// <para>游戏场景内的节点持有者</para>
|
||||
/// <para>Dependencies on the runtime of the game scene</para>
|
||||
/// <para>游戏场景运行时的依赖</para>
|
||||
/// </summary>
|
||||
public static class GameSceneNodeHolder
|
||||
public static class GameSceneDepend
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>The midpoint of the minimap</para>
|
||||
/// <para>迷你地图的中点</para>
|
||||
/// </summary>
|
||||
public static Vector2 MiniMapMidpointCoordinate;
|
||||
|
||||
private static Player? _player;
|
||||
|
||||
/// <summary>
|
||||
|
@ -35,6 +41,18 @@ public static class GameSceneNodeHolder
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>When the mouse enters the scope of a character, it is considered a target</para>
|
||||
/// <para>鼠标进入到某个角色的范围内时,会将其视作目标</para>
|
||||
/// </summary>
|
||||
public static Node2D? TemporaryTargetNode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>MiniMapContainerNode</para>
|
||||
/// <para>迷你地图容器节点</para>
|
||||
/// </summary>
|
||||
public static Node2D? MiniMapContainerNode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>ProjectileContainer</para>
|
||||
/// <para>抛射体容器</para>
|
||||
|
@ -113,7 +131,6 @@ public static class GameSceneNodeHolder
|
|||
|
||||
NodeUtils.ForEachNode<PacksackUi>(BackpackUiContainer, node =>
|
||||
{
|
||||
|
||||
//If the child node is not visible, the traversal continues.
|
||||
//如果子节点不可见,则继续遍历。
|
||||
if (!node.Visible)
|
||||
|
@ -122,7 +139,6 @@ public static class GameSceneNodeHolder
|
|||
//直到找到可见的节点,隐藏该节点,然后返回true,结束遍历。
|
||||
node.Hide();
|
||||
return true;
|
||||
|
||||
});
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ namespace ColdMint.scripts.camp;
|
|||
/// </summary>
|
||||
public static class CampManager
|
||||
{
|
||||
private static readonly Dictionary<string, Camp?> Camps = new Dictionary<string, Camp?>();
|
||||
private static readonly Dictionary<string, Camp?> Camps = new();
|
||||
|
||||
/// <summary>
|
||||
/// <para>The default camp is returned if no corresponding camp is obtained</para>
|
||||
|
|
|
@ -2,7 +2,6 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.bubble;
|
||||
using ColdMint.scripts.camp;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.inventory;
|
||||
using ColdMint.scripts.stateMachine;
|
||||
using ColdMint.scripts.utils;
|
||||
|
@ -63,9 +62,6 @@ public sealed partial class AiCharacter : CharacterTemplate
|
|||
/// </remarks>
|
||||
private RayCast2D? _attackObstacleDetection;
|
||||
|
||||
|
||||
private VisibleOnScreenEnabler2D? _screenEnabler2D;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Navigation agent</para>
|
||||
/// <para>导航代理</para>
|
||||
|
@ -119,22 +115,9 @@ public sealed partial class AiCharacter : CharacterTemplate
|
|||
{
|
||||
base._Ready();
|
||||
|
||||
_enemyInTheAttackRange = new List<CharacterTemplate>();
|
||||
_enemyInTheScoutRange = new List<CharacterTemplate>();
|
||||
_weaponInTheScoutRange = new List<WeaponTemplate>();
|
||||
_screenEnabler2D = GetNode<VisibleOnScreenEnabler2D>("VisibleOnScreenEnabler2D");
|
||||
_screenEnabler2D.ScreenEntered += () =>
|
||||
{
|
||||
//When the character enters the screen.
|
||||
//当角色进入屏幕。
|
||||
ProcessMode = ProcessModeEnum.Disabled;
|
||||
};
|
||||
_screenEnabler2D.ScreenExited += () =>
|
||||
{
|
||||
//When the character leaves the screen.
|
||||
//当角色离开屏幕。
|
||||
ProcessMode = ProcessModeEnum.Inherit;
|
||||
};
|
||||
_enemyInTheAttackRange = [];
|
||||
_enemyInTheScoutRange = [];
|
||||
_weaponInTheScoutRange = [];
|
||||
_bubbleMarker = GetNode<BubbleMarker>("BubbleMarker");
|
||||
if (_bubbleMarker != null)
|
||||
{
|
||||
|
@ -342,7 +325,7 @@ public sealed partial class AiCharacter : CharacterTemplate
|
|||
if (NavigationAgent2D != null && IsOnFloor())
|
||||
{
|
||||
var nextPathPosition = NavigationAgent2D.GetNextPathPosition();
|
||||
var direction = (nextPathPosition - GlobalPosition).Normalized();
|
||||
var direction = GlobalPosition.DirectionTo(nextPathPosition);
|
||||
velocity = direction * Config.CellSize * Speed * ProtectedSpeedScale;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,9 +73,16 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
//物品被扔出后多长时间恢复与地面和平台的碰撞(单位:秒)
|
||||
private readonly double _itemCollisionRecoveryTime = 0.045f;
|
||||
|
||||
public string? ReadOnlyCharacterName => CharacterName;
|
||||
public string? ReadOnlyCharacterName => TranslationServerUtils.Translate(CharacterName);
|
||||
|
||||
protected string? CharacterName;
|
||||
[Export] public string? CharacterName;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Can mutate after death</para>
|
||||
/// <para>是否允许死后变异</para>
|
||||
/// </summary>
|
||||
[Export]
|
||||
public bool CanMutateAfterDeath { get; set; } = true;
|
||||
|
||||
protected IItemContainer? ProtectedItemContainer;
|
||||
|
||||
|
@ -156,21 +163,22 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
|
||||
//The initial health of the character after creation
|
||||
//角色创建后的初始血量
|
||||
private int _initialHp;
|
||||
[Export] public int InitialHp;
|
||||
|
||||
public int MaxHp;
|
||||
[Export] public int MaxHp;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The camp ID of the role</para>
|
||||
/// <para>角色的阵营ID</para>
|
||||
/// </summary>
|
||||
public string CampId = null!;
|
||||
[Export] public string? CampId;
|
||||
|
||||
private DamageNumberNodeSpawn? _damageNumber;
|
||||
|
||||
[Export] public string LootListId { get; private set; } = "";
|
||||
|
||||
private HealthBar? _healthBar;
|
||||
private Label? _tipLabel;
|
||||
private DateTime _lastDamageTime;
|
||||
|
||||
/// <summary>
|
||||
|
@ -244,8 +252,10 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
weaponTemplates.Add(weaponTemplate);
|
||||
}
|
||||
|
||||
return weaponTemplates.ToArray();
|
||||
}
|
||||
|
||||
|
@ -253,9 +263,6 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
{
|
||||
base._Ready();
|
||||
PickingRangeBodiesList = new List<Node>();
|
||||
CharacterName = GetMeta("Name", Name).AsString();
|
||||
CampId = GetMeta("CampId", Config.CampId.Default).AsString();
|
||||
MaxHp = GetMeta("MaxHp", Config.DefaultMaxHp).AsInt32();
|
||||
|
||||
if (MaxHp <= 0)
|
||||
{
|
||||
|
@ -264,15 +271,14 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
MaxHp = Config.DefaultMaxHp;
|
||||
}
|
||||
|
||||
_initialHp = GetMeta("InitialHp", "0").AsInt32();
|
||||
if (_initialHp <= 0 || _initialHp > MaxHp)
|
||||
if (InitialHp <= 0 || InitialHp > MaxHp)
|
||||
{
|
||||
//If the initial health is less than or equal to 0 or greater than the maximum health, then set it to the maximum health
|
||||
//如果初始血量小于等于0或者大于最大血量,那么将其设置为最大血量
|
||||
_initialHp = MaxHp;
|
||||
InitialHp = MaxHp;
|
||||
}
|
||||
|
||||
CurrentHp = _initialHp;
|
||||
CurrentHp = InitialHp;
|
||||
//The health bar of a creature may be null.
|
||||
//生物的健康条,可能为null。
|
||||
_healthBar = GetNodeOrNull<HealthBar>("HealthBar");
|
||||
|
@ -281,12 +287,12 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
_healthBar.MaxValue = MaxHp;
|
||||
}
|
||||
|
||||
|
||||
ItemMarker2D = GetNode<Marker2D>("ItemMarker2D");
|
||||
_itemMarkerOriginalX = ItemMarker2D.Position.X;
|
||||
_animatedSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
|
||||
_pickingArea = GetNode<Area2D>("Area2DPickingArea");
|
||||
_damageNumber = GetNode<Marker2D>("DamageNumber") as DamageNumberNodeSpawn;
|
||||
_tipLabel = GetNodeOrNull<Label>("TipLabel");
|
||||
if (_pickingArea != null)
|
||||
{
|
||||
//If true, the zone will detect objects or areas entering and leaving the zone.
|
||||
|
@ -298,6 +304,56 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
_pickingArea.BodyEntered += EnterThePickingRangeBody;
|
||||
_pickingArea.BodyExited += ExitThePickingRangeBody;
|
||||
}
|
||||
|
||||
//The character cannot pass through the wall and floor
|
||||
//角色不能穿过墙壁和地板
|
||||
SetCollisionMaskValue(Config.LayerNumber.Wall, true);
|
||||
SetCollisionMaskValue(Config.LayerNumber.Floor, true);
|
||||
InputPickable = true;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
base._ExitTree();
|
||||
if (GameSceneDepend.TemporaryTargetNode == this)
|
||||
{
|
||||
GameSceneDepend.TemporaryTargetNode = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public override void _MouseEnter()
|
||||
{
|
||||
if (GameSceneDepend.Player != null)
|
||||
{
|
||||
var targetCamp = CampManager.GetCamp(CampId);
|
||||
var playerCamp = CampManager.GetCamp(GameSceneDepend.Player.CampId);
|
||||
if (CampManager.CanCauseHarm(targetCamp, playerCamp))
|
||||
{
|
||||
GameSceneDepend.TemporaryTargetNode = this;
|
||||
}
|
||||
}
|
||||
|
||||
if (_tipLabel != null)
|
||||
{
|
||||
_tipLabel.Visible = true;
|
||||
_tipLabel.Text = CharacterName;
|
||||
//Vertical Centering Tip
|
||||
//垂直居中提示
|
||||
var oldPosition = _tipLabel.Position;
|
||||
oldPosition.X = -_tipLabel.Size.X / 2;
|
||||
_tipLabel.Position = oldPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public override void _MouseExit()
|
||||
{
|
||||
if (_tipLabel != null)
|
||||
{
|
||||
_tipLabel.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -384,7 +440,9 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
pickAbleTemplate.Owner = this;
|
||||
pickAbleTemplate.Picked = true;
|
||||
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, false);
|
||||
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, false);
|
||||
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Floor, false);
|
||||
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Wall, false);
|
||||
LogCat.Log("item_pickup_disables_collision_damage", LogCat.LogLabel.ContactInjury);
|
||||
pickAbleTemplate.EnableContactInjury = false;
|
||||
pickAbleTemplate.Sleeping = true;
|
||||
}
|
||||
|
@ -453,7 +511,7 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
return;
|
||||
}
|
||||
|
||||
if (GameSceneNodeHolder.Player == null)
|
||||
if (GameSceneDepend.Player == null)
|
||||
{
|
||||
//We didn't know who the player was, so we showed it as a hostile color
|
||||
//我们不知道玩家是谁,所以我们将其显示为敌对颜色
|
||||
|
@ -464,7 +522,7 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
//If we set up a player node, then compare the injured party ID to the player's party ID
|
||||
//如果我们设置了玩家节点,那么将受伤者的阵营ID与玩家的阵营ID进行比较
|
||||
var targetCamp = CampManager.GetCamp(CampId);
|
||||
var playerCamp = CampManager.GetCamp(GameSceneNodeHolder.Player.CampId);
|
||||
var playerCamp = CampManager.GetCamp(GameSceneDepend.Player.CampId);
|
||||
if (CampManager.CanCauseHarm(targetCamp, playerCamp))
|
||||
{
|
||||
if (targetCamp != null && playerCamp != null)
|
||||
|
@ -502,8 +560,8 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
///<para>伤害模板</para>
|
||||
/// </param>
|
||||
/// <returns>
|
||||
///<para>Return whether the damage was done successfully</para>
|
||||
///<para>返回是否成功造成了伤害</para>
|
||||
///<para>Return whether the character is dead</para>
|
||||
///<para>返回本次伤害是否导致角色死亡。</para>
|
||||
/// </returns>
|
||||
public bool Damage(DamageTemplate damageTemplate)
|
||||
{
|
||||
|
@ -517,12 +575,11 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
//角色死亡
|
||||
OnDie(damageTemplate);
|
||||
ThrowAllItemOnDie();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UpDataHealthBar();
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -561,6 +618,10 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
/// <param name="force"></param>
|
||||
public void AddForce(Vector2 force)
|
||||
{
|
||||
if (_additionalForce != Vector2.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("AddForce called more than once");
|
||||
}
|
||||
_additionalForce = force;
|
||||
}
|
||||
|
||||
|
@ -735,7 +796,7 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
switch (item)
|
||||
{
|
||||
case PickAbleTemplate pickAbleTemplate:
|
||||
if (GameSceneNodeHolder.WeaponContainer == null)
|
||||
if (GameSceneDepend.WeaponContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -753,7 +814,9 @@ public partial class CharacterTemplate : CharacterBody2D
|
|||
//We cannot immediately resume the physical collision when the weapon is discharged, which will cause the weapon to collide with the ground and platform earlier, preventing the weapon from flying.
|
||||
//仍出武器时,我们不能立即恢复物理碰撞,立即恢复会导致武器更早的与地面和平台碰撞,阻止武器的飞行。
|
||||
pickAbleTemplate.EnableContactInjury = true;
|
||||
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Ground, true);
|
||||
LogCat.Log("item_thrown_restore_collision_damage", LogCat.LogLabel.ContactInjury);
|
||||
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Floor, true);
|
||||
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Wall, true);
|
||||
pickAbleTemplate.SetCollisionMaskValue(Config.LayerNumber.Platform, true);
|
||||
timer.QueueFree();
|
||||
};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ColdMint.scripts.damage;
|
||||
|
@ -17,8 +16,6 @@ namespace ColdMint.scripts.character;
|
|||
/// </summary>
|
||||
public partial class Player : CharacterTemplate
|
||||
{
|
||||
private Control? _floatLabel;
|
||||
|
||||
//Empty object projectile
|
||||
//空的物品抛射线
|
||||
private readonly Vector2[] _emptyVector2Array = [Vector2.Zero];
|
||||
|
@ -29,10 +26,6 @@ public partial class Player : CharacterTemplate
|
|||
//用于检测玩家是否站在平台上的射线
|
||||
private RayCast2D? _platformDetectionRayCast2D;
|
||||
|
||||
|
||||
private const float PromptTextDistance = 50;
|
||||
|
||||
|
||||
//抛出物品的飞行速度
|
||||
private float _throwingVelocity = Config.CellSize * 13;
|
||||
|
||||
|
@ -50,26 +43,24 @@ public partial class Player : CharacterTemplate
|
|||
CharacterName = TranslationServerUtils.Translate("default_player_name");
|
||||
LogCat.LogWithFormat("player_spawn_debug", LogCat.LogLabel.Default, LogCat.UploadFormat, ReadOnlyCharacterName,
|
||||
GlobalPosition);
|
||||
var floatLabelPackedScene = GD.Load<PackedScene>("res://prefab/ui/FloatLabel.tscn");
|
||||
//Initializes the float label.
|
||||
//初始化悬浮标签。
|
||||
_floatLabel = NodeUtils.InstantiatePackedScene<Control>(floatLabelPackedScene);
|
||||
if (_floatLabel == null)
|
||||
{
|
||||
throw new NullReferenceException(TranslationServer.Translate("float_label_instantiate_failed"));
|
||||
}
|
||||
|
||||
_floatLabel.Hide();
|
||||
NodeUtils.CallDeferredAddChild(this, _floatLabel);
|
||||
_parabola = GetNode<Line2D>("Parabola");
|
||||
_platformDetectionRayCast2D = GetNode<RayCast2D>("PlatformDetectionRayCast");
|
||||
UpdateOperationTip();
|
||||
var healthBarUi = GameSceneNodeHolder.HealthBarUi;
|
||||
var healthBarUi = GameSceneDepend.HealthBarUi;
|
||||
if (healthBarUi != null)
|
||||
{
|
||||
healthBarUi.MaxHp = MaxHp;
|
||||
healthBarUi.CurrentHp = CurrentHp;
|
||||
}
|
||||
|
||||
//Mount the camera.
|
||||
//挂载相机。
|
||||
var mainCameraPackedScene = GD.Load<PackedScene>("res://prefab/MainCamera.tscn");
|
||||
var camera2D = NodeUtils.InstantiatePackedScene<Camera2D>(mainCameraPackedScene);
|
||||
if (camera2D != null)
|
||||
{
|
||||
NodeUtils.CallDeferredAddChild(this, camera2D);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WhenBindItemContainer(IItemContainer? itemContainer)
|
||||
|
@ -98,7 +89,7 @@ public partial class Player : CharacterTemplate
|
|||
private void SelectedItemSlotChangeEvent(SelectedItemSlotChangeEvent selectedItemSlotChangeEvent)
|
||||
{
|
||||
var item = selectedItemSlotChangeEvent.NewItemSlotNode?.GetItem();
|
||||
GameSceneNodeHolder.HideBackpackUiContainerIfVisible();
|
||||
GameSceneDepend.HideBackpackUiContainerIfVisible();
|
||||
if (item is Node2D node2D)
|
||||
{
|
||||
CurrentItem = node2D;
|
||||
|
@ -109,13 +100,21 @@ public partial class Player : CharacterTemplate
|
|||
}
|
||||
}
|
||||
|
||||
public override void _MouseEnter()
|
||||
{
|
||||
}
|
||||
|
||||
public override void _MouseExit()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Update operation prompt</para>
|
||||
/// <para>更新操作提示</para>
|
||||
/// </summary>
|
||||
private void UpdateOperationTip()
|
||||
{
|
||||
var operationTipLabel = GameSceneNodeHolder.OperationTipLabel;
|
||||
var operationTipLabel = GameSceneDepend.OperationTipLabel;
|
||||
if (operationTipLabel == null)
|
||||
{
|
||||
return;
|
||||
|
@ -243,8 +242,6 @@ public partial class Player : CharacterTemplate
|
|||
{
|
||||
PickingRangeBodiesList?.Remove(pickAbleItem);
|
||||
}
|
||||
|
||||
RecycleFloatLabel();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,7 +303,7 @@ public partial class Player : CharacterTemplate
|
|||
}
|
||||
|
||||
ThrowItem(ItemContainer.GetSelectIndex(), 1, GetThrowVelocity());
|
||||
GameSceneNodeHolder.HideBackpackUiContainerIfVisible();
|
||||
GameSceneDepend.HideBackpackUiContainerIfVisible();
|
||||
CurrentItem = null;
|
||||
}
|
||||
}
|
||||
|
@ -388,13 +385,15 @@ public partial class Player : CharacterTemplate
|
|||
public override void Revive(int newHp)
|
||||
{
|
||||
base.Revive(newHp);
|
||||
var healthBarUi = GameSceneNodeHolder.HealthBarUi;
|
||||
var healthBarUi = GameSceneDepend.HealthBarUi;
|
||||
if (healthBarUi != null)
|
||||
{
|
||||
//The purpose of setting Hp to the current Hp is to cause the life bar to refresh.
|
||||
//将Hp设置为当前Hp的目的是,使生命条刷新。
|
||||
healthBarUi.CurrentHp = CurrentHp;
|
||||
}
|
||||
ProcessMode = ProcessModeEnum.Inherit;
|
||||
Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -430,48 +429,11 @@ public partial class Player : CharacterTemplate
|
|||
return;
|
||||
}
|
||||
|
||||
if (node is not Node2D node2D)
|
||||
if (node is not Node2D)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_floatLabel != null)
|
||||
{
|
||||
if (node is not PickAbleTemplate pickAbleTemplate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (pickAbleTemplate.Picked)
|
||||
{
|
||||
//If the pickables are picked up, the label is not displayed.
|
||||
//如果可拾捡物被捡起了,那么不显示标签。
|
||||
LogCat.LogWarning("pickable_picked_up");
|
||||
return;
|
||||
}
|
||||
|
||||
NodeUtils.CallDeferredReparent(node, _floatLabel);
|
||||
var rotationDegreesNode2D = node2D.RotationDegrees;
|
||||
var rotationDegreesNode2DAbs = Math.Abs(rotationDegreesNode2D);
|
||||
_floatLabel.GlobalPosition = node2D.GlobalPosition;
|
||||
_floatLabel.Position = rotationDegreesNode2DAbs > 90
|
||||
? new Vector2(0, PromptTextDistance)
|
||||
: new Vector2(0, -PromptTextDistance);
|
||||
_floatLabel.RotationDegrees = 0 - rotationDegreesNode2D;
|
||||
var label = _floatLabel.GetNode<Label>("Label");
|
||||
|
||||
var stringBuilder = new StringBuilder();
|
||||
if (pickAbleTemplate.Owner is CharacterTemplate characterTemplate)
|
||||
{
|
||||
stringBuilder.Append(characterTemplate.ReadOnlyCharacterName);
|
||||
stringBuilder.Append(TranslationServerUtils.Translate("de"));
|
||||
}
|
||||
|
||||
stringBuilder.Append(TranslationServerUtils.Translate(pickAbleTemplate.Name));
|
||||
label.Text = stringBuilder.ToString();
|
||||
_floatLabel.Show();
|
||||
}
|
||||
|
||||
UpdateOperationTip();
|
||||
}
|
||||
|
||||
|
@ -483,29 +445,13 @@ public partial class Player : CharacterTemplate
|
|||
return;
|
||||
}
|
||||
|
||||
RecycleFloatLabel();
|
||||
UpdateOperationTip();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Recycle Float Label</para>
|
||||
/// <para>回收悬浮标签</para>
|
||||
/// </summary>
|
||||
private void RecycleFloatLabel()
|
||||
{
|
||||
if (_floatLabel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_floatLabel.Hide();
|
||||
NodeUtils.CallDeferredReparent(this, _floatLabel);
|
||||
}
|
||||
|
||||
protected override void OnHit(DamageTemplate damageTemplate)
|
||||
{
|
||||
base.OnHit(damageTemplate);
|
||||
var healthBarUi = GameSceneNodeHolder.HealthBarUi;
|
||||
var healthBarUi = GameSceneDepend.HealthBarUi;
|
||||
if (healthBarUi != null)
|
||||
{
|
||||
healthBarUi.CurrentHp = CurrentHp;
|
||||
|
|
|
@ -27,7 +27,7 @@ public partial class DamageNumber : CharacterBody2D
|
|||
|
||||
private bool _enableGravity;
|
||||
|
||||
public void SetVelocity(Vector2 velocity)
|
||||
public new void SetVelocity(Vector2 velocity)
|
||||
{
|
||||
Velocity = velocity;
|
||||
_enableGravity = true;
|
||||
|
|
48
scripts/database/DataBaseManager.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace ColdMint.scripts.database;
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Game database manager</para>
|
||||
/// <para>游戏数据库管理器</para>
|
||||
/// </summary>
|
||||
public class DataBaseManager
|
||||
{
|
||||
private static ServiceProvider? _serviceProvider;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Initialize database</para>
|
||||
/// <para>初始化数据库</para>
|
||||
/// </summary>
|
||||
public static void InitDataBases(string databasePath)
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddDbContext<GameDbContext>(options =>
|
||||
options.UseSqlite($"Data Source={Path.Join(databasePath, Config.SolutionName + ".db")}"));
|
||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
var dataPackDbContext = GetRequiredService<GameDbContext>();
|
||||
dataPackDbContext.Database.EnsureCreated();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Get database service</para>
|
||||
/// <para>获取数据库服务</para>
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static T GetRequiredService<T>() where T : notnull
|
||||
{
|
||||
if (_serviceProvider == null)
|
||||
{
|
||||
throw new NullReferenceException("service provider is null");
|
||||
}
|
||||
|
||||
var scope = _serviceProvider.CreateScope();
|
||||
return scope.ServiceProvider.GetRequiredService<T>();
|
||||
}
|
||||
}
|
42
scripts/database/GameDbContext.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using ColdMint.scripts.database.gameDbTables;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ColdMint.scripts.database;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Game database</para>
|
||||
/// <para>游戏数据库</para>
|
||||
/// </summary>
|
||||
public class GameDbContext(DbContextOptions<GameDbContext> options) : DbContext(options)
|
||||
{
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
public DbSet<ErrorRecord> ErrorRecords { get; set; }
|
||||
// ReSharper restore UnusedAutoPropertyAccessor.Global
|
||||
|
||||
/// <summary>
|
||||
/// <para>Async add error record</para>
|
||||
/// <para>异步添加错误信息</para>
|
||||
/// </summary>
|
||||
/// <param name="errorRecord"></param>
|
||||
public async void AddOrUpdateErrorRecordAsync(ErrorRecord errorRecord)
|
||||
{
|
||||
if (errorRecord.Message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldErrorRecord = await ErrorRecords.FindAsync(errorRecord.Message);
|
||||
if (oldErrorRecord == null)
|
||||
{
|
||||
ErrorRecords.Add(errorRecord);
|
||||
}
|
||||
else
|
||||
{
|
||||
oldErrorRecord.Count++;
|
||||
oldErrorRecord.LastDateTime = errorRecord.LastDateTime;
|
||||
ErrorRecords.Update(oldErrorRecord);
|
||||
}
|
||||
|
||||
await SaveChangesAsync();
|
||||
}
|
||||
}
|
33
scripts/database/gameDbTables/ErrorRecord.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ColdMint.scripts.database.gameDbTables;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Error record</para>
|
||||
/// <para>错误记录</para>
|
||||
/// </summary>
|
||||
public class ErrorRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Message</para>
|
||||
/// <para>错误消息</para>
|
||||
/// </summary>
|
||||
[Key]
|
||||
[MaxLength(255)]
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Global
|
||||
public string? Message { get; set; }
|
||||
// ReSharper restore UnusedAutoPropertyAccessor.Global
|
||||
|
||||
/// <summary>
|
||||
/// <para>Count</para>
|
||||
/// <para>出现次数</para>
|
||||
/// </summary>
|
||||
public int Count { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// <para>DateTime</para>
|
||||
/// <para>时间</para>
|
||||
/// </summary>
|
||||
public DateTime LastDateTime { get; set; } = DateTime.Now;
|
||||
}
|
|
@ -64,11 +64,30 @@ public static class LogCat
|
|||
/// <para>日志收集器</para>
|
||||
/// </summary>
|
||||
public const string LogCollector = "LogCollector";
|
||||
|
||||
/// <summary>
|
||||
/// <para>Mod Loader</para>
|
||||
/// <para>模组加载器</para>
|
||||
/// </summary>
|
||||
public const string ModLoader = "ModLoader";
|
||||
|
||||
/// <summary>
|
||||
/// <para>PickAble Template</para>
|
||||
/// <para>可拾取物模板</para>
|
||||
/// </summary>
|
||||
public const string PickAbleTemplate = "PickAbleTemplate";
|
||||
|
||||
/// <summary>
|
||||
/// <para>ContactInjury</para>
|
||||
/// <para>物品碰撞伤害</para>
|
||||
/// </summary>
|
||||
public const string ContactInjury = "ContactInjury";
|
||||
|
||||
/// <summary>
|
||||
/// <para>Room</para>
|
||||
/// <para>房间</para>
|
||||
/// </summary>
|
||||
public const string Room = "Room";
|
||||
}
|
||||
|
||||
|
||||
|
@ -237,6 +256,8 @@ public static class LogCat
|
|||
return;
|
||||
}
|
||||
|
||||
//If you need to upload logs, you can upload logs.
|
||||
//如果需要上传日志,并且能够上传日志。
|
||||
if (LogCollector.CanUploadLog && upload)
|
||||
{
|
||||
var logData = new LogData
|
||||
|
|
|
@ -6,9 +6,9 @@ public interface IItem
|
|||
{
|
||||
/// <summary>
|
||||
/// <para>ID of current item</para>
|
||||
/// <para>当前项目的ID</para>
|
||||
/// <para>当前物品的ID</para>
|
||||
/// </summary>
|
||||
string Id { get; }
|
||||
string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Icon of current item</para>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.pickable;
|
||||
using ColdMint.scripts.utils;
|
||||
using Godot;
|
||||
|
||||
|
@ -165,6 +166,11 @@ public partial class ItemSlotNode : MarginContainer
|
|||
}
|
||||
|
||||
var duplicate = node2D.Duplicate();
|
||||
if (node2D is PickAbleTemplate pickAbleTemplate)
|
||||
{
|
||||
pickAbleTemplate.CopyAttributes(duplicate);
|
||||
}
|
||||
|
||||
if (duplicate is not Node2D newNode2D)
|
||||
{
|
||||
return null;
|
||||
|
|
|
@ -5,9 +5,10 @@ namespace ColdMint.scripts.inventory;
|
|||
|
||||
public readonly struct ItemType(
|
||||
string id,
|
||||
Func<Node?,IItem?> createItemFunc,
|
||||
Texture2D? icon,
|
||||
int maxStackQuantity)
|
||||
int maxStackQuantity,
|
||||
Func<Node?, IItem?> createItemFunc
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Item id of this type</para>
|
||||
|
@ -19,7 +20,7 @@ public readonly struct ItemType(
|
|||
/// <para>A function returns a new item instance of this type</para>
|
||||
/// <para>用于创建该类型的物品实例的函数</para>
|
||||
/// </summary>
|
||||
public Func<Node?,IItem?> CreateItemFunc { get; init; } = createItemFunc;
|
||||
public Func<Node?, IItem?> CreateItemFunc { get; init; } = createItemFunc;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Default icon of items of this type</para>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.utils;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.inventory;
|
||||
|
@ -49,7 +48,7 @@ public static class ItemTypeManager
|
|||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Create multiple new item instances for the given item Id</para>
|
||||
/// <para>Create multiple new item instances for the given item ID</para>
|
||||
/// <para>创建多个给定物品Id的新物品实例</para>
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
|
@ -85,23 +84,6 @@ public static class ItemTypeManager
|
|||
return items.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Get the translated default name of the item type for the given id</para>
|
||||
/// <para>获取指定物品id翻译后的物品名</para>
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Translated default name of the item id if it exists. Else, return the id itself
|
||||
/// </returns>
|
||||
public static string DefaultNameOf(string id) => TranslationServerUtils.Translate($"item_{id}") ?? id;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Get the translated default description of the item type for the given id</para>
|
||||
/// <para>获取指定物品id翻译后的描述</para>
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Translated default description of the item id if it exists. Else, return null
|
||||
/// </returns>
|
||||
public static string? DefaultDescriptionOf(string id) => TranslationServerUtils.Translate($"item_{id}_desc");
|
||||
|
||||
/// <summary>
|
||||
/// <para>Get the default icon of the item type for the given id</para>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.serialization;
|
||||
using ColdMint.scripts.utils;
|
||||
|
@ -30,7 +29,7 @@ public static class ItemTypeRegister
|
|||
LogCat.Log("start_item_register_from_file");
|
||||
//初始化文件目录
|
||||
//initialize file dir
|
||||
var itemRegsDirPath = "res://data/itemRegs";
|
||||
const string itemRegsDirPath = "res://data/itemRegs";
|
||||
var itemRegsDir = DirAccess.Open(itemRegsDirPath);
|
||||
var error = DirAccess.GetOpenError();
|
||||
if (error is not Error.Ok)
|
||||
|
@ -103,24 +102,10 @@ public static class ItemTypeRegister
|
|||
//加载场景和图标
|
||||
var scene = ResourceLoader.Load<PackedScene>(typeInfo.ScenePath);
|
||||
var icon = ResourceLoader.Load<Texture2D>(typeInfo.IconPath);
|
||||
|
||||
|
||||
//Create init delegate
|
||||
//创建初始化委托
|
||||
Action<Node?>? setArgs = null;
|
||||
if (typeInfo.CustomArgs != null && typeInfo.CustomArgs.Count > 0)
|
||||
{
|
||||
foreach (var arg in typeInfo.CustomArgs)
|
||||
{
|
||||
setArgs +=
|
||||
node => node?.SetDeferred(arg.Name, arg.ParseValue());
|
||||
}
|
||||
}
|
||||
|
||||
//构造项目类型,寄存器
|
||||
//construct item type, register
|
||||
var itemType = new ItemType(typeInfo.Id,
|
||||
defaultParentNode =>
|
||||
icon, typeInfo.MaxStackValue, defaultParentNode =>
|
||||
{
|
||||
var newItem = NodeUtils.InstantiatePackedScene<IItem>(scene);
|
||||
if (newItem is not Node node) return newItem;
|
||||
|
@ -130,11 +115,10 @@ public static class ItemTypeRegister
|
|||
return null;
|
||||
}
|
||||
|
||||
setArgs?.Invoke(node);
|
||||
newItem.Id = typeInfo.Id;
|
||||
NodeUtils.CallDeferredAddChild(NodeUtils.FindContainerNode(node, defaultParentNode), node);
|
||||
return newItem;
|
||||
},
|
||||
icon, typeInfo.MaxStackValue);
|
||||
});
|
||||
var succeed = ItemTypeManager.Register(itemType);
|
||||
LogCat.LogWithFormat("register_item", label: LogCat.LogLabel.Default, LogCat.UploadFormat, itemType.Id,
|
||||
succeed);
|
||||
|
@ -146,44 +130,5 @@ public static class ItemTypeRegister
|
|||
string Id,
|
||||
string ScenePath,
|
||||
string IconPath,
|
||||
int MaxStackValue,
|
||||
IList<CustomArg>? CustomArgs);
|
||||
|
||||
private readonly record struct CustomArg(string Name, CustomArgType Type, string Value)
|
||||
{
|
||||
public Variant ParseValue() =>
|
||||
Type switch
|
||||
{
|
||||
CustomArgType.String => Value,
|
||||
CustomArgType.Int => int.Parse(Value),
|
||||
CustomArgType.Float => double.Parse(Value),
|
||||
CustomArgType.Vector2 => ParseVector2FromString(Value),
|
||||
CustomArgType.Bool => bool.Parse(Value),
|
||||
CustomArgType.Texture => ResourceLoader.Load<Texture2D>("res://sprites/" + Value),
|
||||
_ => throw new ArgumentOutOfRangeException($"Unknown Arg Type {Type}")
|
||||
};
|
||||
|
||||
private Vector2 ParseVector2FromString(string s)
|
||||
{
|
||||
var ss = s.Split(',');
|
||||
if (ss.Length != 2)
|
||||
{
|
||||
LogCat.LogErrorWithFormat("wrong_custom_arg", LogCat.LogLabel.Default, LogCat.UploadFormat, "Vector2",
|
||||
s);
|
||||
return Vector2.Zero;
|
||||
}
|
||||
|
||||
return new Vector2(float.Parse(ss[0]), float.Parse(ss[1]));
|
||||
}
|
||||
}
|
||||
|
||||
private enum CustomArgType
|
||||
{
|
||||
String,
|
||||
Int,
|
||||
Float,
|
||||
Vector2,
|
||||
Texture,
|
||||
Bool,
|
||||
}
|
||||
int MaxStackValue);
|
||||
}
|
|
@ -46,7 +46,7 @@ public partial class Packsack : PickAbleTemplate
|
|||
}
|
||||
}
|
||||
|
||||
GameSceneNodeHolder.BackpackUiContainer?.Show();
|
||||
GameSceneDepend.BackpackUiContainer?.Show();
|
||||
_packsackUi?.Show();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,42 +19,50 @@ public partial class GameSceneLoader : SceneLoaderTemplate
|
|||
|
||||
public override Task InitializeData()
|
||||
{
|
||||
RenderingServer.SetDefaultClearColor(Color.FromHsv(0, 0, 0));
|
||||
//Loading the blood bar scene
|
||||
//加载血条场景
|
||||
var healthBarUi = GetNode<HealthBarUi>("CanvasLayer/Control/VBoxContainer/HealthBarUi");
|
||||
GameSceneNodeHolder.HealthBarUi = healthBarUi;
|
||||
GameSceneDepend.HealthBarUi = healthBarUi;
|
||||
//Load HotBar
|
||||
//加载HotBar
|
||||
var hotBar = GetNode<HotBar>("CanvasLayer/Control/VBoxContainer/HotBar");
|
||||
GameSceneNodeHolder.HotBar = hotBar;
|
||||
GameSceneDepend.HotBar = hotBar;
|
||||
//Backpack Ui container
|
||||
//背包Ui容器
|
||||
var backpackUiContainer = GetNode<Control>("CanvasLayer/BackpackUIContainer");
|
||||
GameSceneNodeHolder.BackpackUiContainer = backpackUiContainer;
|
||||
GameSceneDepend.BackpackUiContainer = backpackUiContainer;
|
||||
//Load operation prompt
|
||||
//加载操作提示
|
||||
var operationTip = GetNode<RichTextLabel>("CanvasLayer/Control/VBoxContainer/OperationTip");
|
||||
GameSceneNodeHolder.OperationTipLabel = operationTip;
|
||||
GameSceneDepend.OperationTipLabel = operationTip;
|
||||
//Loaded weapon container
|
||||
//加载武器容器
|
||||
var weaponContainer = GetNode<Node2D>("WeaponContainer");
|
||||
GameSceneNodeHolder.WeaponContainer = weaponContainer;
|
||||
GameSceneDepend.WeaponContainer = weaponContainer;
|
||||
//Load projectile container
|
||||
//加载抛射体容器
|
||||
var projectileContainer = GetNode<Node2D>("ProjectileContainer");
|
||||
GameSceneNodeHolder.ProjectileContainer = projectileContainer;
|
||||
GameSceneDepend.ProjectileContainer = projectileContainer;
|
||||
//Load Packsack container
|
||||
//加载背包容器
|
||||
var packsackContainer = GetNode<Node2D>("PacksackContainer");
|
||||
GameSceneNodeHolder.PacksackContainer = packsackContainer;
|
||||
GameSceneDepend.PacksackContainer = packsackContainer;
|
||||
//Load AICharacter container
|
||||
//加载AICharacter容器
|
||||
var aiCharacterContainer = GetNode<Node2D>("AICharacterContainer");
|
||||
GameSceneNodeHolder.AiCharacterContainer = aiCharacterContainer;
|
||||
GameSceneDepend.AiCharacterContainer = aiCharacterContainer;
|
||||
//Load player container
|
||||
//加载玩家容器
|
||||
var playerContainer = GetNode<Node2D>("PlayerContainer");
|
||||
GameSceneNodeHolder.PlayerContainer = playerContainer;
|
||||
GameSceneDepend.PlayerContainer = playerContainer;
|
||||
//Load the room container node
|
||||
//加载房间容器节点
|
||||
var miniMapContainerNode = GetNode<Node2D>("CanvasLayer/Control/MapContainer/MiniMap/RoomPreviewContainer");
|
||||
GameSceneDepend.MiniMapContainerNode = miniMapContainerNode;
|
||||
//计算迷你地图的中点
|
||||
var mapContainer = GetNode<NinePatchRect>("CanvasLayer/Control/MapContainer/MiniMap");
|
||||
GameSceneDepend.MiniMapMidpointCoordinate = mapContainer.Size / 2;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
@ -95,6 +103,7 @@ public partial class GameSceneLoader : SceneLoaderTemplate
|
|||
var seedInfo = TranslationServerUtils.TranslateWithFormat("ui_seed_info", MapGenerator.Seed);
|
||||
_seedLabel.Text = seedInfo ?? $"Seed: {MapGenerator.Seed}";
|
||||
}
|
||||
|
||||
await MapGenerator.GenerateMap();
|
||||
}
|
||||
}
|
|
@ -291,14 +291,7 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
|
|||
|
||||
if (_nodeBinding.TagLineEdit != null)
|
||||
{
|
||||
if (_hasStartNode)
|
||||
{
|
||||
_nodeBinding.TagLineEdit.Text = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
_nodeBinding.TagLineEdit.Text = Config.RoomDataTag.StartingRoom;
|
||||
}
|
||||
_nodeBinding.TagLineEdit.Text = _hasStartNode ? string.Empty : Config.RoomDataTag.StartingRoom;
|
||||
}
|
||||
|
||||
if (_nodeBinding.HBoxContainer != null)
|
||||
|
@ -316,6 +309,7 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GetTree().ChangeSceneToPacked(_mainMenu);
|
||||
};
|
||||
}
|
||||
|
@ -534,11 +528,6 @@ public partial class LevelGraphEditorLoader : UiLoaderTemplate
|
|||
{
|
||||
foreach (var dictionary in connectionList)
|
||||
{
|
||||
if (dictionary == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var keys = dictionary.Keys;
|
||||
if (keys.Count == 0)
|
||||
{
|
||||
|
|
|
@ -107,7 +107,7 @@ public partial class PacksackUi : UiLoaderTemplate
|
|||
{
|
||||
_exitButton.Pressed += () =>
|
||||
{
|
||||
GameSceneNodeHolder.BackpackUiContainer?.Hide();
|
||||
GameSceneDepend.BackpackUiContainer?.Hide();
|
||||
Hide();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ColdMint.scripts.camp;
|
||||
using ColdMint.scripts.contribute;
|
||||
using ColdMint.scripts.database;
|
||||
using ColdMint.scripts.deathInfo;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.inventory;
|
||||
|
@ -82,6 +82,7 @@ public partial class SplashScreenLoader : UiLoaderTemplate
|
|||
//Disable all logs in the release version.
|
||||
//在发行版禁用所有日志。
|
||||
LogCat.MinLogLevel = Config.IsDebug() ? LogCat.InfoLogLevel : LogCat.DisableAllLogLevel;
|
||||
AssetHolder.LoadStaticAsset();
|
||||
ContributorDataManager.RegisterAllContributorData();
|
||||
DeathInfoGenerator.RegisterDeathInfoHandler(new SelfDeathInfoHandler());
|
||||
MapGenerator.RegisterRoomInjectionProcessor(new ChanceRoomInjectionProcessor());
|
||||
|
@ -97,6 +98,13 @@ public partial class SplashScreenLoader : UiLoaderTemplate
|
|||
Directory.CreateDirectory(dataPath);
|
||||
}
|
||||
|
||||
var databasePath = Config.GetDataBaseDirectory();
|
||||
if (!Directory.Exists(databasePath))
|
||||
{
|
||||
Directory.CreateDirectory(databasePath);
|
||||
}
|
||||
|
||||
DataBaseManager.InitDataBases(databasePath);
|
||||
//Registered camp
|
||||
//注册阵营
|
||||
var defaultCamp = new Camp(Config.CampId.Default)
|
||||
|
@ -104,7 +112,10 @@ public partial class SplashScreenLoader : UiLoaderTemplate
|
|||
FriendInjury = true
|
||||
};
|
||||
CampManager.SetDefaultCamp(defaultCamp);
|
||||
var mazoku = new Camp(Config.CampId.Mazoku);
|
||||
var mazoku = new Camp(Config.CampId.Mazoku)
|
||||
{
|
||||
FriendInjury = true
|
||||
};
|
||||
CampManager.AddCamp(mazoku);
|
||||
var aborigines = new Camp(Config.CampId.Aborigines);
|
||||
CampManager.AddCamp(aborigines);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using ColdMint.scripts.utils;
|
||||
|
||||
namespace ColdMint.scripts.loot;
|
||||
|
@ -9,7 +8,7 @@ namespace ColdMint.scripts.loot;
|
|||
/// <para>Loot entry</para>
|
||||
/// <para>战利品条目</para>
|
||||
/// </summary>
|
||||
public readonly struct LootEntry(string itemId,int minQuantity=1,int maxQuantity = 1,int weight = 1)
|
||||
public readonly struct LootEntry(string itemId, int minQuantity = 1, int maxQuantity = 1, int weight = 1)
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>ID of item</para>
|
||||
|
@ -40,9 +39,15 @@ public readonly struct LootEntry(string itemId,int minQuantity=1,int maxQuantity
|
|||
/// <para>Loot Group</para>
|
||||
/// <para>战利品分组</para>
|
||||
/// </summary>
|
||||
/// <param name="Chance"></param>
|
||||
/// <param name="Entries"></param>
|
||||
public readonly record struct LootGroup(double Chance, IEnumerable<LootEntry> Entries)
|
||||
/// <param name="Chance">
|
||||
///<para>Chance</para>
|
||||
///<para>概率</para>
|
||||
/// </param>
|
||||
/// <param name="Entries">
|
||||
///<para>Entries</para>
|
||||
///<para>条目列表</para>
|
||||
/// </param>
|
||||
public readonly record struct LootGroup(float Chance, IEnumerable<LootEntry> Entries)
|
||||
{
|
||||
private int WeightSum { get; } = Entries.Sum(entry => entry.Weight);
|
||||
|
||||
|
@ -61,6 +66,6 @@ public readonly record struct LootGroup(double Chance, IEnumerable<LootEntry> En
|
|||
|
||||
var quantity = random.Next(entry.MinQuantity, entry.MaxQuantity + 1);
|
||||
|
||||
return new(entry.ItemId, quantity);
|
||||
return new LootDatum(entry.ItemId, quantity);
|
||||
}
|
||||
}
|
|
@ -10,19 +10,18 @@ public static class LootRegister
|
|||
/// </summary>
|
||||
public static void StaticRegister()
|
||||
{
|
||||
//Register the test using the loot table
|
||||
//注册测试使用的战利品表
|
||||
if (Config.IsDebug())
|
||||
{
|
||||
IList<LootGroup> lootGroups = [];
|
||||
lootGroups.Add(new LootGroup(0.8,
|
||||
List<LootGroup> lootGroups =
|
||||
[
|
||||
new LootEntry("degraded_staff_of_the_undead", weight: 2), new LootEntry("staff_of_the_undead")
|
||||
]));
|
||||
lootGroups.Add(new LootGroup(1,
|
||||
new LootGroup(0.8f,
|
||||
[
|
||||
new LootEntry("packsack", minQuantity: 2, maxQuantity: 4)
|
||||
]));
|
||||
|
||||
new LootEntry("staff_necromancy"),
|
||||
]),
|
||||
new LootGroup(1, [new LootEntry("portable_backpacks")])
|
||||
];
|
||||
var testLootList = new LootList(Config.LootListId.Test, lootGroups);
|
||||
LootListManager.RegisterLootList(testLootList);
|
||||
}
|
||||
|
|
|
@ -12,14 +12,15 @@ namespace ColdMint.scripts.map;
|
|||
public partial class AiCharacterSpawn : Marker2D
|
||||
{
|
||||
private PackedScene? _packedScene;
|
||||
[Export]
|
||||
public string? ResPath;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
var resPath = GetMeta("ResPath", Name).AsString();
|
||||
if (!string.IsNullOrEmpty(resPath))
|
||||
if (!string.IsNullOrEmpty(ResPath))
|
||||
{
|
||||
_packedScene = GD.Load<PackedScene>(resPath);
|
||||
_packedScene = GD.Load<PackedScene>(ResPath);
|
||||
}
|
||||
|
||||
EventManager.AiCharacterGenerateEvent += OnAiCharacterGenerateEvent;
|
||||
|
@ -32,7 +33,7 @@ public partial class AiCharacterSpawn : Marker2D
|
|||
/// <param name="aiCharacterGenerateEvent"></param>
|
||||
public void OnAiCharacterGenerateEvent(AiCharacterGenerateEvent aiCharacterGenerateEvent)
|
||||
{
|
||||
if (GameSceneNodeHolder.AiCharacterContainer == null)
|
||||
if (GameSceneDepend.AiCharacterContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -48,7 +49,7 @@ public partial class AiCharacterSpawn : Marker2D
|
|||
return;
|
||||
}
|
||||
|
||||
NodeUtils.CallDeferredAddChild(GameSceneNodeHolder.AiCharacterContainer, aiCharacter);
|
||||
NodeUtils.CallDeferredAddChild(GameSceneDepend.AiCharacterContainer, aiCharacter);
|
||||
aiCharacter.GlobalPosition = GlobalPosition;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ using ColdMint.scripts.map.events;
|
|||
using ColdMint.scripts.map.interfaces;
|
||||
using ColdMint.scripts.map.LayoutParsingStrategy;
|
||||
using ColdMint.scripts.map.layoutStrategy;
|
||||
using ColdMint.scripts.map.preview;
|
||||
using ColdMint.scripts.map.room;
|
||||
using ColdMint.scripts.serialization;
|
||||
using ColdMint.scripts.utils;
|
||||
|
@ -148,9 +149,14 @@ public static class MapGenerator
|
|||
return;
|
||||
}
|
||||
|
||||
if (GameSceneNodeHolder.AiCharacterContainer != null)
|
||||
if (GameSceneDepend.MiniMapContainerNode != null)
|
||||
{
|
||||
NodeUtils.DeleteAllChild(GameSceneNodeHolder.AiCharacterContainer);
|
||||
NodeUtils.DeleteAllChild(GameSceneDepend.MiniMapContainerNode);
|
||||
}
|
||||
|
||||
if (GameSceneDepend.AiCharacterContainer != null)
|
||||
{
|
||||
NodeUtils.DeleteAllChild(GameSceneDepend.AiCharacterContainer);
|
||||
}
|
||||
|
||||
NodeUtils.DeleteAllChild(_mapRoot);
|
||||
|
@ -335,6 +341,26 @@ public static class MapGenerator
|
|||
return false;
|
||||
}
|
||||
|
||||
//Create a room preview image.
|
||||
//创建房间预览图。
|
||||
var image = RoomPreview.CreateImage(roomPlacementData.NewRoom.GetTileMapLayer(Config.TileMapLayerName.Ground));
|
||||
if (GameSceneDepend.MiniMapContainerNode == null || roomPlacementData.Position == null ||
|
||||
image == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var sprite = new Sprite2D();
|
||||
sprite.Scale = new Vector2(5, 5);
|
||||
sprite.Texture = image;
|
||||
//TODO:Calculate the coordinates of the room preview view.
|
||||
//TODO:计算房间预览图的坐标。
|
||||
sprite.Position = GameSceneDepend.MiniMapMidpointCoordinate +
|
||||
roomPlacementData.Position.Value / Config.CellSize;
|
||||
NodeUtils.CallDeferredAddChild(GameSceneDepend.MiniMapContainerNode, sprite);
|
||||
|
||||
//Rooms are added to the dictionary only after the preview is created.
|
||||
//创建预览图后才将房间添加到字典。
|
||||
dictionary.Add(roomNodeDataId, roomPlacementData.NewRoom);
|
||||
LogCat.LogWithFormat("room_placement_information", LogCat.LogLabel.Default, LogCat.UploadFormat, roomNodeDataId,
|
||||
roomPlacementData.Position.ToString());
|
||||
|
|
|
@ -25,11 +25,10 @@ public partial class PlayerSpawn : Marker2D
|
|||
|
||||
private void GameReplayEvent(GameReplayEvent gameReplayEvent)
|
||||
{
|
||||
if (GameSceneNodeHolder.Player != null)
|
||||
if (GameSceneDepend.Player != null)
|
||||
{
|
||||
GameSceneNodeHolder.Player.ProcessMode = ProcessModeEnum.Inherit;
|
||||
GameSceneNodeHolder.Player.GlobalPosition = GlobalPosition;
|
||||
GameSceneNodeHolder.Player.Revive(GameSceneNodeHolder.Player.MaxHp);
|
||||
GameSceneDepend.Player.Revive(GameSceneDepend.Player.MaxHp);
|
||||
GameSceneDepend.Player.GlobalPosition = GlobalPosition;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -42,7 +41,7 @@ public partial class PlayerSpawn : Marker2D
|
|||
/// </summary>
|
||||
private void SpawnPlayer()
|
||||
{
|
||||
if (GameSceneNodeHolder.PlayerContainer == null)
|
||||
if (GameSceneDepend.PlayerContainer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -60,10 +59,10 @@ public partial class PlayerSpawn : Marker2D
|
|||
return;
|
||||
}
|
||||
|
||||
//The player's parent node must be GameSceneNodeHolder PlayerContainer.
|
||||
//玩家的父节点必须是GameSceneNodeHolder.PlayerContainer。
|
||||
NodeUtils.CallDeferredAddChild(GameSceneNodeHolder.PlayerContainer, playerNode);
|
||||
var itemContainer = GameSceneNodeHolder.HotBar?.GetItemContainer();
|
||||
//The player's parent node must be GameSceneDepend PlayerContainer.
|
||||
//玩家的父节点必须是GameSceneDepend.PlayerContainer。
|
||||
NodeUtils.CallDeferredAddChild(GameSceneDepend.PlayerContainer, playerNode);
|
||||
var itemContainer = GameSceneDepend.HotBar?.GetItemContainer();
|
||||
if (itemContainer == null)
|
||||
{
|
||||
//Throws an exception when the item container is empty.
|
||||
|
@ -72,7 +71,7 @@ public partial class PlayerSpawn : Marker2D
|
|||
}
|
||||
|
||||
playerNode.ItemContainer = itemContainer;
|
||||
GameSceneNodeHolder.Player = playerNode;
|
||||
GameSceneDepend.Player = playerNode;
|
||||
playerNode.GlobalPosition = GlobalPosition;
|
||||
}
|
||||
|
||||
|
@ -80,7 +79,7 @@ public partial class PlayerSpawn : Marker2D
|
|||
{
|
||||
//After the map is generated, create the player instance.
|
||||
//当地图生成完成后,创建玩家实例。
|
||||
if (GameSceneNodeHolder.Player != null)
|
||||
if (GameSceneDepend.Player != null)
|
||||
{
|
||||
//An existing player instance will not be created.
|
||||
//已经存在玩家实例,不再创建。
|
||||
|
|
70
scripts/map/preview/RoomPreview.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
namespace ColdMint.scripts.map.preview;
|
||||
|
||||
using Godot;
|
||||
|
||||
public static class RoomPreview
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>Create Image</para>
|
||||
/// <para>创建图像</para>
|
||||
/// </summary>
|
||||
/// <param name="groundTileMapLayer"></param>
|
||||
/// <returns></returns>
|
||||
public static ImageTexture? CreateImage(TileMapLayer? groundTileMapLayer)
|
||||
{
|
||||
if (groundTileMapLayer == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
//Calculate the size of the image.
|
||||
//计算图像的尺寸。
|
||||
//The width is maxX minX and the height is maxY minY.
|
||||
//宽度为maxX-minX,高度为maxY-minY。
|
||||
var maxY = int.MinValue;
|
||||
var maxX = int.MinValue;
|
||||
var minY = int.MaxValue;
|
||||
var minX = int.MaxValue;
|
||||
var cellsArray = groundTileMapLayer.GetUsedCells();
|
||||
foreach (var vector2I in cellsArray)
|
||||
{
|
||||
if (vector2I.Y > maxY)
|
||||
{
|
||||
maxY = vector2I.Y;
|
||||
}
|
||||
|
||||
if (vector2I.Y < minY)
|
||||
{
|
||||
minY = vector2I.Y;
|
||||
}
|
||||
|
||||
if (vector2I.X > maxX)
|
||||
{
|
||||
maxX = vector2I.X;
|
||||
}
|
||||
|
||||
if (vector2I.X < minX)
|
||||
{
|
||||
minX = vector2I.X;
|
||||
}
|
||||
}
|
||||
|
||||
var height = maxY - minY;
|
||||
var width = maxX - minX;
|
||||
var offsetVector2 = Vector2I.Zero - new Vector2I(minX, minY);
|
||||
//Create an image.
|
||||
//创建image。
|
||||
var image = Image.CreateEmpty(width + 1, height + 1, false, Image.Format.Rgba8);
|
||||
//image.Fill(Colors.Green);
|
||||
//Fill in pixels
|
||||
//填充像素点
|
||||
foreach (var vector2I in cellsArray)
|
||||
{
|
||||
image.SetPixel(vector2I.X + offsetVector2.X, vector2I.Y + offsetVector2.Y, Colors.Blue);
|
||||
}
|
||||
|
||||
//Create texture.
|
||||
//创建texture
|
||||
return ImageTexture.CreateFromImage(image);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ColdMint.scripts.character;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.map.dateBean;
|
||||
using ColdMint.scripts.utils;
|
||||
|
@ -23,7 +24,10 @@ public class Room
|
|||
private List<TileMapLayer>? _tileMapLayers;
|
||||
|
||||
private Area2D? _area2D;
|
||||
private PointLight2D? _pointLight2D;
|
||||
private CollisionShape2D? _collisionShape2D;
|
||||
private bool _hasPlayer;
|
||||
private readonly List<CharacterTemplate> _characterTemplateList = [];
|
||||
|
||||
public string? EnterRoomEventHandlerId { get; set; }
|
||||
|
||||
|
@ -46,6 +50,34 @@ public class Room
|
|||
return _tileMapLayers?.FirstOrDefault(tileMapLayer => tileMapLayer.Name == layerName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>ShowAllCharacterTemplate</para>
|
||||
/// <para>显示所有角色模板</para>
|
||||
/// </summary>
|
||||
private void ShowAllCharacterTemplate()
|
||||
{
|
||||
LogCat.LogWithFormat("show_all_node", LogCat.LogLabel.Room, LogCat.UploadFormat,
|
||||
_characterTemplateList.Count);
|
||||
foreach (var characterTemplate in _characterTemplateList)
|
||||
{
|
||||
characterTemplate.Show();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>HideAllCharacterTemplate</para>
|
||||
/// <para>隐藏所有角色模板</para>
|
||||
/// </summary>
|
||||
private void HideAllCharacterTemplate()
|
||||
{
|
||||
LogCat.LogWithFormat("hide_all_node", LogCat.LogLabel.Room, LogCat.UploadFormat,
|
||||
_characterTemplateList.Count);
|
||||
foreach (var characterTemplate in _characterTemplateList)
|
||||
{
|
||||
characterTemplate.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>When a node enters the room</para>
|
||||
/// <para>当有节点进入房间时</para>
|
||||
|
@ -59,6 +91,33 @@ public class Room
|
|||
_rootNode.Name);
|
||||
}
|
||||
|
||||
if (node is Player player)
|
||||
{
|
||||
_characterTemplateList.Add(player);
|
||||
_hasPlayer = true;
|
||||
//The player enters the room, opening up their view.
|
||||
//玩家进入了房间,开放视野。
|
||||
if (_pointLight2D != null)
|
||||
{
|
||||
_pointLight2D.Show();
|
||||
_pointLight2D.Texture = AssetHolder.White100;
|
||||
}
|
||||
|
||||
ShowAllCharacterTemplate();
|
||||
}else if (node is CharacterTemplate characterTemplate)
|
||||
{
|
||||
if (_hasPlayer)
|
||||
{
|
||||
characterTemplate.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
characterTemplate.Hide();
|
||||
}
|
||||
|
||||
_characterTemplateList.Add(characterTemplate);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(EnterRoomEventHandlerId))
|
||||
{
|
||||
return;
|
||||
|
@ -68,6 +127,7 @@ public class Room
|
|||
enterRoomEventHandler?.OnEnterRoom(node, this);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>When a node exits the room</para>
|
||||
/// <para>当有节点退出房间时</para>
|
||||
|
@ -81,6 +141,26 @@ public class Room
|
|||
_rootNode.Name);
|
||||
}
|
||||
|
||||
|
||||
if (node is Player player)
|
||||
{
|
||||
//The player leaves the room, hiding the view.
|
||||
//玩家离开了房间,隐藏视野。
|
||||
_characterTemplateList.Remove(player);
|
||||
_hasPlayer = false;
|
||||
if (_pointLight2D != null)
|
||||
{
|
||||
_pointLight2D.Show();
|
||||
_pointLight2D.Texture = AssetHolder.White25;
|
||||
}
|
||||
|
||||
HideAllCharacterTemplate();
|
||||
}
|
||||
else if (node is CharacterTemplate characterTemplate && characterTemplate.Visible)
|
||||
{
|
||||
_characterTemplateList.Remove(characterTemplate);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(ExitRoomEventHandlerId))
|
||||
{
|
||||
return;
|
||||
|
@ -148,11 +228,17 @@ public class Room
|
|||
//Sets the collision layer that can be detected in the current room area.
|
||||
//设置当前房间区域可检测到的碰撞层。
|
||||
_area2D.SetCollisionMaskValue(Config.LayerNumber.Player, true);
|
||||
_area2D.SetCollisionMaskValue(Config.LayerNumber.Mob, true);
|
||||
_area2D.BodyExited += OnExitRoom;
|
||||
_area2D.BodyEntered += OnEnterRoom;
|
||||
_collisionShape2D = _area2D.GetChild<CollisionShape2D>(0);
|
||||
_roomSlots = GetRoomSlots(GetTileMapLayer(Config.TileMapLayerName.Ground), _area2D,
|
||||
node2D.GetNode<Node2D>("RoomSlotList"));
|
||||
_pointLight2D = node2D.GetNodeOrNull<PointLight2D>("PointLight2D");
|
||||
if (_pointLight2D != null)
|
||||
{
|
||||
_pointLight2D.BlendMode = Light2D.BlendModeEnum.Mix;
|
||||
}
|
||||
}
|
||||
|
||||
public Node2D? RootNode => _rootNode;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.utils;
|
||||
|
|
|
@ -4,6 +4,7 @@ using ColdMint.scripts.character;
|
|||
using ColdMint.scripts.damage;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.inventory;
|
||||
using ColdMint.scripts.utils;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.pickable;
|
||||
|
@ -14,12 +15,21 @@ namespace ColdMint.scripts.pickable;
|
|||
/// </summary>
|
||||
public partial class PickAbleTemplate : RigidBody2D, IItem
|
||||
{
|
||||
[Export] public virtual string Id { get; set; } = "ID";
|
||||
//Do not export this field because the ID is specified within yaml.
|
||||
//不要导出此字段,因为ID是在yaml内指定的。
|
||||
public virtual string Id { get; set; } = "ID";
|
||||
[Export] protected Texture2D? UniqueIcon { get; set; }
|
||||
public Texture2D Icon => UniqueIcon ?? ItemTypeManager.DefaultIconOf(Id);
|
||||
[Export] protected string? UniqueName { get; set; }
|
||||
public new string Name => UniqueName ?? ItemTypeManager.DefaultNameOf(Id);
|
||||
[Export] protected string? UniqueDescription { get; set; }
|
||||
|
||||
public new string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
var key = $"item_{Id}";
|
||||
return TranslationServerUtils.Translate(key) ?? key;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanPutInPack => true;
|
||||
|
||||
/// <summary>
|
||||
|
@ -37,7 +47,15 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
|||
[Export] private int _minContactInjury = 1;
|
||||
[Export] private int _maxContactInjury = 2;
|
||||
|
||||
public string? Description => UniqueDescription ?? ItemTypeManager.DefaultDescriptionOf(Id);
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
{
|
||||
var key = $"item_{Id}_desc";
|
||||
return TranslationServerUtils.Translate(key) ?? key;
|
||||
}
|
||||
}
|
||||
|
||||
public int Quantity { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
|
@ -60,6 +78,8 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
|||
|
||||
public int MaxQuantity { get; set; }
|
||||
|
||||
private Label? _tipLabel;
|
||||
|
||||
public virtual void Use(Node2D? owner, Vector2 targetGlobalPosition)
|
||||
{
|
||||
}
|
||||
|
@ -69,6 +89,8 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
|||
_damageArea2D = GetNode<Area2D>("DamageArea2D");
|
||||
_damageArea2D.BodyEntered += OnBodyEnter;
|
||||
_damageArea2D.BodyExited += OnBodyExited;
|
||||
_tipLabel = GetNodeOrNull<Label>("TipLabel");
|
||||
InputPickable = true;
|
||||
}
|
||||
|
||||
private void OnBodyExited(Node node)
|
||||
|
@ -88,6 +110,7 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
|||
//No longer in contact with any shingles can cause injury
|
||||
//不再与任何瓦片接触后,可以造成伤害
|
||||
EnableContactInjury = true;
|
||||
LogCat.Log("after_no_longer_in_contact_with_any_tiles", LogCat.LogLabel.ContactInjury);
|
||||
SetCollisionMaskValue(Config.LayerNumber.Player, false);
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +132,7 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
|||
{
|
||||
_tileMapNumber++;
|
||||
EnableContactInjury = false;
|
||||
LogCat.Log("contact_with_tiles_disables_damage", LogCat.LogLabel.ContactInjury);
|
||||
//Items can be pushed by the player when they are on the ground
|
||||
//当物品在地面上时,可被玩家推动
|
||||
SetCollisionMaskValue(Config.LayerNumber.Player, true);
|
||||
|
@ -117,20 +141,20 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
|||
{
|
||||
if (!EnableContactInjury)
|
||||
{
|
||||
LogCat.LogWarning("contact_damage_disabled_during_collision");
|
||||
LogCat.LogWarning("contact_damage_disabled_during_collision", LogCat.LogLabel.ContactInjury);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (Owner == null)
|
||||
{
|
||||
LogCat.LogWarning("item_has_no_owner");
|
||||
LogCat.LogWarning("item_has_no_owner", LogCat.LogLabel.PickAbleTemplate);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Owner is not CharacterTemplate ownerCharacterTemplate)
|
||||
{
|
||||
LogCat.LogWarning("owner_of_the_item_is_not_character");
|
||||
LogCat.LogWarning("owner_of_the_item_is_not_character", LogCat.LogLabel.PickAbleTemplate);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -140,7 +164,7 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
|||
CampManager.GetCamp(characterTemplate.CampId));
|
||||
if (!canCauseHarm)
|
||||
{
|
||||
LogCat.Log("no_damage_between_camps");
|
||||
LogCat.Log("no_damage_between_camps", LogCat.LogLabel.PickAbleTemplate);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -163,6 +187,35 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
|||
}
|
||||
|
||||
|
||||
public override void _MouseEnter()
|
||||
{
|
||||
if (Picked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_tipLabel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_tipLabel.Visible = true;
|
||||
_tipLabel.Text = Name;
|
||||
//Vertical Centering Tip
|
||||
//垂直居中提示
|
||||
var oldPosition = _tipLabel.Position;
|
||||
oldPosition.X = -_tipLabel.Size.X / 2;
|
||||
_tipLabel.Rotation = -Rotation;
|
||||
_tipLabel.Position = oldPosition;
|
||||
}
|
||||
|
||||
public override void _MouseExit()
|
||||
{
|
||||
if (_tipLabel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_tipLabel.Visible = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Flip item</para>
|
||||
/// <para>翻转物品</para>
|
||||
|
@ -172,6 +225,20 @@ public partial class PickAbleTemplate : RigidBody2D, IItem
|
|||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Please copy node properties within this function</para>
|
||||
/// <para>请在此函数内复制节点属性</para>
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
public void CopyAttributes(Node node)
|
||||
{
|
||||
if (node is not PickAbleTemplate pickAbleTemplate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pickAbleTemplate.Id = Id;
|
||||
}
|
||||
|
||||
public virtual void Destroy()
|
||||
{
|
||||
QueueFree();
|
||||
|
|
|
@ -1,3 +1,363 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ColdMint.scripts.camp;
|
||||
using ColdMint.scripts.character;
|
||||
using ColdMint.scripts.damage;
|
||||
using ColdMint.scripts.pickable;
|
||||
using ColdMint.scripts.projectile.decorator;
|
||||
using ColdMint.scripts.utils;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.projectile;
|
||||
|
||||
public partial class Projectile : ProjectileTemplate;
|
||||
/// <summary>
|
||||
/// <para>Projectile</para>
|
||||
/// <para>抛射体</para>
|
||||
/// </summary>
|
||||
public partial class Projectile : CharacterBody2D
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>life(ms)</para>
|
||||
/// <para>子弹的存在时间(毫秒)</para>
|
||||
/// </summary>
|
||||
[Export] private long _life;
|
||||
|
||||
//The durability of the projectile
|
||||
//抛射体的耐久度
|
||||
//When the projectile hits the object, the durability will be reduced, and when the durability is less than or equal to 0, the projectile will be destroyed
|
||||
//当抛射体撞击到物体时,会减少耐久度,当耐久度小于等于0时,销毁抛射体
|
||||
[Export] private double _durability;
|
||||
|
||||
[Export] private int _maxDamage;
|
||||
[Export] private int _minDamage;
|
||||
[Export] private int _damageType;
|
||||
|
||||
/// <summary>
|
||||
/// <para>After this time destroy the projectile</para>
|
||||
/// <para>超过此时刻销毁抛射体</para>
|
||||
/// </summary>
|
||||
private DateTime? _destructionTime;
|
||||
|
||||
[Export] public float Speed;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Whether it bounces back after hitting an enemy or a wall</para>
|
||||
/// <para>是否撞到敌人或墙壁后反弹</para>
|
||||
/// </summary>
|
||||
[Export] private bool _enableBounce;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Can it penetrate the wall</para>
|
||||
/// <para>是否可以穿透墙壁</para>
|
||||
/// </summary>
|
||||
[Export] private bool _ignoreWall;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Enable the tracking of the enemy</para>
|
||||
/// <para>启用追踪敌人的功能</para>
|
||||
/// </summary>
|
||||
[Export] private bool _enableTracking;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The target dies and destroys the projectile at the same time</para>
|
||||
/// <para>在目标死亡后销毁抛射体</para>
|
||||
/// </summary>
|
||||
[Export] private bool _targetDiesDestroyProjectile;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The target</para>
|
||||
/// <para>设置目标</para>
|
||||
/// </summary>
|
||||
public Node2D? TargetNode { get; set; }
|
||||
|
||||
private List<IProjectileDecorator>? _projectileDecorators;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Rays used to detect walls</para>
|
||||
/// <para>用于检测墙壁的射线</para>
|
||||
/// </summary>
|
||||
private RayCast2D? _wallRayCast;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Repel strength</para>
|
||||
/// <para>击退强度</para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///<para>Must be greater than 0, the unit is in format.</para>
|
||||
///<para>必须大于0,单位为格式。</para>
|
||||
/// </remarks>
|
||||
[Export] private float _repelStrength;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The master of the weapon</para>
|
||||
/// <para>武器的主人</para>
|
||||
/// </summary>
|
||||
public new Node2D? Owner { get; set; }
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
if (!_ignoreWall)
|
||||
{
|
||||
_wallRayCast = new RayCast2D();
|
||||
_wallRayCast.SetCollisionMaskValue(Config.LayerNumber.Wall, true);
|
||||
_wallRayCast.SetCollisionMaskValue(Config.LayerNumber.Floor, true);
|
||||
NodeUtils.CallDeferredAddChild(this, _wallRayCast);
|
||||
}
|
||||
|
||||
//If the existence time is less than or equal to 0, then it is set to exist for 10 seconds, and projectiles that exist indefinitely are prohibited
|
||||
//如果存在时间小于等于0,那么设置为存在10秒,禁止无限期存在的抛射体
|
||||
if (_life <= 0)
|
||||
{
|
||||
_life = 10000;
|
||||
}
|
||||
|
||||
_destructionTime = DateTime.Now.AddMilliseconds(_life);
|
||||
SetCollisionMaskValue(Config.LayerNumber.Wall, !_ignoreWall);
|
||||
SetCollisionMaskValue(Config.LayerNumber.Floor, !_ignoreWall);
|
||||
SetCollisionMaskValue(Config.LayerNumber.Player, true);
|
||||
SetCollisionMaskValue(Config.LayerNumber.Mob, true);
|
||||
SetCollisionMaskValue(Config.LayerNumber.PickAbleItem, true);
|
||||
//Platform collision layer is not allowed to collide
|
||||
//平台碰撞层不可碰撞
|
||||
SetCollisionMaskValue(Config.LayerNumber.Platform, false);
|
||||
if (TargetNode != null)
|
||||
{
|
||||
TargetNode.TreeExiting += () =>
|
||||
{
|
||||
//Clear the trace when the target is destroyed.
|
||||
//在目标被销毁的时候清空跟踪。
|
||||
TargetNode = null;
|
||||
if (_targetDiesDestroyProjectile)
|
||||
{
|
||||
OnTimeOut();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Add decorator</para>
|
||||
/// <para>添加装饰器</para>
|
||||
/// </summary>
|
||||
/// <param name="decorator">
|
||||
///<para>decorator</para>
|
||||
///<para>装饰器</para>
|
||||
/// </param>
|
||||
public void AddProjectileDecorator(IProjectileDecorator decorator)
|
||||
{
|
||||
_projectileDecorators ??= [];
|
||||
_projectileDecorators.Add(decorator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Remove decorator</para>
|
||||
/// <para>移除装饰器</para>
|
||||
/// </summary>
|
||||
/// <param name="decorator">
|
||||
///<para>decorator</para>
|
||||
///<para>装饰器</para>
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveProjectileDecorator(IProjectileDecorator decorator)
|
||||
{
|
||||
return _projectileDecorators?.Remove(decorator) ?? false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Detect whether harm is allowed</para>
|
||||
/// <para>检测是否允许造成伤害</para>
|
||||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
private bool CanCauseHarm(Node2D? owner, Node2D target)
|
||||
{
|
||||
//We must know who the owner of the bullet is in order to determine whether it should cause damage or not
|
||||
//我们必须知道子弹的主人是谁,才能判断是否应该造成伤害
|
||||
if (owner == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (owner is not CharacterTemplate ownerCharacterTemplate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target is TileMapLayer)
|
||||
{
|
||||
//When we hit the tile, we return true to prevent the bullet from penetrating the tile.
|
||||
//撞击到瓦片时,我们返回true,是为了防止子弹穿透瓦片。
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target is PickAbleTemplate pickAbleTemplate)
|
||||
{
|
||||
//The picked-up item cannot resist the bullet.
|
||||
//被拾起的物品无法抵挡子弹。
|
||||
return !pickAbleTemplate.Picked;
|
||||
}
|
||||
|
||||
if (target is not CharacterTemplate characterTemplate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//First get the owner's camp and compare it with the target camp
|
||||
//先获取主人的阵营与目标阵营进行比较
|
||||
var canCauseHarm = CampManager.CanCauseHarm(CampManager.GetCamp(ownerCharacterTemplate.CampId),
|
||||
CampManager.GetCamp(characterTemplate.CampId));
|
||||
return canCauseHarm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Executive injury treatment</para>
|
||||
/// <para>执行伤害处理</para>
|
||||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
/// <param name="target"></param>
|
||||
private void DoDamage(Node2D? owner, Node2D target)
|
||||
{
|
||||
if (target is CharacterTemplate characterTemplate)
|
||||
{
|
||||
//Allow damage to be caused
|
||||
//允许造成伤害
|
||||
var damage = new Damage
|
||||
{
|
||||
Attacker = owner,
|
||||
MaxDamage = _maxDamage,
|
||||
MinDamage = _minDamage
|
||||
};
|
||||
damage.CreateDamage();
|
||||
damage.MoveLeft = Velocity.X < 0;
|
||||
damage.Type = _damageType;
|
||||
var dead = characterTemplate.Damage(damage);
|
||||
if (dead)
|
||||
{
|
||||
//If the character is dead, then call OnKillCharacter
|
||||
//如果角色死亡了,那么调用OnKillCharacter
|
||||
InvokeDecorators(decorator => { decorator.OnKillCharacter(owner, characterTemplate); });
|
||||
}
|
||||
|
||||
if (_repelStrength > 0)
|
||||
{
|
||||
//If we set the attack force, then apply the force to the object
|
||||
//如果我们设置了攻退力,那么将力应用到对象上
|
||||
var normalized = Velocity.Normalized();
|
||||
characterTemplate.AddForce(new Vector2(normalized.X * _repelStrength * Config.CellSize,
|
||||
normalized.Y * _repelStrength * Config.CellSize));
|
||||
}
|
||||
}
|
||||
else if (target is PickAbleTemplate pickAbleTemplate)
|
||||
{
|
||||
if (_repelStrength > 0)
|
||||
{
|
||||
//If we set the attack force, then apply the force to the object
|
||||
//如果我们设置了攻退力,那么将力应用到对象上
|
||||
var normalized = Velocity.Normalized();
|
||||
pickAbleTemplate.ApplyImpulse(new Vector2(normalized.X * _repelStrength * Config.CellSize,
|
||||
normalized.Y * _repelStrength * Config.CellSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Call the method of the decorator</para>
|
||||
/// <para>调用装饰器的方法</para>
|
||||
/// </summary>
|
||||
/// <param name="projectileDecoratorAction"></param>
|
||||
private void InvokeDecorators(Action<IProjectileDecorator> projectileDecoratorAction)
|
||||
{
|
||||
if (_projectileDecorators == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var decorator in _projectileDecorators)
|
||||
{
|
||||
projectileDecoratorAction(decorator);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>When beyond the time of existence</para>
|
||||
/// <para>当超过存在时间</para>
|
||||
/// </summary>
|
||||
private void OnTimeOut()
|
||||
{
|
||||
QueueFree();
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
//If the existence time is exceeded, the projectile is destroyed
|
||||
//如果超过了存在时间,那么销毁抛射体
|
||||
if (DateTime.Now >= _destructionTime)
|
||||
{
|
||||
OnTimeOut();
|
||||
}
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
var collisionInfo = MoveAndCollide(Velocity * (float)delta);
|
||||
if (collisionInfo == null)
|
||||
{
|
||||
//No collision.
|
||||
//没有撞到任何东西。
|
||||
if (_enableTracking && TargetNode != null)
|
||||
{
|
||||
//Track the target
|
||||
//追踪目标
|
||||
//Gets a vector of the projectile pointing at the enemy's position.
|
||||
//得到抛射体指向敌人位置的向量。
|
||||
var desiredVelocity = TargetNode.GlobalPosition - GlobalPosition;
|
||||
if (!_ignoreWall && _wallRayCast != null)
|
||||
{
|
||||
_wallRayCast!.TargetPosition = desiredVelocity;
|
||||
if (_wallRayCast.IsColliding())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var actualDesiredVelocity = desiredVelocity.Normalized() * Speed;
|
||||
//The weight is smaller, the circle is larger.
|
||||
//weight越小,子弹绕的圈越大。
|
||||
Velocity = Velocity.Lerp(actualDesiredVelocity, 0.1f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Bump into other objects.
|
||||
//撞到其他对象。
|
||||
if (_enableBounce)
|
||||
{
|
||||
Velocity = Velocity.Bounce(collisionInfo.GetNormal());
|
||||
}
|
||||
|
||||
//Here we test whether harm is allowed, notice that for TileMap, we directly allow harm.
|
||||
//这里我们检测是否允许造成伤害,注意对于TileMap,我们直接允许造成伤害。
|
||||
var godotObject = collisionInfo.GetCollider();
|
||||
var node = (Node2D)godotObject;
|
||||
var canCauseHarm = CanCauseHarm(Owner, node);
|
||||
if (!canCauseHarm)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DoDamage(Owner, node);
|
||||
//Please specify in the Mask who the bullet will collide with
|
||||
//请在Mask内配置子弹会和谁碰撞
|
||||
//When a bullet hits an object, its durability decreases
|
||||
//子弹撞击到物体时,耐久度减少
|
||||
_durability--;
|
||||
if (_durability <= 0)
|
||||
{
|
||||
//When the durability is less than or equal to 0, destroy the bullet
|
||||
//当耐久度小于等于0时,销毁子弹
|
||||
QueueFree();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
using System;
|
||||
using ColdMint.scripts.camp;
|
||||
using ColdMint.scripts.character;
|
||||
using ColdMint.scripts.damage;
|
||||
using ColdMint.scripts.inventory;
|
||||
using ColdMint.scripts.pickable;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.projectile;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Projectile template</para>
|
||||
/// <para>抛射体模板</para>
|
||||
/// </summary>
|
||||
public partial class ProjectileTemplate : CharacterBody2D
|
||||
{
|
||||
private long _life;
|
||||
|
||||
//The durability of the projectile
|
||||
//抛射体的耐久度
|
||||
//When the projectile hits the object, the durability will be reduced, and when the durability is less than or equal to 0, the projectile will be destroyed
|
||||
//当抛射体撞击到物体时,会减少耐久度,当耐久度小于等于0时,销毁抛射体
|
||||
private double _durability;
|
||||
|
||||
private int _maxDamage;
|
||||
private int _minDamage;
|
||||
private int _damageType;
|
||||
|
||||
/// <summary>
|
||||
/// <para>After this time destroy the projectile</para>
|
||||
/// <para>超过此时刻销毁抛射体</para>
|
||||
/// </summary>
|
||||
private DateTime? _destructionTime;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>The impact area of the bullet</para>
|
||||
/// <para>子弹的碰撞区域</para>
|
||||
/// </summary>
|
||||
private Area2D? _area2D;
|
||||
|
||||
/// <summary>
|
||||
/// <para>knockback</para>
|
||||
/// <para>击退</para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///<para>How much force does it have when hitting the character? Unit: Number of cells,The X direction of the force is inferred automatically.</para>
|
||||
///<para>当击中角色时带有多大的力?单位:格数,力的X方向是自动推断的。</para>
|
||||
/// </remarks>
|
||||
private Vector2 _knockbackForce;
|
||||
|
||||
public float Speed => GetMeta("Speed", "15").AsSingle();
|
||||
|
||||
/// <summary>
|
||||
/// <para>The master of the weapon</para>
|
||||
/// <para>武器的主人</para>
|
||||
/// </summary>
|
||||
public new Node2D? Owner { get; set; }
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
//The bullet's impact detection area
|
||||
//子弹的碰撞检测区域
|
||||
_area2D = GetNode<Area2D>("CollisionDetectionArea");
|
||||
_area2D.Monitoring = true;
|
||||
_area2D.BodyEntered += OnBodyEnter;
|
||||
_area2D.BodyExited += OnBodyExited;
|
||||
_durability = GetMeta("Durability", "1").AsDouble();
|
||||
_maxDamage = GetMeta("MaxDamage", "7").AsInt32();
|
||||
_minDamage = GetMeta("MinDamage", "5").AsInt32();
|
||||
_damageType = GetMeta("DamageType", Config.DamageType.Physical).AsInt32();
|
||||
_knockbackForce = GetMeta("Knockback", Vector2.Zero).AsVector2();
|
||||
//life(ms)
|
||||
//子弹的存在时间(毫秒)
|
||||
_life = GetMeta("Life", "10000").AsInt64();
|
||||
//If the existence time is less than or equal to 0, then it is set to exist for 10 seconds, and projectiles that exist indefinitely are prohibited
|
||||
//如果存在时间小于等于0,那么设置为存在10秒,禁止无限期存在的抛射体
|
||||
if (_life <= 0)
|
||||
{
|
||||
_life = 10000;
|
||||
}
|
||||
|
||||
_destructionTime = DateTime.Now.AddMilliseconds(_life);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>Detect whether harm is allowed</para>
|
||||
/// <para>检测是否允许造成伤害</para>
|
||||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
private bool CanCauseHarm(Node2D? owner, Node2D target)
|
||||
{
|
||||
//We must know who the owner of the bullet is in order to determine whether it should cause damage or not
|
||||
//我们必须知道子弹的主人是谁,才能判断是否应该造成伤害
|
||||
if (owner == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (owner is not CharacterTemplate ownerCharacterTemplate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target is TileMapLayer)
|
||||
{
|
||||
//When we hit the tile, we return true to prevent the bullet from penetrating the tile.
|
||||
//撞击到瓦片时,我们返回true,是为了防止子弹穿透瓦片。
|
||||
return true;
|
||||
}
|
||||
|
||||
//Match any item now
|
||||
//现在使它识别任何物品
|
||||
if (target is IItem)
|
||||
{
|
||||
//Bullets are allowed to strike objects.
|
||||
//允许子弹撞击物品。
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target is not CharacterTemplate characterTemplate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//First get the owner's camp and compare it with the target camp
|
||||
//先获取主人的阵营与目标阵营进行比较
|
||||
var canCauseHarm = CampManager.CanCauseHarm(CampManager.GetCamp(ownerCharacterTemplate.CampId),
|
||||
CampManager.GetCamp(characterTemplate.CampId));
|
||||
return canCauseHarm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Executive injury treatment</para>
|
||||
/// <para>执行伤害处理</para>
|
||||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
/// <param name="target"></param>
|
||||
private void DoDamage(Node2D? owner, Node2D target)
|
||||
{
|
||||
if (target is CharacterTemplate characterTemplate)
|
||||
{
|
||||
//Allow damage to be caused
|
||||
//允许造成伤害
|
||||
var damage = new Damage
|
||||
{
|
||||
Attacker = owner,
|
||||
MaxDamage = _maxDamage,
|
||||
MinDamage = _minDamage
|
||||
};
|
||||
damage.CreateDamage();
|
||||
damage.MoveLeft = Velocity.X < 0;
|
||||
damage.Type = _damageType;
|
||||
characterTemplate.Damage(damage);
|
||||
if (_knockbackForce != Vector2.Zero)
|
||||
{
|
||||
//If we set the attack force, then apply the force to the object
|
||||
//如果我们设置了攻退力,那么将力应用到对象上
|
||||
var force = new Vector2();
|
||||
var forceX = Math.Abs(_knockbackForce.X);
|
||||
if (Velocity.X < 0)
|
||||
{
|
||||
//Beat back to port
|
||||
//向左击退
|
||||
forceX = -forceX;
|
||||
}
|
||||
|
||||
force.X = forceX * Config.CellSize;
|
||||
force.Y = _knockbackForce.Y * Config.CellSize;
|
||||
characterTemplate.AddForce(force);
|
||||
}
|
||||
}
|
||||
else if (target is PickAbleTemplate pickAbleTemplate)
|
||||
{
|
||||
if (_knockbackForce != Vector2.Zero)
|
||||
{
|
||||
//If we set the attack force, then apply the force to the object
|
||||
//如果我们设置了攻退力,那么将力应用到对象上
|
||||
var force = new Vector2();
|
||||
var forceX = Math.Abs(_knockbackForce.X);
|
||||
if (Velocity.X < 0)
|
||||
{
|
||||
//Beat back to port
|
||||
//向左击退
|
||||
forceX = -forceX;
|
||||
}
|
||||
|
||||
force.X = forceX * Config.CellSize;
|
||||
force.Y = _knockbackForce.Y * Config.CellSize;
|
||||
pickAbleTemplate.ApplyImpulse(force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>When the bullet is in contact with the node</para>
|
||||
/// <para>当子弹与节点接触时</para>
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
protected virtual void OnBodyEnter(Node2D node)
|
||||
{
|
||||
//Here we test whether harm is allowed, notice that for TileMap, we directly allow harm.
|
||||
//这里我们检测是否允许造成伤害,注意对于TileMap,我们直接允许造成伤害。
|
||||
var canCauseHarm = CanCauseHarm(Owner, node);
|
||||
if (!canCauseHarm)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DoDamage(Owner, node);
|
||||
//Please specify in the Mask who the bullet will collide with
|
||||
//请在Mask内配置子弹会和谁碰撞
|
||||
//When a bullet hits an object, its durability decreases
|
||||
//子弹撞击到物体时,耐久度减少
|
||||
_durability--;
|
||||
if (_durability <= 0)
|
||||
{
|
||||
//When the durability is less than or equal to 0, destroy the bullet
|
||||
//当耐久度小于等于0时,销毁子弹
|
||||
QueueFree();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>When the bullet leaves the node</para>
|
||||
/// <para>当子弹离开节点时</para>
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
protected virtual void OnBodyExited(Node2D node)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>When beyond the time of existence</para>
|
||||
/// <para>当超过存在时间</para>
|
||||
/// </summary>
|
||||
private void OnTimeOut()
|
||||
{
|
||||
QueueFree();
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
//If the existence time is exceeded, the projectile is destroyed
|
||||
//如果超过了存在时间,那么销毁抛射体
|
||||
if (DateTime.Now >= _destructionTime)
|
||||
{
|
||||
OnTimeOut();
|
||||
}
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
MoveAndSlide();
|
||||
}
|
||||
}
|
29
scripts/projectile/decorator/IProjectileDecorator.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using ColdMint.scripts.character;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.projectile.decorator;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Projectile decorator</para>
|
||||
/// <para>抛射体装饰</para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
///<para>Decorator mode is a structural mode, which can add special features to the projectile.</para>
|
||||
///<para>装饰模式是一种结构性模式,可以将一种抛射体的特殊功能添加到抛射体上。</para>
|
||||
/// </remarks>
|
||||
public interface IProjectileDecorator
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>When the character is killed by this projectile</para>
|
||||
/// <para>当角色被此抛射体击杀时</para>
|
||||
/// </summary>
|
||||
/// <param name="owner">
|
||||
/// <para>owner</para>
|
||||
/// <para>主人</para>
|
||||
/// </param>
|
||||
/// <param name="target">
|
||||
///<para>target</para>
|
||||
///<para>目标</para>
|
||||
/// </param>
|
||||
void OnKillCharacter(Node2D? owner, CharacterTemplate target);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
using ColdMint.scripts.character;
|
||||
using ColdMint.scripts.utils;
|
||||
using Godot;
|
||||
|
||||
namespace ColdMint.scripts.projectile.decorator;
|
||||
|
||||
/// <summary>
|
||||
/// <para>NodeSpawnOnKillCharacterDecorator</para>
|
||||
/// <para>在击杀角色后生成节点</para>
|
||||
/// </summary>
|
||||
public class NodeSpawnOnKillCharacterDecorator : IProjectileDecorator
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>PackedScenePath</para>
|
||||
/// <para>要实例化的场景路径</para>
|
||||
/// </summary>
|
||||
public string? PackedScenePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>DefaultParentNode</para>
|
||||
/// <para>默认的父节点</para>
|
||||
/// </summary>
|
||||
public Node? DefaultParentNode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// <para>Chance</para>
|
||||
/// <para>生成概率</para>
|
||||
/// </summary>
|
||||
public float Chance { get; set; } = 1f;
|
||||
|
||||
public void OnKillCharacter(Node2D? owner, CharacterTemplate target)
|
||||
{
|
||||
if (RandomUtils.Instance.NextSingle() > Chance)
|
||||
{
|
||||
//Not in probability, straight back.
|
||||
//没有在概率内,直接返回。
|
||||
return;
|
||||
}
|
||||
|
||||
if (PackedScenePath == null || DefaultParentNode == null || !target.CanMutateAfterDeath)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var packedScene = GD.Load<PackedScene>(PackedScenePath);
|
||||
var node2D = NodeUtils.InstantiatePackedScene<Node2D>(packedScene);
|
||||
if (node2D == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var container = NodeUtils.FindContainerNode(node2D, DefaultParentNode);
|
||||
node2D.GlobalPosition = target.GlobalPosition;
|
||||
NodeUtils.CallDeferredAddChild(container, node2D);
|
||||
}
|
||||
}
|
123
scripts/utils/LightMaskUtils.cs
Normal file
|
@ -0,0 +1,123 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace ColdMint.scripts.utils;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Light layer utils</para>
|
||||
/// <para>光照层工具</para>
|
||||
/// </summary>
|
||||
public static class LightMaskUtils
|
||||
{
|
||||
private static readonly int[] PowInts =
|
||||
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288];
|
||||
|
||||
/// <summary>
|
||||
/// <para>Pass in a number and return the largest subscript that matches it.</para>
|
||||
/// <para>传入一个数字,返回与其匹配的最大下标</para>
|
||||
/// </summary>
|
||||
/// <param name="number"></param>
|
||||
/// <param name="startIndex"></param>
|
||||
/// <returns></returns>
|
||||
private static int GetMaxPow(int number, int startIndex)
|
||||
{
|
||||
for (var i = startIndex - 1; i >= 0; i--)
|
||||
{
|
||||
var pow = PowInts[i];
|
||||
if (number >= pow)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>ParseMaskValue</para>
|
||||
/// <para>解析Mask的值</para>
|
||||
/// </summary>
|
||||
/// <param name="maskValue">
|
||||
///<para>maskValue</para>
|
||||
///<para>值</para>
|
||||
/// </param>
|
||||
/// <param name="afterParsingTheValue">
|
||||
///<para>This callback returns true when the value is parsed, stopping the parsing immediately.</para>
|
||||
///<para>当解析到值时,此回调返回true,立即停止解析。</para>
|
||||
/// </param>
|
||||
/// <returns>
|
||||
///<para>The position of the element corresponding to its value, For example, passing in 10, returning [1,3]</para>
|
||||
///<para>与其值对应的元素位置,例如传入10,返回[1,3]</para>
|
||||
/// </returns>
|
||||
public static int[] ParseMaskValue(int maskValue, Func<int, bool>? afterParsingTheValue = null)
|
||||
{
|
||||
var result = new List<int>();
|
||||
var startIndex = PowInts.Length;
|
||||
var indexInPowIntArray = GetMaxPow(maskValue, startIndex);
|
||||
while (indexInPowIntArray > -1)
|
||||
{
|
||||
result.Insert(0, indexInPowIntArray);
|
||||
if (afterParsingTheValue != null)
|
||||
{
|
||||
if (afterParsingTheValue.Invoke(indexInPowIntArray))
|
||||
{
|
||||
//If it needs to be stopped, then the result is returned directly.
|
||||
//如果需要停止,那么直接返回结果。
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
maskValue -= PowInts[indexInPowIntArray];
|
||||
startIndex = indexInPowIntArray;
|
||||
indexInPowIntArray = GetMaxPow(maskValue, startIndex);
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Is there a location within the mask value?</para>
|
||||
/// <para>mask值内是否包含某个位置?</para>
|
||||
/// </summary>
|
||||
/// <param name="maskValue"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static bool ContainsMaskValue(int maskValue, int index)
|
||||
{
|
||||
var result = false;
|
||||
ParseMaskValue(maskValue, i =>
|
||||
{
|
||||
result = i == index;
|
||||
return result;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Add a location to MaskValue</para>
|
||||
/// <para>为MaskValue添加某个位置</para>
|
||||
/// </summary>
|
||||
/// <param name="maskValue"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static int AddIndexToMaskValue(int maskValue, int index)
|
||||
{
|
||||
if (ContainsMaskValue(maskValue,index))
|
||||
{
|
||||
return maskValue;
|
||||
}
|
||||
return maskValue + PowInts[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Converting an array to its corresponding value</para>
|
||||
/// <para>将数组转化为与其对应的值</para>
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <returns></returns>
|
||||
public static int ArrayToMaskValue(int[] array)
|
||||
{
|
||||
return array.Sum(index => PowInts[index]);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using ColdMint.scripts.character;
|
||||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.projectile;
|
||||
using Godot;
|
||||
|
@ -224,24 +225,29 @@ public static class NodeUtils
|
|||
/// <returns></returns>
|
||||
public static Node FindContainerNode(Node childNode, Node defaultParentNode)
|
||||
{
|
||||
if (GameSceneNodeHolder.ProjectileContainer != null && childNode is Projectile)
|
||||
if (GameSceneDepend.AiCharacterContainer!= null && childNode is AiCharacter)
|
||||
{
|
||||
return GameSceneNodeHolder.ProjectileContainer;
|
||||
return GameSceneDepend.AiCharacterContainer;
|
||||
}
|
||||
|
||||
if (GameSceneNodeHolder.WeaponContainer != null && childNode is WeaponTemplate)
|
||||
if (GameSceneDepend.ProjectileContainer != null && childNode is Projectile)
|
||||
{
|
||||
return GameSceneNodeHolder.WeaponContainer;
|
||||
return GameSceneDepend.ProjectileContainer;
|
||||
}
|
||||
|
||||
if (GameSceneNodeHolder.PacksackContainer != null && childNode is Packsack)
|
||||
if (GameSceneDepend.WeaponContainer != null && childNode is WeaponTemplate)
|
||||
{
|
||||
return GameSceneNodeHolder.PacksackContainer;
|
||||
return GameSceneDepend.WeaponContainer;
|
||||
}
|
||||
|
||||
if (GameSceneNodeHolder.BackpackUiContainer != null && childNode is PacksackUi)
|
||||
if (GameSceneDepend.PacksackContainer != null && childNode is Packsack)
|
||||
{
|
||||
return GameSceneNodeHolder.BackpackUiContainer;
|
||||
return GameSceneDepend.PacksackContainer;
|
||||
}
|
||||
|
||||
if (GameSceneDepend.BackpackUiContainer != null && childNode is PacksackUi)
|
||||
{
|
||||
return GameSceneDepend.BackpackUiContainer;
|
||||
}
|
||||
|
||||
return defaultParentNode;
|
||||
|
|
|
@ -29,6 +29,6 @@ public static class TranslationServerUtils
|
|||
/// <returns></returns>
|
||||
public static string? Translate(string? key)
|
||||
{
|
||||
return TranslationServer.Translate(key);
|
||||
return key == null ? null : TranslationServer.Translate(key);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using ColdMint.scripts.debug;
|
||||
using ColdMint.scripts.projectile;
|
||||
using ColdMint.scripts.projectile.decorator;
|
||||
using ColdMint.scripts.utils;
|
||||
using Godot;
|
||||
|
||||
|
@ -21,14 +22,105 @@ public partial class ProjectileWeapon : WeaponTemplate
|
|||
/// </summary>
|
||||
private Marker2D? _marker2D;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Scattering radians</para>
|
||||
/// <para>散射弧度</para>
|
||||
/// </summary>
|
||||
[Export] protected float OffsetAngle;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Offset angle mode</para>
|
||||
/// <para>偏移角度模式</para>
|
||||
/// </summary>
|
||||
[Export] protected int OffsetAngleMode = Config.OffsetAngleMode.Random;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Whether the last offset angle is positive</para>
|
||||
/// <para>上次的偏移角度是否为正向的</para>
|
||||
/// </summary>
|
||||
private bool _positiveOffsetAngle = true;
|
||||
|
||||
/// <summary>
|
||||
/// <para>The number of projectiles fired at once</para>
|
||||
/// <para>一次可以发射多少个子弹</para>
|
||||
/// </summary>
|
||||
[Export] protected float NumberOfProjectiles = 1;
|
||||
|
||||
[Export] protected PackedScene[] ProjectileScenes { get; set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// <para>Whether to launch in the order of the projectile list</para>
|
||||
/// <para>是否按照抛射体列表的循序发射</para>
|
||||
/// </summary>
|
||||
[Export]
|
||||
protected bool Sequentially { get; set; }
|
||||
|
||||
private int _projectileIndex;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
_marker2D = GetNode<Marker2D>("Marker2D");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>GetNextProjectileScene</para>
|
||||
/// <para>获取下一个抛射体</para>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private PackedScene GetNextProjectileScene()
|
||||
{
|
||||
if (Sequentially)
|
||||
{
|
||||
_projectileIndex = (_projectileIndex + 1) % ProjectileScenes.Length;
|
||||
return ProjectileScenes[_projectileIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
return ProjectileScenes[RandomUtils.Instance.Next(ProjectileScenes.Length)];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>GetRandomAngle</para>
|
||||
/// <para>获取随机的偏移弧度</para>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private float GetRandomAngle()
|
||||
{
|
||||
if (OffsetAngle == 0)
|
||||
{
|
||||
//If the offset angle is 0, then return 0
|
||||
//弧度为0,不用偏移。
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (OffsetAngleMode == Config.OffsetAngleMode.Cross)
|
||||
{
|
||||
float result;
|
||||
if (_positiveOffsetAngle)
|
||||
{
|
||||
result = -OffsetAngle / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = OffsetAngle / 2;
|
||||
}
|
||||
|
||||
_positiveOffsetAngle = !_positiveOffsetAngle;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (OffsetAngleMode == Config.OffsetAngleMode.AlwaysSame)
|
||||
{
|
||||
return OffsetAngle;
|
||||
}
|
||||
|
||||
var min = -OffsetAngle / 2;
|
||||
return min + RandomUtils.Instance.NextSingle() * OffsetAngle;
|
||||
}
|
||||
|
||||
|
||||
protected override void DoFire(Node2D? owner, Vector2 enemyGlobalPosition)
|
||||
{
|
||||
|
@ -44,11 +136,12 @@ public partial class ProjectileWeapon : WeaponTemplate
|
|||
return;
|
||||
}
|
||||
|
||||
if (GameSceneNodeHolder.ProjectileContainer == null)
|
||||
if (GameSceneDepend.ProjectileContainer == null)
|
||||
{
|
||||
LogCat.LogError("projectile_container_is_null");
|
||||
return;
|
||||
}
|
||||
|
||||
//Empty list check
|
||||
//空列表检查
|
||||
if (ProjectileScenes is [])
|
||||
|
@ -59,13 +152,28 @@ public partial class ProjectileWeapon : WeaponTemplate
|
|||
|
||||
//Get the first projectile
|
||||
//获取第一个抛射体
|
||||
var projectileScene = ProjectileScenes[0];
|
||||
// var projectileScene = _projectileCache[_projectiles[0]];
|
||||
var projectile = NodeUtils.InstantiatePackedScene<ProjectileTemplate>(projectileScene);
|
||||
var projectileScene = GetNextProjectileScene();
|
||||
for (int i = 0; i < NumberOfProjectiles; i++)
|
||||
{
|
||||
var projectile = NodeUtils.InstantiatePackedScene<Projectile>(projectileScene);
|
||||
if (projectile == null) return;
|
||||
NodeUtils.CallDeferredAddChild(GameSceneNodeHolder.ProjectileContainer, projectile);
|
||||
if (Config.IsDebug())
|
||||
{
|
||||
var nodeSpawnOnKillCharacterDecorator = new NodeSpawnOnKillCharacterDecorator
|
||||
{
|
||||
DefaultParentNode = this,
|
||||
PackedScenePath = "res://prefab/entitys/BlackenedAboriginalWarrior.tscn"
|
||||
};
|
||||
projectile.AddProjectileDecorator(nodeSpawnOnKillCharacterDecorator);
|
||||
}
|
||||
|
||||
NodeUtils.CallDeferredAddChild(GameSceneDepend.ProjectileContainer, projectile);
|
||||
projectile.Owner = owner;
|
||||
projectile.Velocity = (enemyGlobalPosition - _marker2D.GlobalPosition).Normalized() * projectile.Speed;
|
||||
projectile.TargetNode = GameSceneDepend.TemporaryTargetNode;
|
||||
projectile.Velocity =
|
||||
(_marker2D.GlobalPosition.DirectionTo(enemyGlobalPosition) * projectile.Speed)
|
||||
.Rotated(GetRandomAngle());
|
||||
projectile.Position = _marker2D.GlobalPosition;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,8 +38,11 @@ public abstract partial class WeaponTemplate : PickAbleTemplate
|
|||
/// <para>开火间隔</para>
|
||||
/// </summary>
|
||||
private TimeSpan _firingInterval;
|
||||
|
||||
private long _firingIntervalAsMillisecond = 100;
|
||||
[Export] protected long FiringIntervalAsMillisecond
|
||||
|
||||
[Export]
|
||||
protected long FiringIntervalAsMillisecond
|
||||
{
|
||||
get => _firingIntervalAsMillisecond;
|
||||
set
|
||||
|
@ -58,7 +61,7 @@ public abstract partial class WeaponTemplate : PickAbleTemplate
|
|||
///<para>When the weapon is fired, how much recoil is applied to the user, in units: the number of cells, and the X direction of the force is automatically inferred.</para>
|
||||
///<para>武器开火,要对使用者施加多大的后坐力,单位:格数,力的X方向是自动推断的。</para>
|
||||
/// </remarks>
|
||||
[Export] private Vector2 _recoil;
|
||||
[Export] private long _recoilStrength;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Discharge of the weapon</para>
|
||||
|
@ -88,19 +91,9 @@ public abstract partial class WeaponTemplate : PickAbleTemplate
|
|||
{
|
||||
//We check the recoil of the weapon before each firing.
|
||||
//我们在每次开火之前,检查武器的后坐力。
|
||||
if (_recoil != Vector2.Zero)
|
||||
if (_recoilStrength != 0)
|
||||
{
|
||||
var force = new Vector2();
|
||||
var forceX = Math.Abs(_recoil.X);
|
||||
if (Math.Abs(RotationDegrees) < 90)
|
||||
{
|
||||
//The weapon goes to the right, and we apply a recoil to the left
|
||||
//武器朝向右边我们向左施加后坐力
|
||||
forceX = -forceX;
|
||||
}
|
||||
|
||||
force.X = forceX * Config.CellSize;
|
||||
force.Y = _recoil.Y * Config.CellSize;
|
||||
var force = -characterTemplate.GlobalPosition.DirectionTo(enemyGlobalPosition) * _recoilStrength * Config.CellSize;
|
||||
characterTemplate.AddForce(force);
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
@ -2,16 +2,16 @@
|
|||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://e6670ykyq145"
|
||||
path="res://.godot/imported/staffOfTheUndead.png-9bf2297abccc48c9610807d69d632153.ctex"
|
||||
uid="uid://di0sw4rgd26y0"
|
||||
path="res://.godot/imported/PortableBackpacks.png-a6ce21006145ee0b58e687b1801240a6.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sprites/weapon/staffOfTheUndead.png"
|
||||
dest_files=["res://.godot/imported/staffOfTheUndead.png-9bf2297abccc48c9610807d69d632153.ctex"]
|
||||
source_file="res://sprites/PortableBackpacks.png"
|
||||
dest_files=["res://.godot/imported/PortableBackpacks.png-a6ce21006145ee0b58e687b1801240a6.ctex"]
|
||||
|
||||
[params]
|
||||
|
BIN
sprites/character/BlackenedAboriginalWarrior.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
34
sprites/character/BlackenedAboriginalWarrior.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c1yxhtiqrfvvp"
|
||||
path="res://.godot/imported/BlackenedAboriginalWarrior.png-483f0928238bc97273aa2fd838825f33.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sprites/character/BlackenedAboriginalWarrior.png"
|
||||
dest_files=["res://.godot/imported/BlackenedAboriginalWarrior.png-483f0928238bc97273aa2fd838825f33.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
|
BIN
sprites/light/White_100.png
Normal file
After Width: | Height: | Size: 117 B |
34
sprites/light/White_100.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://drw45jlmfo0su"
|
||||
path="res://.godot/imported/White_100.png-d319ba9988ade64ae599ad90841c52c7.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sprites/light/White_100.png"
|
||||
dest_files=["res://.godot/imported/White_100.png-d319ba9988ade64ae599ad90841c52c7.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
|
BIN
sprites/light/White_25.png
Normal file
After Width: | Height: | Size: 112 B |
|
@ -2,16 +2,16 @@
|
|||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dvx10dfjctn7t"
|
||||
path="res://.godot/imported/packsack.png-ba078a68a3754f332c41ffcad073a395.ctex"
|
||||
uid="uid://diqf0as7rn18v"
|
||||
path="res://.godot/imported/White_25.png-584d94cc8c53e39250e249a7f385cbb4.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sprites/packsack.png"
|
||||
dest_files=["res://.godot/imported/packsack.png-ba078a68a3754f332c41ffcad073a395.ctex"]
|
||||
source_file="res://sprites/light/White_25.png"
|
||||
dest_files=["res://.godot/imported/White_25.png-584d94cc8c53e39250e249a7f385cbb4.ctex"]
|
||||
|
||||
[params]
|
||||
|
BIN
sprites/ui/MiniMapBg.png
Normal file
After Width: | Height: | Size: 277 B |
34
sprites/ui/MiniMapBg.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c35bsle7thcnh"
|
||||
path="res://.godot/imported/MiniMapBg.png-d798deffd210ae6fbca7a397a2bc688e.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sprites/ui/MiniMapBg.png"
|
||||
dest_files=["res://.godot/imported/MiniMapBg.png-d798deffd210ae6fbca7a397a2bc688e.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
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
34
sprites/weapon/StaffNecromancy.png.import
Normal file
|
@ -0,0 +1,34 @@
|
|||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://wt50kx6bup51"
|
||||
path="res://.godot/imported/StaffNecromancy.png-82a6bb745d120d5cf5445c4e669e3c6d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sprites/weapon/StaffNecromancy.png"
|
||||
dest_files=["res://.godot/imported/StaffNecromancy.png-82a6bb745d120d5cf5445c4e669e3c6d.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
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
@ -3,15 +3,15 @@
|
|||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dg5vwprt66w4j"
|
||||
path="res://.godot/imported/staffOfTheUndead_icon.png-4ca5c1341e2dac55b2ef4a0fc05b6e6c.ctex"
|
||||
path="res://.godot/imported/StaffNecromancy_Icon.png-b792107cbd73ce862a81c86021ad2001.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://sprites/weapon/staffOfTheUndead_icon.png"
|
||||
dest_files=["res://.godot/imported/staffOfTheUndead_icon.png-4ca5c1341e2dac55b2ef4a0fc05b6e6c.ctex"]
|
||||
source_file="res://sprites/weapon/StaffNecromancy_Icon.png"
|
||||
dest_files=["res://.godot/imported/StaffNecromancy_Icon.png-b792107cbd73ce862a81c86021ad2001.ctex"]
|
||||
|
||||
[params]
|
||||
|
|
@ -6,14 +6,14 @@
|
|||
texture = ExtResource("1_4c2am")
|
||||
texture_region_size = Vector2i(32, 32)
|
||||
0:0/0 = 0
|
||||
0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
0:0/0/physics_layer_2/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
1:0/0 = 0
|
||||
1:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
1:1/0 = 0
|
||||
2:1/0 = 0
|
||||
2:1/0/physics_layer_0/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
2:1/0/physics_layer_2/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
2:2/0 = 0
|
||||
2:2/0/physics_layer_0/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
2:2/0/physics_layer_2/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
3:2/0 = 0
|
||||
3:4/0 = 0
|
||||
3:4/0/physics_layer_1/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, -6.75, 8.25, -6, 7, -2.25, 1, -2, -16, 16)
|
||||
|
@ -27,17 +27,18 @@ texture_region_size = Vector2i(32, 32)
|
|||
1:3/0 = 0
|
||||
1:3/0/physics_layer_0/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
1:2/0 = 0
|
||||
1:2/0/physics_layer_0/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
1:2/0/physics_layer_2/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
0:2/0 = 0
|
||||
0:2/0/physics_layer_0/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
0:2/0/physics_layer_2/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
0:1/0 = 0
|
||||
0:1/0/physics_layer_0/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
0:1/0/physics_layer_2/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
2:3/0 = 0
|
||||
2:3/0/physics_layer_0/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
3:1/0 = 0
|
||||
3:0/0 = 0
|
||||
2:0/0 = 0
|
||||
2:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
2:0/0/physics_layer_2/polygon_0/points = PackedVector2Array(-16, -16, 16, -16, 16, 16, -16, 16)
|
||||
4:0/0 = 0
|
||||
4:1/0 = 0
|
||||
5:0/0 = 0
|
||||
|
@ -86,4 +87,6 @@ physics_layer_0/collision_layer = 2
|
|||
physics_layer_0/collision_mask = 0
|
||||
physics_layer_1/collision_layer = 32
|
||||
physics_layer_1/collision_mask = 0
|
||||
physics_layer_2/collision_layer = 128
|
||||
physics_layer_2/collision_mask = 0
|
||||
sources/1 = SubResource("TileSetAtlasSource_abxy2")
|
||||
|
|