1
1
mirror of https://github.com/Rundll86/Dog-Lynx-And-HCN.git synced 2026-07-10 12:02:54 +08:00

Compare commits

..

32 Commits

Author SHA1 Message Date
fallingshrimp 7a964d1f64 fix(statemachine): 调整钻石初始库存数量为100
将实体的钻石初始库存从10修改为100,优化初始道具配置
2026-06-12 17:51:15 +08:00
fallingshrimp 43a8c5329e Merge branch 'master' of https://github.com/Rundll86/Dog-Lynx-And-HCN 2026-06-12 16:39:00 +08:00
fallingshrimp 9389794e5a 1 2026-06-12 16:38:44 +08:00
fallingshrimp 6dd9e5bc58 refactor(LaserSummoner): 修复资源ID命名不规范并更新引用
统一替换场景内的子资源随机ID为更易区分的命名,修正粒子材质对曲线、渐变纹理的引用指向
2026-06-12 16:37:44 +08:00
fallingshrimp 07f1334cc7 chore: 删除todo.md中未完成的框架开发任务
移除了过时的待办条目,清理项目文档
2026-05-13 22:49:24 +08:00
fallingshrimp 2a864ae607 fix: 调整两个角色的技能平衡参数
调整Lynx的冲刺倍率从2改为1.5,下调牧羊犬的技能冷却时间从1000ms到500ms,并更新其技能描述文本
2026-05-12 22:43:27 +08:00
fallingshrimp 2abe9ec8b0 chore: 调整多名角色数值与技能逻辑
1. 下调Lynx冲刺倍率至2,同步更新其卡片属性
2. 重写牧羊犬技能为伤害吸血,替换原格挡逻辑
3. 上调氰化氢击杀储能数值,优化其描述文本
4. 新增全局伤害触发信号,完善实体伤害上报逻辑
2026-05-12 22:41:47 +08:00
fallingshrimp d4b9999e44 feat(MuyangDog): add Tree weapon to the character's weapon store
add Tree weapon resource and attach it to MuyangDog's weapon slot 1
2026-05-12 22:28:25 +08:00
fallingshrimp 8a75adc580 fix(volcano bullet): adjust animation speed, cuttable time and hitbox
调整了火山子弹的动画播放速度、可切割时长,修改了动画轨道参数与碰撞箱位置旋转角度
2026-05-12 22:13:30 +08:00
fallingshrimp fb5488b10d fix(Volcano.gd): 硬编码动画播放速度替换原攻击速度计算
将原本根据攻击速度计算的动画缩放倍率改为固定0.75,简化子弹动画速度控制逻辑
2026-05-12 06:49:36 +08:00
fallingshrimp 4026caf0de refactor(bullet): 修复子弹切割时的速度缩放逻辑
1. 修正Volcano子弹动画速度计算改为直接修改animator.speed_scale
2. 调整Volcano子弹发射速度倍率从0.9倍改为原0.75倍
3. 优化Volcano子弹粒子发射速度从300改为150
4. 调整MuyangDog的fieldValues并添加点击重建属性
5. 修复Volcano动画轨迹的多余关键帧与modulate显示逻辑
2026-05-12 06:46:33 +08:00
fallingshrimp 68259981f4 feat: 完善火山子弹相关内容与木羊犬属性
- 为火山子弹添加动画轨迹粒子与攻击速度适配逻辑
- 调整木羊犬的字段数值
- 修复子弹切割速度的缩放逻辑
- 新增能量最大值的实体属性处理器
2026-05-11 22:55:39 +08:00
fallingshrimp 53d1be6749 refactor(Volcano bullet): 优化火山弹相关逻辑
1. 新增spawn方法并设置可切割属性
2. 调整命中时的冲量大小从500改为400
3. 为动画添加hitbox显隐切换轨道
2026-05-11 22:11:31 +08:00
fallingshrimp 97f52a5dda fix(EnergyBlock): 为不同攻击类型添加适当的延迟
在攻击动画结束后添加等待时间,确保动画播放完整后再执行后续动作
2026-05-10 23:04:16 +08:00
fallingshrimp b1e25afb02 fix(角色平衡): 调整多个角色的移动速度和攻击冷却时间
修改MTY、Dog、Cat和Hen角色的移动速度参数,降低部分角色的速度以平衡游戏性
调整Dog和Hen的攻击冷却时间范围,使战斗节奏更合理
简化Hen的攻击逻辑,移除随机子弹数量生成
2026-05-10 23:00:47 +08:00
fallingshrimp d22a86b7c8 refactor(Characters): 移除HCN角色中的BigLaser武器
BigLaser武器在当前角色中未被使用,移除以减少冗余资源
2026-05-10 22:59:06 +08:00
fallingshrimp bae3a21cf9 fix(Bullets/Volcano): 调整火山子弹动画轨迹参数
修改火山子弹动画轨迹中的关键点参数,使动画效果更流畅
2026-05-10 22:53:33 +08:00
fallingshrimp f5f63ee602 fix(Statemachine): 在子弹过滤中添加实例有效性检查
确保在过滤子弹时检查实例是否有效,避免因无效实例导致的错误
2026-05-10 22:51:03 +08:00
fallingshrimp 2ebcdbee25 fix(Bullets): 调整火山子弹的动画参数和碰撞体尺寸
修改火山子弹的动画轨迹点和碰撞体尺寸,以改善视觉效果和命中判定
2026-05-10 22:50:28 +08:00
fallingshrimp 8bc6c11611 fix(Bullets/Volcano): 调整火山子弹的轨迹和碰撞位置
修改火山子弹的动画轨迹和碰撞框位置,使其更符合预期效果
2026-05-10 22:32:03 +08:00
fallingshrimp c52d293718 refactor(Bullets): 重构格挡子弹逻辑并提取特效创建方法
将ParrierBullet中的特效创建逻辑提取到ParryEffect和ParryEntityEffect类中
修改Volcano子弹的碰撞形状为矩形并更新相关特效调用
调整MuyangDog角色的默认武器为Volcano
统一多个子弹脚本中的函数调用空格格式
2026-05-10 22:30:22 +08:00
fallingshrimp 0fe8daabb2 refactor(命名): 将"饲料"统一重命名为"增益"
修改多处代码和文档中的"饲料"为"增益",包括UI显示、类别存储、实体属性和游戏详情说明,保持术语一致性
2026-05-10 22:13:30 +08:00
fallingshrimp a05a288612 feat(武器): 为齿轮武器添加升华选项和增强效果
- 在Cogwheel.gd中添加5个升华选项,提供不同属性增强
- 修改齿轮子弹逻辑,支持旋转加速、重力吸附等新效果
- 调整MuyangDog的初始武器配置,将齿轮作为主武器
- 修复箭头武器的生命偷取值归零
- 初始化钻石库存为10个
2026-05-10 16:34:45 +08:00
fallingshrimp dd586e89c7 feat(武器系统): 为链式枪和紫水晶子弹添加能量循环机制
- 在紫水晶子弹类中添加能量循环相关变量和方法
- 修改链式枪子弹生成逻辑以传递能量循环属性
- 为链式枪武器添加5个新的升华选项,包括能量回收功能
- 更新攻击方法以处理新增的穿透和能量循环属性
2026-05-10 15:47:31 +08:00
fallingshrimp 31d344e50a feat(武器): 为弓武器添加生命汲取和升华选项
添加生命汲取效果,使箭矢命中时回复生命值
实现弓武器的升华系统,包含5种不同品质的强化选项
调整攻击逻辑以支持升华效果和生命值相关的伤害加成
2026-05-10 15:31:40 +08:00
fallingshrimp 848a878992 feat(蓝水晶): 添加蓝水晶子弹追踪和分裂功能
为蓝水晶子弹添加追踪开关和速度提升属性
实现命中敌人时概率分裂钻石的功能
添加武器升华选项以增强蓝水晶能力
2026-05-10 15:17:57 +08:00
fallingshrimp af5d4437df fix(Weapons/BigLaser): 修复executeAtk从硬编码值改为读取配置
将BigLaser武器的executeAtk属性从硬编码值1改为从readStoreExtra读取配置,使其能够根据实际配置调整攻击执行参数
2026-05-10 15:04:17 +08:00
fallingshrimp ba32ccdbba fix(BigLaser): 修复跳帧导致伤害检测失败的问题
在applyDot函数中添加等待一帧的逻辑,确保伤害检测不会因跳帧而失效
2026-05-10 15:02:37 +08:00
fallingshrimp dfb5f8c926 feat(武器): 为HCN角色添加BigLaser武器并调整伤害计算
- 在HCN角色中新增BigLaser武器
- 修改BigLaser的executeAtk为固定值1
- 调整bulletHit和calculateDamage方法,添加entity参数
2026-05-10 15:00:10 +08:00
fallingshrimp f7861a26c4 refactor: 修改子弹实例添加到场景的方法调用方式
将直接调用call_deferred改为通过add_child属性调用,保持代码风格一致
2026-05-10 14:58:31 +08:00
fallingshrimp 6b7801e1ce feat(武器系统): 为BigLaser武器添加执行伤害和升华选项
- 在ObstacleBase和EntityBase中添加getHealthPercent方法用于获取生命值百分比
- 修改bulletHit方法支持伤害覆盖参数
- 为BigLaser武器添加5个升华选项,包括临界斩杀效果
- 实现damageOverride方法根据目标生命值动态调整伤害
- 修复store数值可能为负数的问题
2026-05-10 14:58:05 +08:00
fallingshrimp a0372f0867 refactor: 优化参数命名并调整角色属性
- 将匿名函数参数从 `_who`, `_by` 简化为 `_w`, `_b`
- 更新 HCN 角色的字段和数值配置
- 调整 MuyangDog 角色的数值和描述文本
2026-05-10 14:36:30 +08:00
46 changed files with 641 additions and 150 deletions
+14 -14
View File
@@ -19,42 +19,42 @@ shader_parameter/wave_width = 0.5
shader_parameter/edge_nonalpha = 0.6
shader_parameter/alpha = 1.0
[sub_resource type="Curve" id="Curve_itlbo"]
[sub_resource type="Curve" id="Curve_5qhrw"]
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="CurveTexture" id="CurveTexture_xathl"]
curve = SubResource("Curve_itlbo")
[sub_resource type="CurveTexture" id="CurveTexture_e7vrf"]
curve = SubResource("Curve_5qhrw")
[sub_resource type="Curve" id="Curve_75ctq"]
[sub_resource type="Curve" id="Curve_ws1jd"]
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="CurveTexture" id="CurveTexture_my7uy"]
curve = SubResource("Curve_75ctq")
[sub_resource type="CurveTexture" id="CurveTexture_ypwek"]
curve = SubResource("Curve_ws1jd")
[sub_resource type="Gradient" id="Gradient_62xhh"]
[sub_resource type="Gradient" id="Gradient_3diwe"]
offsets = PackedFloat32Array(0, 0.60687)
colors = PackedColorArray(1, 1, 1, 1, 1, 0, 0, 1)
[sub_resource type="GradientTexture1D" id="GradientTexture1D_yix5j"]
gradient = SubResource("Gradient_62xhh")
[sub_resource type="GradientTexture1D" id="GradientTexture1D_tn8hw"]
gradient = SubResource("Gradient_3diwe")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_m7jvq"]
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_nu1m4"]
particle_flag_disable_z = true
emission_shape = 3
emission_box_extents = Vector3(0, 1000, 1)
angle_min = 1.07288e-05
angle_max = 360.0
angle_curve = SubResource("CurveTexture_my7uy")
angle_curve = SubResource("CurveTexture_ypwek")
spread = 15.0
initial_velocity_min = -500.0
initial_velocity_max = 500.0
gravity = Vector3(0, 0, 0)
scale_min = 3.0
scale_max = 15.0
color_ramp = SubResource("GradientTexture1D_yix5j")
alpha_curve = SubResource("CurveTexture_xathl")
color_ramp = SubResource("GradientTexture1D_tn8hw")
alpha_curve = SubResource("CurveTexture_e7vrf")
[sub_resource type="Animation" id="Animation_1rif1"]
length = 0.001
@@ -230,7 +230,7 @@ position = Vector2(50, 1000)
amount = 400
lifetime = 0.25
visibility_rect = Rect2(-100, -1000, 200, 2000)
process_material = SubResource("ParticleProcessMaterial_m7jvq")
process_material = SubResource("ParticleProcessMaterial_nu1m4")
[node name="launcher" type="Polygon2D" parent="texture/rect" index="1" unique_id=1254071384]
position = Vector2(50, -21)
+198 -9
View File
@@ -67,6 +67,66 @@ tracks/4/keys = {
"update": 1,
"values": [0]
}
tracks/5/type = "value"
tracks/5/imported = false
tracks/5/enabled = true
tracks/5/path = NodePath("%hitbox:disabled")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/6/type = "value"
tracks/6/imported = false
tracks/6/enabled = true
tracks/6/path = NodePath("anchor/textureSword/trailRight:visible")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
tracks/7/type = "value"
tracks/7/imported = false
tracks/7/enabled = true
tracks/7/path = NodePath("anchor/textureSword/trailLine:visible")
tracks/7/interp = 1
tracks/7/loop_wrap = true
tracks/7/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
tracks/8/type = "value"
tracks/8/imported = false
tracks/8/enabled = true
tracks/8/path = NodePath("%textureSword/trailLeft:visible")
tracks/8/interp = 1
tracks/8/loop_wrap = true
tracks/8/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
tracks/9/type = "value"
tracks/9/imported = false
tracks/9/enabled = true
tracks/9/path = NodePath("%textureSword/trailLine:modulate")
tracks/9/interp = 1
tracks/9/loop_wrap = true
tracks/9/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 1)]
}
[sub_resource type="Animation" id="Animation_oinqg"]
resource_name = "destroy"
@@ -85,7 +145,7 @@ tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"handle_modes": PackedInt32Array(0, 0, 2),
"points": PackedFloat32Array(150, -0.25, 0, 0.25, 0, 500, -0.083333336, -8.333334, 0, 0, 300, -0.01666667, 83.333336, 0, 0),
"points": PackedFloat32Array(200, 0, 0, 0.1, 0, 500, -0.2, 0, 0.02, 0, 200, -0.05, -100, 0, 0),
"times": PackedFloat32Array(0, 0.2, 0.3)
}
tracks/1/type = "bezier"
@@ -96,8 +156,8 @@ tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"handle_modes": PackedInt32Array(0, 0, 0, 0, 0, 0),
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0, 0.7853982, -0.06585455, -0.14365458, 0.2, 0, -0.7853982, -0.06666666, 0.2617994, 0, 0, -0.7853982, -0.033333343, 0, 0, 0, 3.142, -0.041666668, 0.91629785, 0, 0),
"times": PackedFloat32Array(0, 0.3, 0.6, 0.888264, 1.2, 1.45)
"points": PackedFloat32Array(0, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.1, 0, 0.7853982, -0.1, 0, 0.4, 0, -0.7853982, 0, 0.3, 0, -2, -0.7853982, -0.1, 0, 0.2, 0, 3.142, -0.2, 3, 0, 0),
"times": PackedFloat32Array(0, 0.2, 0.4, 0.888264, 1.2, 1.45)
}
tracks/2/type = "bezier"
tracks/2/imported = false
@@ -107,7 +167,7 @@ tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"handle_modes": PackedInt32Array(0, 0),
"points": PackedFloat32Array(1, 0, 0, 0.2, 0, -1, 0, 0, 0, 0),
"points": PackedFloat32Array(1, 0, 0, 0.2, 0, -2, 0, 0, 0, 0),
"times": PackedFloat32Array(1.45, 1.7)
}
tracks/3/type = "value"
@@ -175,6 +235,66 @@ tracks/6/keys = {
"update": 1,
"values": [6, 0, 6]
}
tracks/7/type = "value"
tracks/7/imported = false
tracks/7/enabled = true
tracks/7/path = NodePath("%hitbox:disabled")
tracks/7/interp = 1
tracks/7/loop_wrap = true
tracks/7/keys = {
"times": PackedFloat32Array(0, 0.05, 0.2, 0.7, 0.9, 1.2, 1.35, 1.65, 1.8),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1),
"update": 1,
"values": [true, false, true, false, true, false, true, false, true]
}
tracks/8/type = "value"
tracks/8/imported = false
tracks/8/enabled = true
tracks/8/path = NodePath("anchor/textureSword/trailRight:visible")
tracks/8/interp = 1
tracks/8/loop_wrap = true
tracks/8/keys = {
"times": PackedFloat32Array(0, 0.75, 0.95, 1.35),
"transitions": PackedFloat32Array(1, 1, 1, 1),
"update": 1,
"values": [false, true, false, false]
}
tracks/9/type = "value"
tracks/9/imported = false
tracks/9/enabled = true
tracks/9/path = NodePath("anchor/textureSword/trailLine:visible")
tracks/9/interp = 1
tracks/9/loop_wrap = true
tracks/9/keys = {
"times": PackedFloat32Array(0, 0.15, 0.2, 1.65),
"transitions": PackedFloat32Array(1, 1, 1, 1),
"update": 1,
"values": [false, true, false, true]
}
tracks/10/type = "value"
tracks/10/imported = false
tracks/10/enabled = true
tracks/10/path = NodePath("%textureSword/trailLeft:visible")
tracks/10/interp = 1
tracks/10/loop_wrap = true
tracks/10/keys = {
"times": PackedFloat32Array(0, 1.2, 1.35),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 1,
"values": [false, true, false]
}
tracks/11/type = "value"
tracks/11/imported = false
tracks/11/enabled = true
tracks/11/path = NodePath("%textureSword/trailLine:modulate")
tracks/11/interp = 1
tracks/11/loop_wrap = true
tracks/11/keys = {
"times": PackedFloat32Array(0, 1.65, 2),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_w1utg"]
_data = {
@@ -183,8 +303,53 @@ _data = {
&"spawn": SubResource("Animation_o5h0y")
}
[sub_resource type="CircleShape2D" id="CircleShape2D_1sgli"]
radius = 78.05767
[sub_resource type="Curve" id="Curve_1sgli"]
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="CurveTexture" id="CurveTexture_b5nxd"]
curve = SubResource("Curve_1sgli")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_xrrxo"]
particle_flag_disable_z = true
direction = Vector3(1, 1, 0)
spread = 0.0
initial_velocity_min = 100.0
initial_velocity_max = 100.0
gravity = Vector3(0, 0, 0)
tangential_accel_min = 99.99999
tangential_accel_max = 99.99999
alpha_curve = SubResource("CurveTexture_b5nxd")
[sub_resource type="Curve" id="Curve_b5nxd"]
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="CurveTexture" id="CurveTexture_xrrxo"]
curve = SubResource("Curve_b5nxd")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_b5pv2"]
particle_flag_disable_z = true
direction = Vector3(-1, -1, 0)
spread = 0.0
initial_velocity_min = 100.0
initial_velocity_max = 100.0
gravity = Vector3(0, 0, 0)
tangential_accel_min = -100.0
tangential_accel_max = -100.0
alpha_curve = SubResource("CurveTexture_xrrxo")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_1sgli"]
particle_flag_disable_z = true
direction = Vector3(-1, 1, 0)
spread = 0.0
initial_velocity_min = 150.0
initial_velocity_max = 150.0
gravity = Vector3(0, 0, 0)
alpha_curve = SubResource("CurveTexture_b5nxd")
[sub_resource type="RectangleShape2D" id="RectangleShape2D_1sgli"]
size = Vector2(23.999994, 153)
[node name="Volcano" unique_id=2046549391 instance=ExtResource("1_gd3m7")]
script = ExtResource("2_w1utg")
@@ -209,10 +374,34 @@ texture = ExtResource("2_wwxm2")
[node name="audio" type="AudioStreamPlayer2D" parent="texture/anchor/textureSword" index="0" unique_id=167399724]
[node name="trailRight" type="GPUParticles2D" parent="texture/anchor/textureSword" index="1" unique_id=1304800613]
z_index = -1
amount = 12
texture = ExtResource("2_wwxm2")
preprocess = 2.0
local_coords = true
process_material = SubResource("ParticleProcessMaterial_xrrxo")
[node name="trailLeft" type="GPUParticles2D" parent="texture/anchor/textureSword" index="2" unique_id=1450319379]
visible = false
z_index = -1
texture = ExtResource("2_wwxm2")
preprocess = 2.0
local_coords = true
process_material = SubResource("ParticleProcessMaterial_b5pv2")
[node name="trailLine" type="GPUParticles2D" parent="texture/anchor/textureSword" index="3" unique_id=946145693]
z_index = -1
amount = 12
texture = ExtResource("2_wwxm2")
preprocess = 2.0
local_coords = true
process_material = SubResource("ParticleProcessMaterial_1sgli")
[node name="hitbox" parent="." index="1" unique_id=175349408 node_paths=PackedStringArray("target")]
position = Vector2(299.99997, 0.1221919)
rotation = 0.00040731629
shape = SubResource("CircleShape2D_1sgli")
position = Vector2(399.99997, 0.16292253)
rotation = 0.32215786
shape = SubResource("RectangleShape2D_1sgli")
script = ExtResource("7_1sgli")
target = NodePath("../texture/anchor/textureSword")
offsetRotation = 45.0
+4 -5
View File
@@ -20,10 +20,9 @@ theme_override_styles/panel = SubResource("StyleBoxFlat_1yfsg")
displayName = "氰化氢"
slogan = "微量便可颠覆生机"
avatar = ExtResource("2_1yfsg")
description = "击杀敌人时储能。"
fields = Array[int]([18, 1])
fieldValues = Array[float]([3.0, -0.15])
clickToRebuild = true
description = "击杀敌人时少量储能。"
fields = Array[int]([18, 10, 1])
fieldValues = Array[float]([4.0, 0.2, -0.2])
[node name="avatarTexture" parent="wrapper" parent_id_path=PackedInt32Array(2023039659) index="0" unique_id=1334645594]
texture = ExtResource("2_1yfsg")
@@ -35,4 +34,4 @@ text = "氰化氢"
text = "“微量便可颠覆生机”"
[node name="descriptionLabel" parent="wrapper/infoContainer" parent_id_path=PackedInt32Array(143242635) index="1" unique_id=808054282]
text = "击杀敌人时储能。"
text = "击杀敌人时少量储能。"
+1 -1
View File
@@ -22,7 +22,7 @@ slogan = "栖于寒域密林,孤影独行世间"
avatar = ExtResource("2_1s675")
description = "拥有更大的冲刺速度。"
fields = Array[int]([3, 4, 10])
fieldValues = Array[float]([0.15, 0.04, -0.3])
fieldValues = Array[float]([0.2, 0.04, -0.3])
clickToRebuild = true
[node name="avatarTexture" parent="wrapper" parent_id_path=PackedInt32Array(2023039659) index="0" unique_id=1334645594]
+5 -3
View File
@@ -19,9 +19,10 @@ theme_override_styles/panel = SubResource("StyleBoxFlat_fi2nw")
displayName = "牧羊犬"
slogan = "恪守使命不离不弃"
avatar = ExtResource("2_fi2nw")
description = "每隔3秒发动格挡。"
description = "造成伤害时少量吸血,
每0.5秒最多触发一次。"
fields = Array[int]([0, 8, 3])
fieldValues = Array[float]([50.0, 0.15, -0.2])
fieldValues = Array[float]([35.0, 0.2, -0.5])
[node name="avatarTexture" parent="wrapper" parent_id_path=PackedInt32Array(2023039659) index="0" unique_id=1334645594]
texture = ExtResource("2_fi2nw")
@@ -33,4 +34,5 @@ text = "牧羊犬"
text = "“恪守使命不离不弃”"
[node name="descriptionLabel" parent="wrapper/infoContainer" parent_id_path=PackedInt32Array(143242635) index="1" unique_id=808054282]
text = "每3秒发动格挡。"
text = "造成伤害时少量吸血,
每0.5秒最多触发一次。"
+4 -1
View File
@@ -2,10 +2,13 @@
[ext_resource type="PackedScene" uid="uid://bs863g2s8r770" path="res://components/Abstracts/PlayerBase.tscn" id="1_y3l4w"]
[ext_resource type="Script" uid="uid://bbmb572iba42l" path="res://scripts/Contents/Characters/MuyangDog.gd" id="2_mr6nm"]
[ext_resource type="PackedScene" uid="uid://u0djqwuuysp8" path="res://components/Weapons/Volcano.tscn" id="3_e236u"]
[ext_resource type="PackedScene" uid="uid://cx7nogfnv7s8t" path="res://components/Weapons/Tree.tscn" id="4_im5m5"]
[node name="MuyangDog" unique_id=1711205167 instance=ExtResource("1_y3l4w")]
script = ExtResource("2_mr6nm")
displayName = "牧羊犬"
[node name="Tree" parent="weaponStore" parent_id_path=PackedInt32Array(1319091445) index="0" unique_id=185228402 instance=ExtResource("4_im5m5")]
[node name="Volcano" parent="weaponStore" parent_id_path=PackedInt32Array(1319091445) index="0" unique_id=204992396 instance=ExtResource("3_e236u")]
[node name="Tree" parent="weaponStore" parent_id_path=PackedInt32Array(1319091445) index="1" unique_id=185228402 instance=ExtResource("4_im5m5")]
+1 -1
View File
@@ -48,7 +48,7 @@ unique_name_in_owner = true
layout_mode = 2
theme = ExtResource("2_k71he")
bbcode_enabled = true
text = "[b]未命名饲料[/b]"
text = "[b]未命名[/b]"
fit_content = true
autowrap_mode = 0
+1 -1
View File
@@ -44,7 +44,7 @@
- **ENERGY_MULTIPILER(储能倍率)**:不管以何种方式获得能量,都将乘以此倍率。
- **PENARATION_RESISTANCE(穿透抗性)**:敌人发射的可穿透射弹在击中自己后降低穿透率。
- **EXTRA_BULLET_COUNT(多重射击)**:发射射弹时有概率多生成一些。
- **LUCK_VALUE(幸运值)**:影响很多概率行为,如暴击、掉落苹果、出现高品质饲料
- **LUCK_VALUE(幸运值)**:影响很多概率行为,如暴击、掉落苹果、出现高品质增益
- **ENERGY_REGENERATION(能量再生效率)**:自身缓慢恢复能量的速度。
- **BULLET_SPLIT(分裂)**:射弹命中后有概率分裂成多颗。
- **BULLET_REFRACTION(折射)**:射弹命中后有概率生成面向最近的敌人的同类射弹。
+3 -1
View File
@@ -6,6 +6,7 @@ class_name Arrow
var atk: float = 0
var waitTime: float = 0
var offsetRotation: float = 0
var lifesteal: float = 0
func register():
trail.emitting = false
@@ -23,6 +24,7 @@ func ai():
PresetBulletAI.forward(self , rotation)
if speed < 1:
tryDestroy()
func succeedToHit(_dmg: float, _entity: EntityBase):
func succeedToHit(_dmg: float, entity: EntityBase):
var effect = EffectController.create(ComponentManager.getEffect("ShootBlood"), position)
effect.shot()
entity.heal(lifesteal)
+9 -1
View File
@@ -2,18 +2,26 @@ extends BulletBase
class_name BigLaser
var dotTime: float = 100
var executeAtk: float = 0.25
func spawn():
CameraManager.shake(5000, 100) # 激光会运行5秒(5000毫秒),期间震屏超高强度
CameraManager.playAnimation("bigLaser")
baseDamage *= launcher.fields[FieldStore.Entity.ATTACK_SPEED]
func ai():
rotation = lerp_angle(rotation, ((launcher.currentFocusedPosition - position).angle()), 0.1)
position = launcher.texture.global_position
func applyDot():
hitbox.disabled = true
await TickTool.millseconds(dotTime / launcher.fields[FieldStore.Entity.ATTACK_SPEED])
await TickTool.frame() # 等至少一帧,防止跳帧导致没检测到伤害
hitbox.disabled = false
await TickTool.millseconds(dotTime / launcher.fields[FieldStore.Entity.ATTACK_SPEED])
await TickTool.frame() # 等至少一帧,防止跳帧导致没检测到伤害
return true
func damageOverride(origin: float, something: Variant):
if something is EntityBase:
return origin * (1 + (executeAtk if something.getHealthPercent() < 0.3 else 0.0))
elif something is ObstacleBase:
return origin * (1 + (executeAtk if something.getHealthPercent() < 0.3 else 0.0))
else:
return origin
+17 -3
View File
@@ -2,6 +2,10 @@ extends BulletBase
class_name BlueCrystalBullet
var tracer: EntityBase = null
var canTrace: bool = true
var speedBoost: float = 0
var splitRate: float = 0
@onready var trail: GPUParticles2D = $%trail
func ai():
@@ -9,12 +13,22 @@ func ai():
var tracker = tracer.getTrackingAnchor()
var targetAngle = position.angle_to_point(tracker)
trail.rotation = - Vector2.from_angle(rotation).angle_to(Vector2.from_angle(targetAngle)) / (speed / initialSpeed)
PresetBulletAI.trace(self, tracker, 0.07)
if canTrace:
PresetBulletAI.trace(self , tracker, 0.07)
else:
trail.rotation = 0
speed += 0.1
PresetBulletAI.forward(self, rotation)
speed += 0.1 * (1 + speedBoost)
PresetBulletAI.forward(self , rotation)
func destroy(_beacuseMap: bool):
var eff = EffectController.create(ComponentManager.getEffect("BlueCrystalExplosion"), global_position)
eff.rotation = rotation
eff.shot()
func succeedToHit(_dmg: float, entity: EntityBase):
if entity == tracer:
canTrace = false
if MathTool.rate(splitRate):
for diamond in BulletBase.generate(ComponentManager.getBullet("Diamond2"), entity, position, deg_to_rad(randf_range(0, 360))):
if diamond is Diamond2Bullet:
diamond.baseDamage = baseDamage * 0.8
diamond.parent = self
diamond.penerate += penerate
+7 -5
View File
@@ -4,20 +4,22 @@ class_name Bow
var count: int = 0
var atk: float = 0
var waitTime: float = 2000
var lifesteal: float = 0.2
func spawn():
var startAngle = rotation - deg_to_rad(count * 10.0 / 2)
for c in count:
for i in BulletBase.generate(
for bullet in BulletBase.generate(
ComponentManager.getBullet("Arrow"),
launcher,
position,
startAngle
):
var bullet: Arrow = i
bullet.atk = atk
bullet.waitTime = waitTime
bullet.offsetRotation = deg_to_rad(c * 10.0)
if bullet is Arrow:
bullet.atk = atk
bullet.waitTime = waitTime
bullet.offsetRotation = deg_to_rad(c * 10.0)
bullet.lifesteal = lifesteal
await TickTool.millseconds(waitTime)
tryDestroy()
func ai():
+13 -7
View File
@@ -1,21 +1,27 @@
extends BulletBase
class_name ChainGunBullet
var count: int = 0
var splits: float = 0
var cycleRate: float = 0
var cycleCount: float = 0
@onready var anchor: Node2D = $"%anchor"
func spawn():
for j in count:
for i in BulletBase.generate(
for i in count:
for bullet in BulletBase.generate(
ComponentManager.getBullet("PurpleCrystalSmall"),
launcher,
anchor.global_position,
rotation + deg_to_rad(splits * randf_range(-1, 1))
):
if i is BulletBase:
var dir = Vector2.from_angle(i.rotation).rotated(deg_to_rad(-90))
i.baseDamage = baseDamage
i.position += dir * (count - j * 2) * 10 / 2
if bullet is PurpleCrystalBullet:
var dir = Vector2.from_angle(bullet.rotation).rotated(deg_to_rad(-90))
bullet.baseDamage = baseDamage
bullet.position += dir * (count - i * 2) * 10 / 2
bullet.penerate += penerate
bullet.cycleRate = cycleRate
bullet.cycleCount = cycleCount
func ai():
PresetBulletAI.lockLauncher(self, launcher, true)
PresetBulletAI.lockLauncher(self , launcher, true)
+14 -5
View File
@@ -5,23 +5,29 @@ var initialRotate: float = 0
var rotateSpeed: float = 0
var dotTime: float = 0
var slow: float = 0.2
var rotateExtra: float = 0
var atkSpeeddown: float = 0
var gravityForce: float = 0
func spawn():
slow *= initialRotate / 15.0
func ai():
PresetBulletAI.forward(self, rotation)
PresetBulletAI.forward(self , rotation)
texture.rotation_degrees += rotateSpeed
if rotateSpeed < 0:
speed += slow
PresetBulletAI.trace(self, launcher.position, 0.1)
PresetBulletAI.trace(self , launcher.position, 0.1)
if position.distance_to(launcher.position) < 100:
tryDestroy()
else:
speed = initialSpeed * (rotateSpeed / initialRotate)
if rotateSpeed >= 0 and rotateSpeed / slow <= 0:
slow = 0.5
dotTime = 1000 / (rotateSpeed)
rotateSpeed -= slow
slow = 0.4
dotTime = 1000 / rotateSpeed
if rotateSpeed < 0:
rotateSpeed -= slow * (1 + rotateExtra)
else:
rotateSpeed -= slow
func applyDot():
hitbox.disabled = true
await TickTool.frame()
@@ -29,6 +35,9 @@ func applyDot():
await TickTool.millseconds(dotTime / launcher.fields[FieldStore.Entity.ATTACK_SPEED])
await TickTool.frame() # 等至少一帧,防止跳帧导致没检测到伤害
return true
func succeedToHit(_dmg: float, entity: EntityBase):
entity.impluse((position - entity.getTrackingAnchor()).normalized() * gravityForce)
entity.fields[FieldStore.Entity.ATTACK_SPEED] *= 0.98 ** atkSpeeddown
func refract(newBullet: BulletBase, _entity: EntityBase, _index: int, _total: int, _lastBullet: float):
if newBullet is CogwheelBullet:
+2 -2
View File
@@ -10,14 +10,14 @@ var delta = 0.01
func spawn():
texture.play(str(randi_range(0, 2)))
func ai():
PresetBulletAI.forward(self, rotation)
PresetBulletAI.forward(self , rotation)
func succeedToHit(_dmg: float, entity: EntityBase):
if entity.isBoss:
launcher.fields[FieldStore.Entity.DAMAGE_MULTIPILER] *= 1 + delta
addTimes += 1
if is_instance_valid(lastHit):
if lastHit.get_class() == entity.get_class():
entity.bulletHit(self, true)
entity.bulletHit(self , true)
lastHit = entity
if bouncedTime < maxBouncedTime:
var newEntity = EntityTool.findClosetEntity(position, get_tree(), !launcher.isPlayer(), launcher.isPlayer(), [entity])
+10 -23
View File
@@ -9,19 +9,6 @@ var maxBallCount: int = 5
var atk: float = 1
var reflectRate: float = 1
func parryEffect(bullet: BulletBase):
parryiedTimes += 1
var eff = EffectController.create(ComponentManager.getEffect("Parry"), position + (bullet.position - position).normalized() * 200) # 从子弹位置,面向其他子弹的方向前进150
eff.modulate = bullet.modulate.blend(bullet.texture.modulate)
eff.rotation = position.angle_to_point(bullet.position)
eff.shot()
launcher.impluse((position - bullet.position).normalized() * sqrt(abs(bullet.speed)) * 250)
func penerateEffect(entity: EntityBase, effPos: Vector2):
parryiedTimes += 1
var eff = EffectController.create(ComponentManager.getEffect("ParryEntity"), effPos)
eff.rotation = entity.position.angle_to_point(effPos)
eff.shot()
entity.impluse((entity.position - effPos).normalized() * 450)
func generateParryBall(bDamage: float):
var cycler = launcher.getOrCreateCycleTimer("parry", 2000, 100)
if len(cycler.bullets) < maxBallCount: # 玩家最多只能拥有多少气
@@ -47,7 +34,7 @@ func spawn():
eff.shot()
func succeedToHit(_dmg: float, entity: EntityBase):
if parryiedTimes < maxParryTimes && MathTool.rate(parryRate):
penerateEffect(entity, entity.position * 1.01)
ParryEntityEffect.createEffectEntity(entity, self )
func hitBullet(bullet: BulletBase): # 当前子弹与其他子弹相撞
if !is_instance_valid(launcher): return
if BulletTool.canDamage(bullet, launcher): # 其他子弹可以使当前子弹的发射者受伤吗?
@@ -57,8 +44,8 @@ func hitBullet(bullet: BulletBase): # 当前子弹与其他子弹相撞
# 魔法运动和召唤运动的子弹虽不能格挡,但是可以储能,吐息运动的子弹会对发射者产生击退
if bullet.motionType == BulletBase.MotionType.PROJECTILE:
# 无论如何都要生成格挡特效
parryEffect(bullet)
# 弹反 还是 格挡?
ParryEffect.createEffect(bullet, self )
# 弹反 还是 格挡?
if MathTool.rate(reflectRate):
bullet.look_at(bullet.launcher.getTrackingAnchor())
bullet.launcher = launcher
@@ -70,33 +57,33 @@ func hitBullet(bullet: BulletBase): # 当前子弹与其他子弹相撞
bullet.tryDestroy()
generateParryBall(bullet.baseDamage)
elif bullet.motionType == BulletBase.MotionType.SWING:
parryEffect(bullet)
ParryEffect.createEffect(bullet, self )
bullet.hitbox.set_deferred("disabled", true)
generateParryBall(bullet.baseDamage)
InternalDamageBullet.generateBall(launcher, bullet.launcher, atk)
elif bullet.motionType == BulletBase.MotionType.STAB:
parryEffect(bullet)
penerateEffect(bullet.launcher, bullet.launcher.position * 1.01)
ParryEffect.createEffect(bullet, self )
ParryEntityEffect.createEffectEntity(bullet.launcher, self )
bullet.hitbox.set_deferred("disabled", true)
bullet.launcher.takeDamage(atk * reflectRate * bullet.getDamage())
bullet.launcher.impluse(Vector2.from_angle(bullet.rotation) * -2000)
generateParryBall(bullet.baseDamage)
InternalDamageBullet.generateBall(launcher, bullet.launcher, atk)
elif bullet.motionType == BulletBase.MotionType.SPRINT:
parryEffect(bullet)
ParryEffect.createEffect(bullet, self )
bullet.tryDestroy()
bullet.launcher.velocity *= 0
generateParryBall(bullet.baseDamage)
for i in 2:
InternalDamageBullet.generateBall(launcher, bullet.launcher, atk)
elif bullet.motionType == BulletBase.MotionType.BREATH:
penerateEffect(bullet.launcher, launcher.position)
ParryEntityEffect.createEffectEntity(bullet.launcher, self )
bullet.launcher.impluse(Vector2.from_angle(bullet.rotation) * -500)
elif bullet.motionType == BulletBase.MotionType.SUMMON || bullet.motionType == BulletBase.MotionType.MAGIC:
penerateEffect(bullet.launcher, launcher.position)
ParryEntityEffect.createEffectEntity(bullet.launcher, self )
launcher.storeEnergy(sqrt(bullet.baseDamage))
elif bullet.motionType == BulletBase.MotionType.EXPLOSION:
penerateEffect(bullet.launcher, launcher.position)
ParryEntityEffect.createEffectEntity(bullet.launcher, self )
for i in 2:
InternalDamageBullet.generateBall(bullet.launcher, launcher, sqrt(bullet.getDamage()))
else:
+1 -1
View File
@@ -4,7 +4,7 @@ class_name PipeBullet
var energy: float = 0
func ai():
PresetBulletAI.forward(self, rotation)
PresetBulletAI.forward(self , rotation)
texture.rotation += initialSpeed * (1 - lifeTimePercent()) / 100
speed = initialSpeed * (1 - lifeTimePercent())
baseDamage = energy
+10 -1
View File
@@ -1,8 +1,17 @@
extends BulletBase
class_name PurpleCrystalBullet
var cycleRate: float = 0
var cycleCount: float = 0
var cycled: bool = false
func ai():
PresetBulletAI.forward(self, rotation)
PresetBulletAI.forward(self , rotation)
func destroy(_beacuseMap: bool):
var eff = EffectController.create(ComponentManager.getEffect("PurpleCrystalExplosion"), global_position)
eff.rotation = rotation
eff.shot()
func succeedToHit(_dmg: float, entity: EntityBase):
if MathTool.rate(cycleRate):
entity.storeEnergy(cycleCount)
cycled = true
+1 -1
View File
@@ -2,7 +2,7 @@ extends BulletBase
class_name RainbowCat
func ai():
PresetBulletAI.forward(self, rotation)
PresetBulletAI.forward(self , rotation)
func succeedToHit(_dmg: float, _entity: EntityBase):
EffectController.create(ComponentManager.getEffect("CatBoom"), position).shot()
rotation_degrees = randf_range(0, 360)
+2 -2
View File
@@ -8,12 +8,12 @@ var count: int = 0
func register():
hitbox.shape = hitbox.shape.duplicate()
func ai():
PresetBulletAI.forward(self, rotation)
PresetBulletAI.forward(self , rotation)
speed = (1 - lifeTimePercent()) * initialSpeed
func destroy(_beacuseMap: bool):
hitbox.shape.radius = radius
EffectController.create(ComponentManager.getEffect("RedCrystalExplosion"), global_position).shot()
if !is_instance_valid(launcher):return
if !is_instance_valid(launcher): return
for i in randi_range(1, count):
for bullet in BulletBase.generate(ComponentManager.getBullet("CrystalBlock"), launcher, position, deg_to_rad(randf_range(0, 360))):
if bullet is CrystalBlockBullet:
+6 -2
View File
@@ -9,6 +9,9 @@ var count: int = 0
var dmg5: float = 0
var splitAngle: float = 10
func spawn():
animator.speed_scale = 0.85
setupCuttable(0.75)
func ai():
PresetBulletAI.lockLauncher(self , launcher, true)
rotation = lerp_angle(
@@ -16,8 +19,9 @@ func ai():
position.angle_to_point(launcher.currentFocusedPosition),
rotates
)
func succeedToHit(_dmg: float, _entity: EntityBase):
EffectController.create(ComponentManager.getEffect("SwordCut"), textureSword.global_position).shot()
func succeedToHit(_dmg: float, entity: EntityBase):
entity.impluse((entity.getTrackingAnchor() - position).normalized() * 400)
ParryEntityEffect.createEffectEntity(entity, self )
func generateShadow():
var startAngle = rotation - deg_to_rad(count * splitAngle / 2)
+3 -3
View File
@@ -3,17 +3,17 @@ class_name Maodie
func register():
fields[FieldStore.Entity.MAX_HEALTH] = 75
fields[FieldStore.Entity.MOVEMENT_SPEED] = randf_range(0.5, 0.8)
fields[FieldStore.Entity.MOVEMENT_SPEED] = randf_range(0.4, 0.65)
attackCooldownMap[0] = randi_range(5000, 8000)
sprintMultiplier = randf_range(5, 15)
func spawn():
texture.play("walk")
func ai():
PresetEntityAI.follow(self, currentFocusedBoss)
PresetEntityAI.follow(self , currentFocusedBoss)
tryAttack(0, true)
func attack(type: int):
if type == 0:
BulletBase.generate(ComponentManager.getBullet("ChickSprint"), self, position, 0)
BulletBase.generate(ComponentManager.getBullet("ChickSprint"), self , position, 0)
trySprint()
return true
func sprint():
+1 -1
View File
@@ -4,7 +4,7 @@ func register():
fields[FieldStore.Entity.MAX_HEALTH] = 120
fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.2
fields[FieldStore.Entity.OFFSET_SHOOT] = 0
attackCooldownMap[0] = randi_range(100, 500)
attackCooldownMap[0] = randi_range(1000, 3000)
func spawn():
texture.play("walk")
func ai():
@@ -27,6 +27,7 @@ func attack(type: int):
for bullet in BulletBase.generate(ComponentManager.getBullet("SwingSword"), self , getTrackingAnchor(), getTrackingAnchor().angle_to_point(currentFocusedPosition)):
if bullet is SwingSwordBullet:
await bullet.destroied
await TickTool.millseconds(1000)
elif type == 1:
for bullet in BulletBase.generate(ComponentManager.getBullet("Broom"), self , getTrackingAnchor(), getTrackingAnchor().angle_to_point(currentFocusedPosition)):
if is_instance_valid(currentBroom): break
@@ -38,6 +39,7 @@ func attack(type: int):
await chargeUp()
trySprint()
currentBroom.animator.play("attack")
await TickTool.millseconds(1000)
elif type == 2:
for i in 3:
for bullet in BulletBase.generate(ComponentManager.getBullet("BroomGun"), self , position, position.angle_to_point(currentFocusedPosition)):
@@ -51,6 +53,7 @@ func attack(type: int):
bullet.animator.play("rotate")
await bullet.destroied
fields[FieldStore.Entity.MOVEMENT_SPEED] /= 3
await TickTool.millseconds(4000)
elif type == 4:
BulletBase.generate(ComponentManager.getBullet("BroomBoomerang"), self , position, position.angle_to_point(currentFocusedPosition))
elif type == 5:
+1 -1
View File
@@ -3,6 +3,6 @@ extends PlayerBase
func register():
super.register()
killEnemy.connect(
func(_who, _by):
func(_w, _b):
storeEnergy(1)
)
+3 -4
View File
@@ -5,15 +5,14 @@ func register():
fields[FieldStore.Entity.MAX_HEALTH] = 100
fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.3
fields[FieldStore.Entity.OFFSET_SHOOT] = 10
attackCooldownMap[0] = randi_range(2000, 5000)
func spawn():
texture.play("walk")
func ai():
attackCooldownMap[0] = randi_range(1500, 4000)
PresetEntityAI.follow(self, currentFocusedBoss, 300)
PresetEntityAI.follow(self , currentFocusedBoss, 300)
tryAttack(0)
func attack(type):
if type == 0:
var weaponPos = findWeaponAnchor("normal")
for i in randi_range(1, 4):
BulletBase.generate(ComponentManager.getBullet("Star"), self, weaponPos, (currentFocusedBoss.position - position).angle())
BulletBase.generate(ComponentManager.getBullet("Star"), self , weaponPos, (currentFocusedBoss.position - position).angle())
return true
+1 -1
View File
@@ -2,4 +2,4 @@ extends PlayerBase
func register():
super.register()
sprintMultiplier += 3
sprintMultiplier += 1.5
+1 -1
View File
@@ -6,7 +6,7 @@ var MIN_ATTACK_DISTANCE = 400
func register():
fields[FieldStore.Entity.MAX_HEALTH] = 400
fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.9
fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.7
attackCooldownMap[0] = 4000
attackCooldownMap[1] = 3000
attackCooldownMap[2] = 500
+8 -9
View File
@@ -1,12 +1,11 @@
extends PlayerBase
var parryCounter: CooldownTimer = CooldownTimer.new(3000)
var healCounter = CooldownTimer.new(500)
func ai():
super.ai()
if parryCounter.isCooldowned():
var track = getTrackingAnchor()
var bullet = BulletTool.findClosetBulletCanDamage(track, get_tree(), self , 300)
if is_instance_valid(bullet):
BulletBase.generate(ComponentManager.getBullet("Parrier"), self , track, track.angle_to_point(bullet.position))
parryCounter.start()
func register():
super.register()
madeDamage.connect(
func(_w, _b):
if healCounter.start():
heal(1)
)
+8
View File
@@ -3,3 +3,11 @@ class_name ParryEffect
func beforeShot():
CameraManager.shake(300, 500)
static func createEffect(bullet: BulletBase, parrier: ParrierBullet):
parrier.parryiedTimes += 1
var eff = EffectController.create(ComponentManager.getEffect("Parry"), parrier.position + (bullet.position - parrier.position).normalized() * 200) # 从子弹位置,面向其他子弹的方向前进150
eff.modulate = bullet.modulate.blend(bullet.texture.modulate)
eff.rotation = parrier.position.angle_to_point(bullet.position)
eff.shot()
parrier.launcher.impluse((parrier.position - bullet.position).normalized() * sqrt(abs(bullet.speed)) * 250)
+8
View File
@@ -3,3 +3,11 @@ class_name ParryEntityEffect
func beforeShot():
CameraManager.shake(50, 200)
static func createEffectEntity(entity: EntityBase, by: BulletBase):
if by is ParrierBullet:
by.parryiedTimes += 1
var eff = EffectController.create(ComponentManager.getEffect("ParryEntity"), entity.position)
eff.rotation = by.position.angle_to_point(entity.position)
eff.shot()
entity.impluse((entity.position - by.position).normalized() * 450)
+48
View File
@@ -2,6 +2,51 @@
extends Weapon
class_name BigLaserWeapon
func sublimateOptions() -> Array[SublimateOption]:
return [
SublimateOption.new(
"湮灭强化",
"伤害+15",
func(w: Weapon, _e):
w.addStoreExtra("atk", 15),
1,
CategoryStore.Quality.COMMON
),
SublimateOption.new(
"高频激荡",
"间隔-0.01秒",
func(w: Weapon, _e):
w.addStoreExtra("time", -0.01),
1,
CategoryStore.Quality.RARE
),
SublimateOption.new(
"临界斩杀",
"对生命值低于30%的敌人,伤害+25%",
func(w: Weapon, _e):
w.addStoreExtra("executeAtk", 0.25),
1,
CategoryStore.Quality.EPIC
),
SublimateOption.new(
"蓄能聚焦",
"射线伤害+30,但伤害间隔+0.03秒",
func(w: Weapon, _e):
w.addStoreExtra("atk", 30)
w.addStoreExtra("time", 0.03),
1,
CategoryStore.Quality.LEGENDARY
),
SublimateOption.new(
"空间撕裂",
"射线持续时间+0.3秒",
func(w: Weapon, _e):
w.addStoreExtra("duration", 0.3),
2,
CategoryStore.Quality.COMMON
),
]
func update(to: int, origin: Dictionary, _entity: EntityBase):
origin["atk"] += 5 * to * soulLevel
origin["time"] /= 1 + 0.05 * to * soulLevel
@@ -12,4 +57,7 @@ func attack(entity: EntityBase):
var bigLaser: BigLaser = bullet
bigLaser.dotTime = readStore("time") * 1000
bigLaser.baseDamage = readStore("atk")
bigLaser.executeAtk = readStoreExtra("executeAtk")
await TickTool.frame()
bigLaser.animator.speed_scale = 5 / (5 + readStoreExtra("duration"))
return true
+54 -8
View File
@@ -1,18 +1,64 @@
@tool
extends Weapon
func sublimateOptions() -> Array[SublimateOption]:
return [
SublimateOption.new(
"晶核提纯",
"蓝水晶与钻石伤害+1",
func(w: Weapon, _e):
w.addStoreExtra("atk", 1),
1,
CategoryStore.Quality.COMMON
),
SublimateOption.new(
"钻石护盾",
"环绕钻石数量+1",
func(w: Weapon, _e):
w.addStoreExtra("count", 1),
1,
CategoryStore.Quality.RARE
),
SublimateOption.new(
"贯穿棱镜",
"穿透+10%",
func(w: Weapon, _e):
w.addStoreExtra("penerate", 0.1),
1,
CategoryStore.Quality.EPIC
),
SublimateOption.new(
"碎晶裂变",
"蓝水晶命中敌人时,有8%概率分裂出1颗钻石(伤害-20%",
func(w: Weapon, _e):
w.addStoreExtra("splitRate", 0.08),
1,
CategoryStore.Quality.LEGENDARY
),
SublimateOption.new(
"动能贯穿",
"蓝水晶飞行速度+10%",
func(w: Weapon, _e):
w.addStoreExtra("speed", 0.1),
1,
CategoryStore.Quality.COMMON
),
]
func update(to: int, origin: Dictionary, _entity: EntityBase):
origin["atk"] += 2 * to * soulLevel
origin["count"] = 1 + 1 * soulLevel
return origin
func attack(entity: EntityBase):
for i in BulletBase.generate(ComponentManager.getBullet("BlueCrystal"), entity, entity.findWeaponAnchor("normal"), deg_to_rad(randf_range(0, 360))):
if i is BlueCrystalBullet:
i.tracer = EntityTool.findClosetEntity(get_global_mouse_position(), get_tree(), !entity.isPlayer(), entity.isPlayer())
i.baseDamage = readStore("atk")
for crystal in BulletBase.generate(ComponentManager.getBullet("BlueCrystal"), entity, entity.findWeaponAnchor("normal"), deg_to_rad(randf_range(0, 360))):
if crystal is BlueCrystalBullet:
crystal.tracer = EntityTool.findClosetEntity(get_global_mouse_position(), get_tree(), !entity.isPlayer(), entity.isPlayer())
crystal.baseDamage = readStore("atk")
crystal.penerate += readStoreExtra("penerate")
crystal.speedBoost = readStoreExtra("speed")
for index in readStore("count"):
for j in BulletBase.generate(ComponentManager.getBullet("Diamond2"), entity, i.position, deg_to_rad(360.0 / readStore("count") * index)):
if j is Diamond2Bullet:
j.baseDamage = readStore("atk")
j.parent = i
for diamond in BulletBase.generate(ComponentManager.getBullet("Diamond2"), entity, crystal.position, deg_to_rad(360.0 / readStore("count") * index)):
if diamond is Diamond2Bullet:
diamond.baseDamage = readStore("atk")
diamond.parent = crystal
diamond.penerate += readStoreExtra("penerate")
return true
+50 -4
View File
@@ -1,6 +1,50 @@
@tool
extends Weapon
func sublimateOptions() -> Array[SublimateOption]:
return [
SublimateOption.new(
"破风之翎",
"伤害+15%",
func(w: Weapon, _e):
w.addStoreExtra("atk", 0.15),
1,
CategoryStore.Quality.COMMON
),
SublimateOption.new(
"嗜血",
"伤害倍率+40%,但额外消耗1点生命值",
func(w: Weapon, _e):
w.addStoreExtra("atk", 0.4)
w.addStoreExtra("self", 1),
2,
CategoryStore.Quality.LEGENDARY
),
SublimateOption.new(
"风行者",
"鸡毛箭出手速度+5%",
func(w: Weapon, _e):
w.addStoreExtra("initSpeed", 0.05),
1,
CategoryStore.Quality.RARE
),
SublimateOption.new(
"破釜沉舟",
"每失去5%生命值,伤害+6%",
func(w: Weapon, _e):
w.addStoreExtra("missAtk", 0.06),
2,
CategoryStore.Quality.EPIC
),
SublimateOption.new(
"生命汲取",
"鸡毛箭命中时回复0.2点生命值",
func(w: Weapon, _e):
w.addStoreExtra("lifesteal", 0.2),
1,
CategoryStore.Quality.COMMON
),
]
func update(to: int, origin: Dictionary, _entity: EntityBase):
origin["atk"] += 0.1 * to * soulLevel
origin["count"] = 1 * soulLevel
@@ -9,13 +53,15 @@ func update(to: int, origin: Dictionary, _entity: EntityBase):
func attack(entity: EntityBase):
entity.takeDamage(readStore("self"))
var weaponPos = entity.findWeaponAnchor("normal")
for i in BulletBase.generate(
for bullet in BulletBase.generate(
ComponentManager.getBullet("Bow"),
entity,
weaponPos,
weaponPos.angle_to_point(get_global_mouse_position())
):
var bullet: Bow = i
bullet.count = readStore("count")
bullet.atk = readStore("atk")
if bullet is Bow:
bullet.count = readStore("count")
bullet.atk = readStore("atk") * (1 + (1 - entity.getHealthPercent()) * readStoreExtra("missAtk"))
bullet.speed *= 1 + readStoreExtra("initSpeed")
bullet.lifesteal = readStoreExtra("lifesteal")
return true
+53 -5
View File
@@ -1,15 +1,63 @@
@tool
extends Weapon
func sublimateOptions() -> Array[SublimateOption]:
return [
SublimateOption.new(
"聚焦校准",
"散射-2°",
func(w: Weapon, _e):
w.addStoreExtra("split", -2),
1,
CategoryStore.Quality.RARE
),
SublimateOption.new(
"晶核过载",
"伤害+5,但散射+4°",
func(w: Weapon, _e):
w.addStoreExtra("atk", 5)
w.addStoreExtra("split", 4),
1,
CategoryStore.Quality.LEGENDARY
),
SublimateOption.new(
"弹头塑形",
"穿透+5%",
func(w: Weapon, _e):
w.addStoreExtra("penerate", 0.1),
1,
CategoryStore.Quality.EPIC
),
SublimateOption.new(
"晶能循环",
"能量消耗-0.05",
func(_w, _e):
needEnergy -= 0.05,
1,
CategoryStore.Quality.COMMON
),
SublimateOption.new(
"动能压制",
"命中敌人时有5%概率回收0.25点能量",
func(w: Weapon, _e):
w.addStoreExtra("cycleRate", 0.05)
w.addStoreExtra("cycleCount", 0.25),
1,
CategoryStore.Quality.COMMON
),
]
func update(to, origin, _entity):
origin["atk"] += 1 * to * soulLevel
origin["count"] = 1 * soulLevel
origin["split"] /= 1 + 0.005 * to * soulLevel
return origin
func attack(entity: EntityBase):
for i in BulletBase.generate(ComponentManager.getBullet("ChainGun"), entity, entity.texture.global_position, (get_global_mouse_position() - entity.texture.global_position).angle()):
if i is BulletBase:
i.baseDamage = readStore("atk")
i.count = floor(readStore("count"))
i.splits = readStore("split")
for bullet in BulletBase.generate(ComponentManager.getBullet("ChainGun"), entity, entity.texture.global_position, (get_global_mouse_position() - entity.texture.global_position).angle()):
if bullet is ChainGunBullet:
bullet.baseDamage = readStore("atk")
bullet.count = floor(readStore("count"))
bullet.splits = readStore("split")
bullet.penerate += readStoreExtra("penerate")
bullet.cycleRate = readStoreExtra("cycleRate")
bullet.cycleCount = readStoreExtra("cycleCount")
return true
+46 -1
View File
@@ -1,6 +1,49 @@
@tool
extends Weapon
func sublimateOptions() -> Array[SublimateOption]:
return [
SublimateOption.new(
"锋利齿轮",
"锋利度+4",
func(w: Weapon, _e):
w.addStoreExtra("atk", 4),
2,
CategoryStore.Quality.COMMON
),
SublimateOption.new(
"回旋风暴",
"齿轮回旋时转速额外+15%",
func(w: Weapon, _e):
w.addStoreExtra("rotateExtra", 0.15),
1,
CategoryStore.Quality.EPIC
),
SublimateOption.new(
"顺手牵羊",
"齿轮命中敌人时降低其2%攻击速度",
func(w: Weapon, _e):
w.addStoreExtra("atkspeed", 1),
1,
CategoryStore.Quality.COMMON
),
SublimateOption.new(
"离心甩臂",
"伤害+5%",
func(w: Weapon, _e):
w.addStoreExtra("dmg", 0.05),
1,
CategoryStore.Quality.WASTE
),
SublimateOption.new(
"撕裂锯齿",
"齿轮回旋时可吸附敌人,吸力+15",
func(w: Weapon, _e):
w.addStoreExtra("gravity", 15),
1,
CategoryStore.Quality.COMMON
),
]
func update(to: int, origin: Dictionary, _entity: EntityBase):
origin["atk"] += 1 * to * soulLevel
origin["rotate"] += 0.5 * to * soulLevel
@@ -16,5 +59,7 @@ func attack(entity: EntityBase):
if bullet is CogwheelBullet:
bullet.initialRotate = readStore("rotate")
bullet.rotateSpeed = readStore("rotate")
bullet.baseDamage = readStore("atk")
bullet.baseDamage = readStore("atk") * (1 + readStoreExtra("dmg"))
bullet.rotateExtra = readStoreExtra("rotateExtra")
bullet.gravityForce = readStoreExtra("gravity")
return true
+8 -6
View File
@@ -131,20 +131,20 @@ func setupCuttable(cutSpeed: float):
func(body):
var entity = EntityTool.fromHurtbox(body)
if entity:
speedScale = cutSpeed
animator.speed_scale *= cutSpeed
)
area_exited.connect(
func(body):
var entity = EntityTool.fromHurtbox(body)
if entity:
speedScale = 1
animator.speed_scale /= cutSpeed
)
func getDamage():
return baseDamage * damageMultipliers[usingDamageMultiplier]
func calculateDamage(crit: bool):
func calculateDamage(crit: bool, something: Variant):
var baseDmg = getDamage() * launcher.fields.get(FieldStore.Entity.DAMAGE_MULTIPILER) * randf_range(1 - GameRule.damageOffset, 1 + GameRule.damageOffset)
var damage = baseDmg + baseDmg * int(crit) * launcher.fields.get(FieldStore.Entity.CRIT_DAMAGE)
return damage
return damageOverride(damage, something)
func determineCrit():
return MathTool.rate(launcher.fields.get(FieldStore.Entity.CRIT_RATE) + GameRule.critRateInfluenceByLuckValue * launcher.fields[FieldStore.Entity.LUCK_VALUE])
func hitEntity(target: Node):
@@ -161,7 +161,7 @@ func hitObstacle(target: Node):
var obstacle = target as ObstacleBase
if is_instance_valid(obstacle.launcher):
if not BulletTool.canDamage(self , obstacle.launcher): return
obstacle.takeDamage(calculateDamage(determineCrit()))
obstacle.takeDamage(calculateDamage(determineCrit(), target))
if MathTool.rate(fullPenerate() - obstacle.penerateResistance):
baseDamage *= 1.0 - penerateDamageReduction
else:
@@ -257,6 +257,8 @@ func refract(newBullet: BulletBase, _entity: EntityBase, _index: int, _total: in
return newBullet
func hitBullet(_bullet: BulletBase):
pass
func damageOverride(origin: float, _something: Variant):
return origin
static func generate(
bullet: PackedScene,
@@ -275,7 +277,7 @@ static func generate(
instance.position = spawnPosition
instance.rotation = spawnRotation + deg_to_rad(launchBy.fields.get(FieldStore.Entity.OFFSET_SHOOT) * randf_range(-1, 1) * int(!ignoreOffset))
if addToWorld:
WorldManager.rootNode.call_deferred("add_child", instance)
WorldManager.rootNode.add_child.call_deferred(instance)
instance.add_to_group("bullets")
instances.append(instance)
return instances
+1 -1
View File
@@ -17,7 +17,7 @@ func getStateAngle(index: int):
func forceFilter():
bullets = bullets.filter(
func(b):
if b is BulletBase:
if is_instance_valid(b) && b is BulletBase:
return !b.destroying
else:
return false
+8 -5
View File
@@ -6,6 +6,7 @@ signal healed(amount: float)
signal healthChanged(health: float)
signal died()
signal killEnemy(who: EntityBase, by: BulletBase)
signal madeDamage(who: EntityBase, by: BulletBase)
signal energyChanged(energy: float, dontChangeDirection: bool)
@@ -46,7 +47,7 @@ var fields = {
FieldStore.Entity.CRIT_DAMAGE: 1,
"概率": TITLE_FLAG,
FieldStore.Entity.LUCK_VALUE: 1,
"饲料": TITLE_FLAG,
"增益": TITLE_FLAG,
FieldStore.Entity.PRICE_REDUCTION: 0,
FieldStore.Entity.FEED_COUNT_SHOW: 3,
FieldStore.Entity.FEED_COUNT_CAN_MADE: 5,
@@ -69,7 +70,7 @@ var inventory = {
ItemStore.ItemType.BEACHBALL: 0,
ItemStore.ItemType.SOUL: 0,
ItemStore.ItemType.CRYSTAL: 0,
ItemStore.ItemType.DIAMOND: 0
ItemStore.ItemType.DIAMOND: 100
}
var inventoryMax = {
ItemStore.ItemType.BASEBALL: INF, # 无限
@@ -171,7 +172,8 @@ func _ready():
)
healthChanged.emit(health)
energyChanged.emit(energy, false)
killEnemy.connect(func(): return )
killEnemy.connect(func(_w, _b): return )
madeDamage.connect(func(_w, _b): return )
spawn()
func _process(_delta):
health = clamp(health, 0, fields.get(FieldStore.Entity.MAX_HEALTH))
@@ -282,10 +284,10 @@ func takeDamage(baseDamage: float, crit: bool = false, perfectMiss: bool = false
if health <= 0:
tryDie(null)
return resultDamage
func bulletHit(bullet: BulletBase, crit: bool):
func bulletHit(bullet: BulletBase, crit: bool, damageOverride = "none"):
# 当受伤时
hurtAnimator.play("hurt")
var damage = bullet.calculateDamage(crit)
var damage = bullet.calculateDamage(crit, self ) if damageOverride == "none" else damageOverride
var perfectMiss = false
if sprinting:
playSound("miss")
@@ -304,6 +306,7 @@ func bulletHit(bullet: BulletBase, crit: bool):
hit.emit(damage, bullet, crit)
health -= damage
healthChanged.emit(health)
bullet.launcher.madeDamage.emit(self , bullet)
DamageLabel.create(damage, crit || perfectMiss, damageAnchor.global_position + MathTool.sampleInCircle(GameRule.damageLabelSpawnOffset))
if isBoss and bullet.launcher.isPlayer():
bullet.launcher.setBoss(self )
+2 -2
View File
@@ -2,7 +2,7 @@
extends HBoxContainer
class_name FeedName
@export var displayName: String = "未命名饲料"
@export var displayName: String = "未命名"
@export var quality: CategoryStore.Quality = CategoryStore.Quality.COMMON
@export var topic: CategoryStore.Topic = CategoryStore.Topic.SURVIVAL
@export var qualityColorMap = {
@@ -40,7 +40,7 @@ class_name FeedName
CategoryStore.Topic.SPEED: "速度",
CategoryStore.Topic.DAMAGE: "伤害",
CategoryStore.Topic.PROBABILITY: "幸运",
CategoryStore.Topic.FEED: "饲料",
CategoryStore.Topic.FEED: "增益",
CategoryStore.Topic.DROP: "掉落物",
CategoryStore.Topic.WEAPON: "武器",
CategoryStore.Topic.SUMMON: "召唤",
+2
View File
@@ -44,6 +44,8 @@ func takeDamage(damage: float):
func tryDie():
die()
queue_free()
func getHealthPercent():
return health / healthMax
func die():
pass
+1 -1
View File
@@ -5,7 +5,7 @@ class_name Feed
signal selected(applied: bool)
@export var avatarTexture: Texture2D = null
@export var displayName: String = "未命名饲料"
@export var displayName: String = "未命名"
@export var quality: CategoryStore.Quality = CategoryStore.Quality.COMMON
@export var topic: CategoryStore.Topic = CategoryStore.Topic.SURVIVAL
@export var fields: Array[FieldStore.Entity] = []
+1 -2
View File
@@ -241,7 +241,7 @@ func rebuildSublimateOptions(showNext: bool):
sublimateOptionsBox.add_child(instance)
disruptSublimateOptions()
func readStore(key: String):
return store.get(key, 0) + readStoreExtra(key)
return clamp(store.get(key, 0) + readStoreExtra(key), 0, INF)
func playSound(sound: String):
var body = sounds.get_node_or_null(sound)
if body is AudioStreamPlayer2D:
@@ -284,7 +284,6 @@ func addStoreExtra(key: String, value: float):
if !storeExtra.has(key):
storeExtra[key] = 0
storeExtra[key] += value
storeExtra[key] = clamp(storeExtra[key], 0, INF)
func readStoreExtra(key: String):
return storeExtra.get(key, 0)
func getSublimateOptions() -> Array[SublimateOption]:
+1 -1
View File
@@ -56,7 +56,7 @@ static var topicNameMap = {
Topic.SPEED: "速度",
Topic.DAMAGE: "伤害",
Topic.PROBABILITY: "幸运",
Topic.FEED: "饲料",
Topic.FEED: "增益",
Topic.DROP: "掉落物",
Topic.WEAPON: "武器",
Topic.SUMMON: "召唤",
+6 -2
View File
@@ -116,15 +116,19 @@ static var entityMinValueMap = {
Entity.DAMAGE_MULTIPILER: 0.01
}
static var entityApplier = {
Entity.MAX_HEALTH: func(entity: EntityBase, value):
Entity.MAX_HEALTH: func(entity: EntityBase, value: float):
entity.health += value
entity.statebar.forceSync()
return true
,
Entity.EXTRA_APPLE_MAX: func(entity, value):
Entity.EXTRA_APPLE_MAX: func(entity: EntityBase, value: float):
entity.inventoryMax[ItemStore.ItemType.APPLE] += value
return true
,
Entity.MAX_ENERGY: func(entity: EntityBase, value: float):
entity.energy += value
return true
,
}
static var entityViewCastMap = {
Entity.EXTRA_APPLE_MAX: func(entity, _value):
-3
View File
@@ -1,3 +0,0 @@
# Todo
1. 完成Godockly UGC框架。