1
1
mirror of https://github.com/Rundll86/Dog-Lynx-And-HCN.git synced 2026-07-11 20:42:55 +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/edge_nonalpha = 0.6
shader_parameter/alpha = 1.0 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] _data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 2 point_count = 2
[sub_resource type="CurveTexture" id="CurveTexture_xathl"] [sub_resource type="CurveTexture" id="CurveTexture_e7vrf"]
curve = SubResource("Curve_itlbo") 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] _data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
point_count = 2 point_count = 2
[sub_resource type="CurveTexture" id="CurveTexture_my7uy"] [sub_resource type="CurveTexture" id="CurveTexture_ypwek"]
curve = SubResource("Curve_75ctq") curve = SubResource("Curve_ws1jd")
[sub_resource type="Gradient" id="Gradient_62xhh"] [sub_resource type="Gradient" id="Gradient_3diwe"]
offsets = PackedFloat32Array(0, 0.60687) offsets = PackedFloat32Array(0, 0.60687)
colors = PackedColorArray(1, 1, 1, 1, 1, 0, 0, 1) colors = PackedColorArray(1, 1, 1, 1, 1, 0, 0, 1)
[sub_resource type="GradientTexture1D" id="GradientTexture1D_yix5j"] [sub_resource type="GradientTexture1D" id="GradientTexture1D_tn8hw"]
gradient = SubResource("Gradient_62xhh") gradient = SubResource("Gradient_3diwe")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_m7jvq"] [sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_nu1m4"]
particle_flag_disable_z = true particle_flag_disable_z = true
emission_shape = 3 emission_shape = 3
emission_box_extents = Vector3(0, 1000, 1) emission_box_extents = Vector3(0, 1000, 1)
angle_min = 1.07288e-05 angle_min = 1.07288e-05
angle_max = 360.0 angle_max = 360.0
angle_curve = SubResource("CurveTexture_my7uy") angle_curve = SubResource("CurveTexture_ypwek")
spread = 15.0 spread = 15.0
initial_velocity_min = -500.0 initial_velocity_min = -500.0
initial_velocity_max = 500.0 initial_velocity_max = 500.0
gravity = Vector3(0, 0, 0) gravity = Vector3(0, 0, 0)
scale_min = 3.0 scale_min = 3.0
scale_max = 15.0 scale_max = 15.0
color_ramp = SubResource("GradientTexture1D_yix5j") color_ramp = SubResource("GradientTexture1D_tn8hw")
alpha_curve = SubResource("CurveTexture_xathl") alpha_curve = SubResource("CurveTexture_e7vrf")
[sub_resource type="Animation" id="Animation_1rif1"] [sub_resource type="Animation" id="Animation_1rif1"]
length = 0.001 length = 0.001
@@ -230,7 +230,7 @@ position = Vector2(50, 1000)
amount = 400 amount = 400
lifetime = 0.25 lifetime = 0.25
visibility_rect = Rect2(-100, -1000, 200, 2000) 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] [node name="launcher" type="Polygon2D" parent="texture/rect" index="1" unique_id=1254071384]
position = Vector2(50, -21) position = Vector2(50, -21)
+198 -9
View File
@@ -67,6 +67,66 @@ tracks/4/keys = {
"update": 1, "update": 1,
"values": [0] "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"] [sub_resource type="Animation" id="Animation_oinqg"]
resource_name = "destroy" resource_name = "destroy"
@@ -85,7 +145,7 @@ tracks/0/interp = 1
tracks/0/loop_wrap = true tracks/0/loop_wrap = true
tracks/0/keys = { tracks/0/keys = {
"handle_modes": PackedInt32Array(0, 0, 2), "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) "times": PackedFloat32Array(0, 0.2, 0.3)
} }
tracks/1/type = "bezier" tracks/1/type = "bezier"
@@ -96,8 +156,8 @@ tracks/1/interp = 1
tracks/1/loop_wrap = true tracks/1/loop_wrap = true
tracks/1/keys = { tracks/1/keys = {
"handle_modes": PackedInt32Array(0, 0, 0, 0, 0, 0), "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), "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.3, 0.6, 0.888264, 1.2, 1.45) "times": PackedFloat32Array(0, 0.2, 0.4, 0.888264, 1.2, 1.45)
} }
tracks/2/type = "bezier" tracks/2/type = "bezier"
tracks/2/imported = false tracks/2/imported = false
@@ -107,7 +167,7 @@ tracks/2/interp = 1
tracks/2/loop_wrap = true tracks/2/loop_wrap = true
tracks/2/keys = { tracks/2/keys = {
"handle_modes": PackedInt32Array(0, 0), "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) "times": PackedFloat32Array(1.45, 1.7)
} }
tracks/3/type = "value" tracks/3/type = "value"
@@ -175,6 +235,66 @@ tracks/6/keys = {
"update": 1, "update": 1,
"values": [6, 0, 6] "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"] [sub_resource type="AnimationLibrary" id="AnimationLibrary_w1utg"]
_data = { _data = {
@@ -183,8 +303,53 @@ _data = {
&"spawn": SubResource("Animation_o5h0y") &"spawn": SubResource("Animation_o5h0y")
} }
[sub_resource type="CircleShape2D" id="CircleShape2D_1sgli"] [sub_resource type="Curve" id="Curve_1sgli"]
radius = 78.05767 _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")] [node name="Volcano" unique_id=2046549391 instance=ExtResource("1_gd3m7")]
script = ExtResource("2_w1utg") 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="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")] [node name="hitbox" parent="." index="1" unique_id=175349408 node_paths=PackedStringArray("target")]
position = Vector2(299.99997, 0.1221919) position = Vector2(399.99997, 0.16292253)
rotation = 0.00040731629 rotation = 0.32215786
shape = SubResource("CircleShape2D_1sgli") shape = SubResource("RectangleShape2D_1sgli")
script = ExtResource("7_1sgli") script = ExtResource("7_1sgli")
target = NodePath("../texture/anchor/textureSword") target = NodePath("../texture/anchor/textureSword")
offsetRotation = 45.0 offsetRotation = 45.0
+4 -5
View File
@@ -20,10 +20,9 @@ theme_override_styles/panel = SubResource("StyleBoxFlat_1yfsg")
displayName = "氰化氢" displayName = "氰化氢"
slogan = "微量便可颠覆生机" slogan = "微量便可颠覆生机"
avatar = ExtResource("2_1yfsg") avatar = ExtResource("2_1yfsg")
description = "击杀敌人时储能。" description = "击杀敌人时少量储能。"
fields = Array[int]([18, 1]) fields = Array[int]([18, 10, 1])
fieldValues = Array[float]([3.0, -0.15]) fieldValues = Array[float]([4.0, 0.2, -0.2])
clickToRebuild = true
[node name="avatarTexture" parent="wrapper" parent_id_path=PackedInt32Array(2023039659) index="0" unique_id=1334645594] [node name="avatarTexture" parent="wrapper" parent_id_path=PackedInt32Array(2023039659) index="0" unique_id=1334645594]
texture = ExtResource("2_1yfsg") texture = ExtResource("2_1yfsg")
@@ -35,4 +34,4 @@ text = "氰化氢"
text = "“微量便可颠覆生机”" text = "“微量便可颠覆生机”"
[node name="descriptionLabel" parent="wrapper/infoContainer" parent_id_path=PackedInt32Array(143242635) index="1" unique_id=808054282] [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") avatar = ExtResource("2_1s675")
description = "拥有更大的冲刺速度。" description = "拥有更大的冲刺速度。"
fields = Array[int]([3, 4, 10]) 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 clickToRebuild = true
[node name="avatarTexture" parent="wrapper" parent_id_path=PackedInt32Array(2023039659) index="0" unique_id=1334645594] [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 = "牧羊犬" displayName = "牧羊犬"
slogan = "恪守使命不离不弃" slogan = "恪守使命不离不弃"
avatar = ExtResource("2_fi2nw") avatar = ExtResource("2_fi2nw")
description = "每隔3秒发动格挡。" description = "造成伤害时少量吸血,
每0.5秒最多触发一次。"
fields = Array[int]([0, 8, 3]) 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] [node name="avatarTexture" parent="wrapper" parent_id_path=PackedInt32Array(2023039659) index="0" unique_id=1334645594]
texture = ExtResource("2_fi2nw") texture = ExtResource("2_fi2nw")
@@ -33,4 +34,5 @@ text = "牧羊犬"
text = "“恪守使命不离不弃”" text = "“恪守使命不离不弃”"
[node name="descriptionLabel" parent="wrapper/infoContainer" parent_id_path=PackedInt32Array(143242635) index="1" unique_id=808054282] [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="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="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"] [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")] [node name="MuyangDog" unique_id=1711205167 instance=ExtResource("1_y3l4w")]
script = ExtResource("2_mr6nm") script = ExtResource("2_mr6nm")
displayName = "牧羊犬" 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 layout_mode = 2
theme = ExtResource("2_k71he") theme = ExtResource("2_k71he")
bbcode_enabled = true bbcode_enabled = true
text = "[b]未命名饲料[/b]" text = "[b]未命名[/b]"
fit_content = true fit_content = true
autowrap_mode = 0 autowrap_mode = 0
+1 -1
View File
@@ -44,7 +44,7 @@
- **ENERGY_MULTIPILER(储能倍率)**:不管以何种方式获得能量,都将乘以此倍率。 - **ENERGY_MULTIPILER(储能倍率)**:不管以何种方式获得能量,都将乘以此倍率。
- **PENARATION_RESISTANCE(穿透抗性)**:敌人发射的可穿透射弹在击中自己后降低穿透率。 - **PENARATION_RESISTANCE(穿透抗性)**:敌人发射的可穿透射弹在击中自己后降低穿透率。
- **EXTRA_BULLET_COUNT(多重射击)**:发射射弹时有概率多生成一些。 - **EXTRA_BULLET_COUNT(多重射击)**:发射射弹时有概率多生成一些。
- **LUCK_VALUE(幸运值)**:影响很多概率行为,如暴击、掉落苹果、出现高品质饲料 - **LUCK_VALUE(幸运值)**:影响很多概率行为,如暴击、掉落苹果、出现高品质增益
- **ENERGY_REGENERATION(能量再生效率)**:自身缓慢恢复能量的速度。 - **ENERGY_REGENERATION(能量再生效率)**:自身缓慢恢复能量的速度。
- **BULLET_SPLIT(分裂)**:射弹命中后有概率分裂成多颗。 - **BULLET_SPLIT(分裂)**:射弹命中后有概率分裂成多颗。
- **BULLET_REFRACTION(折射)**:射弹命中后有概率生成面向最近的敌人的同类射弹。 - **BULLET_REFRACTION(折射)**:射弹命中后有概率生成面向最近的敌人的同类射弹。
+3 -1
View File
@@ -6,6 +6,7 @@ class_name Arrow
var atk: float = 0 var atk: float = 0
var waitTime: float = 0 var waitTime: float = 0
var offsetRotation: float = 0 var offsetRotation: float = 0
var lifesteal: float = 0
func register(): func register():
trail.emitting = false trail.emitting = false
@@ -23,6 +24,7 @@ func ai():
PresetBulletAI.forward(self , rotation) PresetBulletAI.forward(self , rotation)
if speed < 1: if speed < 1:
tryDestroy() tryDestroy()
func succeedToHit(_dmg: float, _entity: EntityBase): func succeedToHit(_dmg: float, entity: EntityBase):
var effect = EffectController.create(ComponentManager.getEffect("ShootBlood"), position) var effect = EffectController.create(ComponentManager.getEffect("ShootBlood"), position)
effect.shot() effect.shot()
entity.heal(lifesteal)
+9 -1
View File
@@ -2,18 +2,26 @@ extends BulletBase
class_name BigLaser class_name BigLaser
var dotTime: float = 100 var dotTime: float = 100
var executeAtk: float = 0.25
func spawn(): func spawn():
CameraManager.shake(5000, 100) # 激光会运行5秒(5000毫秒),期间震屏超高强度 CameraManager.shake(5000, 100) # 激光会运行5秒(5000毫秒),期间震屏超高强度
CameraManager.playAnimation("bigLaser") CameraManager.playAnimation("bigLaser")
baseDamage *= launcher.fields[FieldStore.Entity.ATTACK_SPEED]
func ai(): func ai():
rotation = lerp_angle(rotation, ((launcher.currentFocusedPosition - position).angle()), 0.1) rotation = lerp_angle(rotation, ((launcher.currentFocusedPosition - position).angle()), 0.1)
position = launcher.texture.global_position position = launcher.texture.global_position
func applyDot(): func applyDot():
hitbox.disabled = true hitbox.disabled = true
await TickTool.millseconds(dotTime / launcher.fields[FieldStore.Entity.ATTACK_SPEED]) await TickTool.millseconds(dotTime / launcher.fields[FieldStore.Entity.ATTACK_SPEED])
await TickTool.frame() # 等至少一帧,防止跳帧导致没检测到伤害
hitbox.disabled = false hitbox.disabled = false
await TickTool.millseconds(dotTime / launcher.fields[FieldStore.Entity.ATTACK_SPEED]) await TickTool.millseconds(dotTime / launcher.fields[FieldStore.Entity.ATTACK_SPEED])
await TickTool.frame() # 等至少一帧,防止跳帧导致没检测到伤害 await TickTool.frame() # 等至少一帧,防止跳帧导致没检测到伤害
return true 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 class_name BlueCrystalBullet
var tracer: EntityBase = null var tracer: EntityBase = null
var canTrace: bool = true
var speedBoost: float = 0
var splitRate: float = 0
@onready var trail: GPUParticles2D = $%trail @onready var trail: GPUParticles2D = $%trail
func ai(): func ai():
@@ -9,12 +13,22 @@ func ai():
var tracker = tracer.getTrackingAnchor() var tracker = tracer.getTrackingAnchor()
var targetAngle = position.angle_to_point(tracker) var targetAngle = position.angle_to_point(tracker)
trail.rotation = - Vector2.from_angle(rotation).angle_to(Vector2.from_angle(targetAngle)) / (speed / initialSpeed) 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: else:
trail.rotation = 0 trail.rotation = 0
speed += 0.1 speed += 0.1 * (1 + speedBoost)
PresetBulletAI.forward(self, rotation) PresetBulletAI.forward(self , rotation)
func destroy(_beacuseMap: bool): func destroy(_beacuseMap: bool):
var eff = EffectController.create(ComponentManager.getEffect("BlueCrystalExplosion"), global_position) var eff = EffectController.create(ComponentManager.getEffect("BlueCrystalExplosion"), global_position)
eff.rotation = rotation eff.rotation = rotation
eff.shot() 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 count: int = 0
var atk: float = 0 var atk: float = 0
var waitTime: float = 2000 var waitTime: float = 2000
var lifesteal: float = 0.2
func spawn(): func spawn():
var startAngle = rotation - deg_to_rad(count * 10.0 / 2) var startAngle = rotation - deg_to_rad(count * 10.0 / 2)
for c in count: for c in count:
for i in BulletBase.generate( for bullet in BulletBase.generate(
ComponentManager.getBullet("Arrow"), ComponentManager.getBullet("Arrow"),
launcher, launcher,
position, position,
startAngle startAngle
): ):
var bullet: Arrow = i if bullet is Arrow:
bullet.atk = atk bullet.atk = atk
bullet.waitTime = waitTime bullet.waitTime = waitTime
bullet.offsetRotation = deg_to_rad(c * 10.0) bullet.offsetRotation = deg_to_rad(c * 10.0)
bullet.lifesteal = lifesteal
await TickTool.millseconds(waitTime) await TickTool.millseconds(waitTime)
tryDestroy() tryDestroy()
func ai(): func ai():
+13 -7
View File
@@ -1,21 +1,27 @@
extends BulletBase extends BulletBase
class_name ChainGunBullet
var count: int = 0 var count: int = 0
var splits: float = 0 var splits: float = 0
var cycleRate: float = 0
var cycleCount: float = 0
@onready var anchor: Node2D = $"%anchor" @onready var anchor: Node2D = $"%anchor"
func spawn(): func spawn():
for j in count: for i in count:
for i in BulletBase.generate( for bullet in BulletBase.generate(
ComponentManager.getBullet("PurpleCrystalSmall"), ComponentManager.getBullet("PurpleCrystalSmall"),
launcher, launcher,
anchor.global_position, anchor.global_position,
rotation + deg_to_rad(splits * randf_range(-1, 1)) rotation + deg_to_rad(splits * randf_range(-1, 1))
): ):
if i is BulletBase: if bullet is PurpleCrystalBullet:
var dir = Vector2.from_angle(i.rotation).rotated(deg_to_rad(-90)) var dir = Vector2.from_angle(bullet.rotation).rotated(deg_to_rad(-90))
i.baseDamage = baseDamage bullet.baseDamage = baseDamage
i.position += dir * (count - j * 2) * 10 / 2 bullet.position += dir * (count - i * 2) * 10 / 2
bullet.penerate += penerate
bullet.cycleRate = cycleRate
bullet.cycleCount = cycleCount
func ai(): 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 rotateSpeed: float = 0
var dotTime: float = 0 var dotTime: float = 0
var slow: float = 0.2 var slow: float = 0.2
var rotateExtra: float = 0
var atkSpeeddown: float = 0
var gravityForce: float = 0
func spawn(): func spawn():
slow *= initialRotate / 15.0 slow *= initialRotate / 15.0
func ai(): func ai():
PresetBulletAI.forward(self, rotation) PresetBulletAI.forward(self , rotation)
texture.rotation_degrees += rotateSpeed texture.rotation_degrees += rotateSpeed
if rotateSpeed < 0: if rotateSpeed < 0:
speed += slow speed += slow
PresetBulletAI.trace(self, launcher.position, 0.1) PresetBulletAI.trace(self , launcher.position, 0.1)
if position.distance_to(launcher.position) < 100: if position.distance_to(launcher.position) < 100:
tryDestroy() tryDestroy()
else: else:
speed = initialSpeed * (rotateSpeed / initialRotate) speed = initialSpeed * (rotateSpeed / initialRotate)
if rotateSpeed >= 0 and rotateSpeed / slow <= 0: if rotateSpeed >= 0 and rotateSpeed / slow <= 0:
slow = 0.5 slow = 0.4
dotTime = 1000 / (rotateSpeed) dotTime = 1000 / rotateSpeed
rotateSpeed -= slow if rotateSpeed < 0:
rotateSpeed -= slow * (1 + rotateExtra)
else:
rotateSpeed -= slow
func applyDot(): func applyDot():
hitbox.disabled = true hitbox.disabled = true
await TickTool.frame() await TickTool.frame()
@@ -29,6 +35,9 @@ func applyDot():
await TickTool.millseconds(dotTime / launcher.fields[FieldStore.Entity.ATTACK_SPEED]) await TickTool.millseconds(dotTime / launcher.fields[FieldStore.Entity.ATTACK_SPEED])
await TickTool.frame() # 等至少一帧,防止跳帧导致没检测到伤害 await TickTool.frame() # 等至少一帧,防止跳帧导致没检测到伤害
return true 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): func refract(newBullet: BulletBase, _entity: EntityBase, _index: int, _total: int, _lastBullet: float):
if newBullet is CogwheelBullet: if newBullet is CogwheelBullet:
+2 -2
View File
@@ -10,14 +10,14 @@ var delta = 0.01
func spawn(): func spawn():
texture.play(str(randi_range(0, 2))) texture.play(str(randi_range(0, 2)))
func ai(): func ai():
PresetBulletAI.forward(self, rotation) PresetBulletAI.forward(self , rotation)
func succeedToHit(_dmg: float, entity: EntityBase): func succeedToHit(_dmg: float, entity: EntityBase):
if entity.isBoss: if entity.isBoss:
launcher.fields[FieldStore.Entity.DAMAGE_MULTIPILER] *= 1 + delta launcher.fields[FieldStore.Entity.DAMAGE_MULTIPILER] *= 1 + delta
addTimes += 1 addTimes += 1
if is_instance_valid(lastHit): if is_instance_valid(lastHit):
if lastHit.get_class() == entity.get_class(): if lastHit.get_class() == entity.get_class():
entity.bulletHit(self, true) entity.bulletHit(self , true)
lastHit = entity lastHit = entity
if bouncedTime < maxBouncedTime: if bouncedTime < maxBouncedTime:
var newEntity = EntityTool.findClosetEntity(position, get_tree(), !launcher.isPlayer(), launcher.isPlayer(), [entity]) 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 atk: float = 1
var reflectRate: 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): func generateParryBall(bDamage: float):
var cycler = launcher.getOrCreateCycleTimer("parry", 2000, 100) var cycler = launcher.getOrCreateCycleTimer("parry", 2000, 100)
if len(cycler.bullets) < maxBallCount: # 玩家最多只能拥有多少气 if len(cycler.bullets) < maxBallCount: # 玩家最多只能拥有多少气
@@ -47,7 +34,7 @@ func spawn():
eff.shot() eff.shot()
func succeedToHit(_dmg: float, entity: EntityBase): func succeedToHit(_dmg: float, entity: EntityBase):
if parryiedTimes < maxParryTimes && MathTool.rate(parryRate): if parryiedTimes < maxParryTimes && MathTool.rate(parryRate):
penerateEffect(entity, entity.position * 1.01) ParryEntityEffect.createEffectEntity(entity, self )
func hitBullet(bullet: BulletBase): # 当前子弹与其他子弹相撞 func hitBullet(bullet: BulletBase): # 当前子弹与其他子弹相撞
if !is_instance_valid(launcher): return if !is_instance_valid(launcher): return
if BulletTool.canDamage(bullet, launcher): # 其他子弹可以使当前子弹的发射者受伤吗? if BulletTool.canDamage(bullet, launcher): # 其他子弹可以使当前子弹的发射者受伤吗?
@@ -57,8 +44,8 @@ func hitBullet(bullet: BulletBase): # 当前子弹与其他子弹相撞
# 魔法运动和召唤运动的子弹虽不能格挡,但是可以储能,吐息运动的子弹会对发射者产生击退 # 魔法运动和召唤运动的子弹虽不能格挡,但是可以储能,吐息运动的子弹会对发射者产生击退
if bullet.motionType == BulletBase.MotionType.PROJECTILE: if bullet.motionType == BulletBase.MotionType.PROJECTILE:
# 无论如何都要生成格挡特效 # 无论如何都要生成格挡特效
parryEffect(bullet) ParryEffect.createEffect(bullet, self )
# 弹反 还是 格挡? # 弹反 还是 格挡?
if MathTool.rate(reflectRate): if MathTool.rate(reflectRate):
bullet.look_at(bullet.launcher.getTrackingAnchor()) bullet.look_at(bullet.launcher.getTrackingAnchor())
bullet.launcher = launcher bullet.launcher = launcher
@@ -70,33 +57,33 @@ func hitBullet(bullet: BulletBase): # 当前子弹与其他子弹相撞
bullet.tryDestroy() bullet.tryDestroy()
generateParryBall(bullet.baseDamage) generateParryBall(bullet.baseDamage)
elif bullet.motionType == BulletBase.MotionType.SWING: elif bullet.motionType == BulletBase.MotionType.SWING:
parryEffect(bullet) ParryEffect.createEffect(bullet, self )
bullet.hitbox.set_deferred("disabled", true) bullet.hitbox.set_deferred("disabled", true)
generateParryBall(bullet.baseDamage) generateParryBall(bullet.baseDamage)
InternalDamageBullet.generateBall(launcher, bullet.launcher, atk) InternalDamageBullet.generateBall(launcher, bullet.launcher, atk)
elif bullet.motionType == BulletBase.MotionType.STAB: elif bullet.motionType == BulletBase.MotionType.STAB:
parryEffect(bullet) ParryEffect.createEffect(bullet, self )
penerateEffect(bullet.launcher, bullet.launcher.position * 1.01) ParryEntityEffect.createEffectEntity(bullet.launcher, self )
bullet.hitbox.set_deferred("disabled", true) bullet.hitbox.set_deferred("disabled", true)
bullet.launcher.takeDamage(atk * reflectRate * bullet.getDamage()) bullet.launcher.takeDamage(atk * reflectRate * bullet.getDamage())
bullet.launcher.impluse(Vector2.from_angle(bullet.rotation) * -2000) bullet.launcher.impluse(Vector2.from_angle(bullet.rotation) * -2000)
generateParryBall(bullet.baseDamage) generateParryBall(bullet.baseDamage)
InternalDamageBullet.generateBall(launcher, bullet.launcher, atk) InternalDamageBullet.generateBall(launcher, bullet.launcher, atk)
elif bullet.motionType == BulletBase.MotionType.SPRINT: elif bullet.motionType == BulletBase.MotionType.SPRINT:
parryEffect(bullet) ParryEffect.createEffect(bullet, self )
bullet.tryDestroy() bullet.tryDestroy()
bullet.launcher.velocity *= 0 bullet.launcher.velocity *= 0
generateParryBall(bullet.baseDamage) generateParryBall(bullet.baseDamage)
for i in 2: for i in 2:
InternalDamageBullet.generateBall(launcher, bullet.launcher, atk) InternalDamageBullet.generateBall(launcher, bullet.launcher, atk)
elif bullet.motionType == BulletBase.MotionType.BREATH: 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) bullet.launcher.impluse(Vector2.from_angle(bullet.rotation) * -500)
elif bullet.motionType == BulletBase.MotionType.SUMMON || bullet.motionType == BulletBase.MotionType.MAGIC: 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)) launcher.storeEnergy(sqrt(bullet.baseDamage))
elif bullet.motionType == BulletBase.MotionType.EXPLOSION: elif bullet.motionType == BulletBase.MotionType.EXPLOSION:
penerateEffect(bullet.launcher, launcher.position) ParryEntityEffect.createEffectEntity(bullet.launcher, self )
for i in 2: for i in 2:
InternalDamageBullet.generateBall(bullet.launcher, launcher, sqrt(bullet.getDamage())) InternalDamageBullet.generateBall(bullet.launcher, launcher, sqrt(bullet.getDamage()))
else: else:
+1 -1
View File
@@ -4,7 +4,7 @@ class_name PipeBullet
var energy: float = 0 var energy: float = 0
func ai(): func ai():
PresetBulletAI.forward(self, rotation) PresetBulletAI.forward(self , rotation)
texture.rotation += initialSpeed * (1 - lifeTimePercent()) / 100 texture.rotation += initialSpeed * (1 - lifeTimePercent()) / 100
speed = initialSpeed * (1 - lifeTimePercent()) speed = initialSpeed * (1 - lifeTimePercent())
baseDamage = energy baseDamage = energy
+10 -1
View File
@@ -1,8 +1,17 @@
extends BulletBase extends BulletBase
class_name PurpleCrystalBullet
var cycleRate: float = 0
var cycleCount: float = 0
var cycled: bool = false
func ai(): func ai():
PresetBulletAI.forward(self, rotation) PresetBulletAI.forward(self , rotation)
func destroy(_beacuseMap: bool): func destroy(_beacuseMap: bool):
var eff = EffectController.create(ComponentManager.getEffect("PurpleCrystalExplosion"), global_position) var eff = EffectController.create(ComponentManager.getEffect("PurpleCrystalExplosion"), global_position)
eff.rotation = rotation eff.rotation = rotation
eff.shot() 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 class_name RainbowCat
func ai(): func ai():
PresetBulletAI.forward(self, rotation) PresetBulletAI.forward(self , rotation)
func succeedToHit(_dmg: float, _entity: EntityBase): func succeedToHit(_dmg: float, _entity: EntityBase):
EffectController.create(ComponentManager.getEffect("CatBoom"), position).shot() EffectController.create(ComponentManager.getEffect("CatBoom"), position).shot()
rotation_degrees = randf_range(0, 360) rotation_degrees = randf_range(0, 360)
+2 -2
View File
@@ -8,12 +8,12 @@ var count: int = 0
func register(): func register():
hitbox.shape = hitbox.shape.duplicate() hitbox.shape = hitbox.shape.duplicate()
func ai(): func ai():
PresetBulletAI.forward(self, rotation) PresetBulletAI.forward(self , rotation)
speed = (1 - lifeTimePercent()) * initialSpeed speed = (1 - lifeTimePercent()) * initialSpeed
func destroy(_beacuseMap: bool): func destroy(_beacuseMap: bool):
hitbox.shape.radius = radius hitbox.shape.radius = radius
EffectController.create(ComponentManager.getEffect("RedCrystalExplosion"), global_position).shot() 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 i in randi_range(1, count):
for bullet in BulletBase.generate(ComponentManager.getBullet("CrystalBlock"), launcher, position, deg_to_rad(randf_range(0, 360))): for bullet in BulletBase.generate(ComponentManager.getBullet("CrystalBlock"), launcher, position, deg_to_rad(randf_range(0, 360))):
if bullet is CrystalBlockBullet: if bullet is CrystalBlockBullet:
+6 -2
View File
@@ -9,6 +9,9 @@ var count: int = 0
var dmg5: float = 0 var dmg5: float = 0
var splitAngle: float = 10 var splitAngle: float = 10
func spawn():
animator.speed_scale = 0.85
setupCuttable(0.75)
func ai(): func ai():
PresetBulletAI.lockLauncher(self , launcher, true) PresetBulletAI.lockLauncher(self , launcher, true)
rotation = lerp_angle( rotation = lerp_angle(
@@ -16,8 +19,9 @@ func ai():
position.angle_to_point(launcher.currentFocusedPosition), position.angle_to_point(launcher.currentFocusedPosition),
rotates rotates
) )
func succeedToHit(_dmg: float, _entity: EntityBase): func succeedToHit(_dmg: float, entity: EntityBase):
EffectController.create(ComponentManager.getEffect("SwordCut"), textureSword.global_position).shot() entity.impluse((entity.getTrackingAnchor() - position).normalized() * 400)
ParryEntityEffect.createEffectEntity(entity, self )
func generateShadow(): func generateShadow():
var startAngle = rotation - deg_to_rad(count * splitAngle / 2) var startAngle = rotation - deg_to_rad(count * splitAngle / 2)
+3 -3
View File
@@ -3,17 +3,17 @@ class_name Maodie
func register(): func register():
fields[FieldStore.Entity.MAX_HEALTH] = 75 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) attackCooldownMap[0] = randi_range(5000, 8000)
sprintMultiplier = randf_range(5, 15) sprintMultiplier = randf_range(5, 15)
func spawn(): func spawn():
texture.play("walk") texture.play("walk")
func ai(): func ai():
PresetEntityAI.follow(self, currentFocusedBoss) PresetEntityAI.follow(self , currentFocusedBoss)
tryAttack(0, true) tryAttack(0, true)
func attack(type: int): func attack(type: int):
if type == 0: if type == 0:
BulletBase.generate(ComponentManager.getBullet("ChickSprint"), self, position, 0) BulletBase.generate(ComponentManager.getBullet("ChickSprint"), self , position, 0)
trySprint() trySprint()
return true return true
func sprint(): func sprint():
+1 -1
View File
@@ -4,7 +4,7 @@ func register():
fields[FieldStore.Entity.MAX_HEALTH] = 120 fields[FieldStore.Entity.MAX_HEALTH] = 120
fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.2 fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.2
fields[FieldStore.Entity.OFFSET_SHOOT] = 0 fields[FieldStore.Entity.OFFSET_SHOOT] = 0
attackCooldownMap[0] = randi_range(100, 500) attackCooldownMap[0] = randi_range(1000, 3000)
func spawn(): func spawn():
texture.play("walk") texture.play("walk")
func ai(): 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)): for bullet in BulletBase.generate(ComponentManager.getBullet("SwingSword"), self , getTrackingAnchor(), getTrackingAnchor().angle_to_point(currentFocusedPosition)):
if bullet is SwingSwordBullet: if bullet is SwingSwordBullet:
await bullet.destroied await bullet.destroied
await TickTool.millseconds(1000)
elif type == 1: elif type == 1:
for bullet in BulletBase.generate(ComponentManager.getBullet("Broom"), self , getTrackingAnchor(), getTrackingAnchor().angle_to_point(currentFocusedPosition)): for bullet in BulletBase.generate(ComponentManager.getBullet("Broom"), self , getTrackingAnchor(), getTrackingAnchor().angle_to_point(currentFocusedPosition)):
if is_instance_valid(currentBroom): break if is_instance_valid(currentBroom): break
@@ -38,6 +39,7 @@ func attack(type: int):
await chargeUp() await chargeUp()
trySprint() trySprint()
currentBroom.animator.play("attack") currentBroom.animator.play("attack")
await TickTool.millseconds(1000)
elif type == 2: elif type == 2:
for i in 3: for i in 3:
for bullet in BulletBase.generate(ComponentManager.getBullet("BroomGun"), self , position, position.angle_to_point(currentFocusedPosition)): 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") bullet.animator.play("rotate")
await bullet.destroied await bullet.destroied
fields[FieldStore.Entity.MOVEMENT_SPEED] /= 3 fields[FieldStore.Entity.MOVEMENT_SPEED] /= 3
await TickTool.millseconds(4000)
elif type == 4: elif type == 4:
BulletBase.generate(ComponentManager.getBullet("BroomBoomerang"), self , position, position.angle_to_point(currentFocusedPosition)) BulletBase.generate(ComponentManager.getBullet("BroomBoomerang"), self , position, position.angle_to_point(currentFocusedPosition))
elif type == 5: elif type == 5:
+1 -1
View File
@@ -3,6 +3,6 @@ extends PlayerBase
func register(): func register():
super.register() super.register()
killEnemy.connect( killEnemy.connect(
func(_who, _by): func(_w, _b):
storeEnergy(1) storeEnergy(1)
) )
+3 -4
View File
@@ -5,15 +5,14 @@ func register():
fields[FieldStore.Entity.MAX_HEALTH] = 100 fields[FieldStore.Entity.MAX_HEALTH] = 100
fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.3 fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.3
fields[FieldStore.Entity.OFFSET_SHOOT] = 10 fields[FieldStore.Entity.OFFSET_SHOOT] = 10
attackCooldownMap[0] = randi_range(2000, 5000)
func spawn(): func spawn():
texture.play("walk") texture.play("walk")
func ai(): func ai():
attackCooldownMap[0] = randi_range(1500, 4000) PresetEntityAI.follow(self , currentFocusedBoss, 300)
PresetEntityAI.follow(self, currentFocusedBoss, 300)
tryAttack(0) tryAttack(0)
func attack(type): func attack(type):
if type == 0: if type == 0:
var weaponPos = findWeaponAnchor("normal") 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 return true
+1 -1
View File
@@ -2,4 +2,4 @@ extends PlayerBase
func register(): func register():
super.register() super.register()
sprintMultiplier += 3 sprintMultiplier += 1.5
+1 -1
View File
@@ -6,7 +6,7 @@ var MIN_ATTACK_DISTANCE = 400
func register(): func register():
fields[FieldStore.Entity.MAX_HEALTH] = 400 fields[FieldStore.Entity.MAX_HEALTH] = 400
fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.9 fields[FieldStore.Entity.MOVEMENT_SPEED] = 0.7
attackCooldownMap[0] = 4000 attackCooldownMap[0] = 4000
attackCooldownMap[1] = 3000 attackCooldownMap[1] = 3000
attackCooldownMap[2] = 500 attackCooldownMap[2] = 500
+8 -9
View File
@@ -1,12 +1,11 @@
extends PlayerBase extends PlayerBase
var parryCounter: CooldownTimer = CooldownTimer.new(3000) var healCounter = CooldownTimer.new(500)
func ai(): func register():
super.ai() super.register()
if parryCounter.isCooldowned(): madeDamage.connect(
var track = getTrackingAnchor() func(_w, _b):
var bullet = BulletTool.findClosetBulletCanDamage(track, get_tree(), self , 300) if healCounter.start():
if is_instance_valid(bullet): heal(1)
BulletBase.generate(ComponentManager.getBullet("Parrier"), self , track, track.angle_to_point(bullet.position)) )
parryCounter.start()
+8
View File
@@ -3,3 +3,11 @@ class_name ParryEffect
func beforeShot(): func beforeShot():
CameraManager.shake(300, 500) 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(): func beforeShot():
CameraManager.shake(50, 200) 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 extends Weapon
class_name BigLaserWeapon 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): func update(to: int, origin: Dictionary, _entity: EntityBase):
origin["atk"] += 5 * to * soulLevel origin["atk"] += 5 * to * soulLevel
origin["time"] /= 1 + 0.05 * to * soulLevel origin["time"] /= 1 + 0.05 * to * soulLevel
@@ -12,4 +57,7 @@ func attack(entity: EntityBase):
var bigLaser: BigLaser = bullet var bigLaser: BigLaser = bullet
bigLaser.dotTime = readStore("time") * 1000 bigLaser.dotTime = readStore("time") * 1000
bigLaser.baseDamage = readStore("atk") bigLaser.baseDamage = readStore("atk")
bigLaser.executeAtk = readStoreExtra("executeAtk")
await TickTool.frame()
bigLaser.animator.speed_scale = 5 / (5 + readStoreExtra("duration"))
return true return true
+54 -8
View File
@@ -1,18 +1,64 @@
@tool @tool
extends Weapon 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): func update(to: int, origin: Dictionary, _entity: EntityBase):
origin["atk"] += 2 * to * soulLevel origin["atk"] += 2 * to * soulLevel
origin["count"] = 1 + 1 * soulLevel origin["count"] = 1 + 1 * soulLevel
return origin return origin
func attack(entity: EntityBase): func attack(entity: EntityBase):
for i in BulletBase.generate(ComponentManager.getBullet("BlueCrystal"), entity, entity.findWeaponAnchor("normal"), deg_to_rad(randf_range(0, 360))): for crystal in BulletBase.generate(ComponentManager.getBullet("BlueCrystal"), entity, entity.findWeaponAnchor("normal"), deg_to_rad(randf_range(0, 360))):
if i is BlueCrystalBullet: if crystal is BlueCrystalBullet:
i.tracer = EntityTool.findClosetEntity(get_global_mouse_position(), get_tree(), !entity.isPlayer(), entity.isPlayer()) crystal.tracer = EntityTool.findClosetEntity(get_global_mouse_position(), get_tree(), !entity.isPlayer(), entity.isPlayer())
i.baseDamage = readStore("atk") crystal.baseDamage = readStore("atk")
crystal.penerate += readStoreExtra("penerate")
crystal.speedBoost = readStoreExtra("speed")
for index in readStore("count"): for index in readStore("count"):
for j in BulletBase.generate(ComponentManager.getBullet("Diamond2"), entity, i.position, deg_to_rad(360.0 / readStore("count") * index)): for diamond in BulletBase.generate(ComponentManager.getBullet("Diamond2"), entity, crystal.position, deg_to_rad(360.0 / readStore("count") * index)):
if j is Diamond2Bullet: if diamond is Diamond2Bullet:
j.baseDamage = readStore("atk") diamond.baseDamage = readStore("atk")
j.parent = i diamond.parent = crystal
diamond.penerate += readStoreExtra("penerate")
return true return true
+50 -4
View File
@@ -1,6 +1,50 @@
@tool @tool
extends Weapon 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): func update(to: int, origin: Dictionary, _entity: EntityBase):
origin["atk"] += 0.1 * to * soulLevel origin["atk"] += 0.1 * to * soulLevel
origin["count"] = 1 * soulLevel origin["count"] = 1 * soulLevel
@@ -9,13 +53,15 @@ func update(to: int, origin: Dictionary, _entity: EntityBase):
func attack(entity: EntityBase): func attack(entity: EntityBase):
entity.takeDamage(readStore("self")) entity.takeDamage(readStore("self"))
var weaponPos = entity.findWeaponAnchor("normal") var weaponPos = entity.findWeaponAnchor("normal")
for i in BulletBase.generate( for bullet in BulletBase.generate(
ComponentManager.getBullet("Bow"), ComponentManager.getBullet("Bow"),
entity, entity,
weaponPos, weaponPos,
weaponPos.angle_to_point(get_global_mouse_position()) weaponPos.angle_to_point(get_global_mouse_position())
): ):
var bullet: Bow = i if bullet is Bow:
bullet.count = readStore("count") bullet.count = readStore("count")
bullet.atk = readStore("atk") bullet.atk = readStore("atk") * (1 + (1 - entity.getHealthPercent()) * readStoreExtra("missAtk"))
bullet.speed *= 1 + readStoreExtra("initSpeed")
bullet.lifesteal = readStoreExtra("lifesteal")
return true return true
+53 -5
View File
@@ -1,15 +1,63 @@
@tool @tool
extends Weapon 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): func update(to, origin, _entity):
origin["atk"] += 1 * to * soulLevel origin["atk"] += 1 * to * soulLevel
origin["count"] = 1 * soulLevel origin["count"] = 1 * soulLevel
origin["split"] /= 1 + 0.005 * to * soulLevel origin["split"] /= 1 + 0.005 * to * soulLevel
return origin return origin
func attack(entity: EntityBase): 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()): for bullet in BulletBase.generate(ComponentManager.getBullet("ChainGun"), entity, entity.texture.global_position, (get_global_mouse_position() - entity.texture.global_position).angle()):
if i is BulletBase: if bullet is ChainGunBullet:
i.baseDamage = readStore("atk") bullet.baseDamage = readStore("atk")
i.count = floor(readStore("count")) bullet.count = floor(readStore("count"))
i.splits = readStore("split") bullet.splits = readStore("split")
bullet.penerate += readStoreExtra("penerate")
bullet.cycleRate = readStoreExtra("cycleRate")
bullet.cycleCount = readStoreExtra("cycleCount")
return true return true
+46 -1
View File
@@ -1,6 +1,49 @@
@tool @tool
extends Weapon 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): func update(to: int, origin: Dictionary, _entity: EntityBase):
origin["atk"] += 1 * to * soulLevel origin["atk"] += 1 * to * soulLevel
origin["rotate"] += 0.5 * to * soulLevel origin["rotate"] += 0.5 * to * soulLevel
@@ -16,5 +59,7 @@ func attack(entity: EntityBase):
if bullet is CogwheelBullet: if bullet is CogwheelBullet:
bullet.initialRotate = readStore("rotate") bullet.initialRotate = readStore("rotate")
bullet.rotateSpeed = 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 return true
+8 -6
View File
@@ -131,20 +131,20 @@ func setupCuttable(cutSpeed: float):
func(body): func(body):
var entity = EntityTool.fromHurtbox(body) var entity = EntityTool.fromHurtbox(body)
if entity: if entity:
speedScale = cutSpeed animator.speed_scale *= cutSpeed
) )
area_exited.connect( area_exited.connect(
func(body): func(body):
var entity = EntityTool.fromHurtbox(body) var entity = EntityTool.fromHurtbox(body)
if entity: if entity:
speedScale = 1 animator.speed_scale /= cutSpeed
) )
func getDamage(): func getDamage():
return baseDamage * damageMultipliers[usingDamageMultiplier] 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 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) var damage = baseDmg + baseDmg * int(crit) * launcher.fields.get(FieldStore.Entity.CRIT_DAMAGE)
return damage return damageOverride(damage, something)
func determineCrit(): func determineCrit():
return MathTool.rate(launcher.fields.get(FieldStore.Entity.CRIT_RATE) + GameRule.critRateInfluenceByLuckValue * launcher.fields[FieldStore.Entity.LUCK_VALUE]) return MathTool.rate(launcher.fields.get(FieldStore.Entity.CRIT_RATE) + GameRule.critRateInfluenceByLuckValue * launcher.fields[FieldStore.Entity.LUCK_VALUE])
func hitEntity(target: Node): func hitEntity(target: Node):
@@ -161,7 +161,7 @@ func hitObstacle(target: Node):
var obstacle = target as ObstacleBase var obstacle = target as ObstacleBase
if is_instance_valid(obstacle.launcher): if is_instance_valid(obstacle.launcher):
if not BulletTool.canDamage(self , obstacle.launcher): return if not BulletTool.canDamage(self , obstacle.launcher): return
obstacle.takeDamage(calculateDamage(determineCrit())) obstacle.takeDamage(calculateDamage(determineCrit(), target))
if MathTool.rate(fullPenerate() - obstacle.penerateResistance): if MathTool.rate(fullPenerate() - obstacle.penerateResistance):
baseDamage *= 1.0 - penerateDamageReduction baseDamage *= 1.0 - penerateDamageReduction
else: else:
@@ -257,6 +257,8 @@ func refract(newBullet: BulletBase, _entity: EntityBase, _index: int, _total: in
return newBullet return newBullet
func hitBullet(_bullet: BulletBase): func hitBullet(_bullet: BulletBase):
pass pass
func damageOverride(origin: float, _something: Variant):
return origin
static func generate( static func generate(
bullet: PackedScene, bullet: PackedScene,
@@ -275,7 +277,7 @@ static func generate(
instance.position = spawnPosition instance.position = spawnPosition
instance.rotation = spawnRotation + deg_to_rad(launchBy.fields.get(FieldStore.Entity.OFFSET_SHOOT) * randf_range(-1, 1) * int(!ignoreOffset)) instance.rotation = spawnRotation + deg_to_rad(launchBy.fields.get(FieldStore.Entity.OFFSET_SHOOT) * randf_range(-1, 1) * int(!ignoreOffset))
if addToWorld: if addToWorld:
WorldManager.rootNode.call_deferred("add_child", instance) WorldManager.rootNode.add_child.call_deferred(instance)
instance.add_to_group("bullets") instance.add_to_group("bullets")
instances.append(instance) instances.append(instance)
return instances return instances
+1 -1
View File
@@ -17,7 +17,7 @@ func getStateAngle(index: int):
func forceFilter(): func forceFilter():
bullets = bullets.filter( bullets = bullets.filter(
func(b): func(b):
if b is BulletBase: if is_instance_valid(b) && b is BulletBase:
return !b.destroying return !b.destroying
else: else:
return false return false
+8 -5
View File
@@ -6,6 +6,7 @@ signal healed(amount: float)
signal healthChanged(health: float) signal healthChanged(health: float)
signal died() signal died()
signal killEnemy(who: EntityBase, by: BulletBase) signal killEnemy(who: EntityBase, by: BulletBase)
signal madeDamage(who: EntityBase, by: BulletBase)
signal energyChanged(energy: float, dontChangeDirection: bool) signal energyChanged(energy: float, dontChangeDirection: bool)
@@ -46,7 +47,7 @@ var fields = {
FieldStore.Entity.CRIT_DAMAGE: 1, FieldStore.Entity.CRIT_DAMAGE: 1,
"概率": TITLE_FLAG, "概率": TITLE_FLAG,
FieldStore.Entity.LUCK_VALUE: 1, FieldStore.Entity.LUCK_VALUE: 1,
"饲料": TITLE_FLAG, "增益": TITLE_FLAG,
FieldStore.Entity.PRICE_REDUCTION: 0, FieldStore.Entity.PRICE_REDUCTION: 0,
FieldStore.Entity.FEED_COUNT_SHOW: 3, FieldStore.Entity.FEED_COUNT_SHOW: 3,
FieldStore.Entity.FEED_COUNT_CAN_MADE: 5, FieldStore.Entity.FEED_COUNT_CAN_MADE: 5,
@@ -69,7 +70,7 @@ var inventory = {
ItemStore.ItemType.BEACHBALL: 0, ItemStore.ItemType.BEACHBALL: 0,
ItemStore.ItemType.SOUL: 0, ItemStore.ItemType.SOUL: 0,
ItemStore.ItemType.CRYSTAL: 0, ItemStore.ItemType.CRYSTAL: 0,
ItemStore.ItemType.DIAMOND: 0 ItemStore.ItemType.DIAMOND: 100
} }
var inventoryMax = { var inventoryMax = {
ItemStore.ItemType.BASEBALL: INF, # 无限 ItemStore.ItemType.BASEBALL: INF, # 无限
@@ -171,7 +172,8 @@ func _ready():
) )
healthChanged.emit(health) healthChanged.emit(health)
energyChanged.emit(energy, false) energyChanged.emit(energy, false)
killEnemy.connect(func(): return ) killEnemy.connect(func(_w, _b): return )
madeDamage.connect(func(_w, _b): return )
spawn() spawn()
func _process(_delta): func _process(_delta):
health = clamp(health, 0, fields.get(FieldStore.Entity.MAX_HEALTH)) 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: if health <= 0:
tryDie(null) tryDie(null)
return resultDamage return resultDamage
func bulletHit(bullet: BulletBase, crit: bool): func bulletHit(bullet: BulletBase, crit: bool, damageOverride = "none"):
# 当受伤时 # 当受伤时
hurtAnimator.play("hurt") hurtAnimator.play("hurt")
var damage = bullet.calculateDamage(crit) var damage = bullet.calculateDamage(crit, self ) if damageOverride == "none" else damageOverride
var perfectMiss = false var perfectMiss = false
if sprinting: if sprinting:
playSound("miss") playSound("miss")
@@ -304,6 +306,7 @@ func bulletHit(bullet: BulletBase, crit: bool):
hit.emit(damage, bullet, crit) hit.emit(damage, bullet, crit)
health -= damage health -= damage
healthChanged.emit(health) healthChanged.emit(health)
bullet.launcher.madeDamage.emit(self , bullet)
DamageLabel.create(damage, crit || perfectMiss, damageAnchor.global_position + MathTool.sampleInCircle(GameRule.damageLabelSpawnOffset)) DamageLabel.create(damage, crit || perfectMiss, damageAnchor.global_position + MathTool.sampleInCircle(GameRule.damageLabelSpawnOffset))
if isBoss and bullet.launcher.isPlayer(): if isBoss and bullet.launcher.isPlayer():
bullet.launcher.setBoss(self ) bullet.launcher.setBoss(self )
+2 -2
View File
@@ -2,7 +2,7 @@
extends HBoxContainer extends HBoxContainer
class_name FeedName class_name FeedName
@export var displayName: String = "未命名饲料" @export var displayName: String = "未命名"
@export var quality: CategoryStore.Quality = CategoryStore.Quality.COMMON @export var quality: CategoryStore.Quality = CategoryStore.Quality.COMMON
@export var topic: CategoryStore.Topic = CategoryStore.Topic.SURVIVAL @export var topic: CategoryStore.Topic = CategoryStore.Topic.SURVIVAL
@export var qualityColorMap = { @export var qualityColorMap = {
@@ -40,7 +40,7 @@ class_name FeedName
CategoryStore.Topic.SPEED: "速度", CategoryStore.Topic.SPEED: "速度",
CategoryStore.Topic.DAMAGE: "伤害", CategoryStore.Topic.DAMAGE: "伤害",
CategoryStore.Topic.PROBABILITY: "幸运", CategoryStore.Topic.PROBABILITY: "幸运",
CategoryStore.Topic.FEED: "饲料", CategoryStore.Topic.FEED: "增益",
CategoryStore.Topic.DROP: "掉落物", CategoryStore.Topic.DROP: "掉落物",
CategoryStore.Topic.WEAPON: "武器", CategoryStore.Topic.WEAPON: "武器",
CategoryStore.Topic.SUMMON: "召唤", CategoryStore.Topic.SUMMON: "召唤",
+2
View File
@@ -44,6 +44,8 @@ func takeDamage(damage: float):
func tryDie(): func tryDie():
die() die()
queue_free() queue_free()
func getHealthPercent():
return health / healthMax
func die(): func die():
pass pass
+1 -1
View File
@@ -5,7 +5,7 @@ class_name Feed
signal selected(applied: bool) signal selected(applied: bool)
@export var avatarTexture: Texture2D = null @export var avatarTexture: Texture2D = null
@export var displayName: String = "未命名饲料" @export var displayName: String = "未命名"
@export var quality: CategoryStore.Quality = CategoryStore.Quality.COMMON @export var quality: CategoryStore.Quality = CategoryStore.Quality.COMMON
@export var topic: CategoryStore.Topic = CategoryStore.Topic.SURVIVAL @export var topic: CategoryStore.Topic = CategoryStore.Topic.SURVIVAL
@export var fields: Array[FieldStore.Entity] = [] @export var fields: Array[FieldStore.Entity] = []
+1 -2
View File
@@ -241,7 +241,7 @@ func rebuildSublimateOptions(showNext: bool):
sublimateOptionsBox.add_child(instance) sublimateOptionsBox.add_child(instance)
disruptSublimateOptions() disruptSublimateOptions()
func readStore(key: String): 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): func playSound(sound: String):
var body = sounds.get_node_or_null(sound) var body = sounds.get_node_or_null(sound)
if body is AudioStreamPlayer2D: if body is AudioStreamPlayer2D:
@@ -284,7 +284,6 @@ func addStoreExtra(key: String, value: float):
if !storeExtra.has(key): if !storeExtra.has(key):
storeExtra[key] = 0 storeExtra[key] = 0
storeExtra[key] += value storeExtra[key] += value
storeExtra[key] = clamp(storeExtra[key], 0, INF)
func readStoreExtra(key: String): func readStoreExtra(key: String):
return storeExtra.get(key, 0) return storeExtra.get(key, 0)
func getSublimateOptions() -> Array[SublimateOption]: func getSublimateOptions() -> Array[SublimateOption]:
+1 -1
View File
@@ -56,7 +56,7 @@ static var topicNameMap = {
Topic.SPEED: "速度", Topic.SPEED: "速度",
Topic.DAMAGE: "伤害", Topic.DAMAGE: "伤害",
Topic.PROBABILITY: "幸运", Topic.PROBABILITY: "幸运",
Topic.FEED: "饲料", Topic.FEED: "增益",
Topic.DROP: "掉落物", Topic.DROP: "掉落物",
Topic.WEAPON: "武器", Topic.WEAPON: "武器",
Topic.SUMMON: "召唤", Topic.SUMMON: "召唤",
+6 -2
View File
@@ -116,15 +116,19 @@ static var entityMinValueMap = {
Entity.DAMAGE_MULTIPILER: 0.01 Entity.DAMAGE_MULTIPILER: 0.01
} }
static var entityApplier = { static var entityApplier = {
Entity.MAX_HEALTH: func(entity: EntityBase, value): Entity.MAX_HEALTH: func(entity: EntityBase, value: float):
entity.health += value entity.health += value
entity.statebar.forceSync() entity.statebar.forceSync()
return true return true
, ,
Entity.EXTRA_APPLE_MAX: func(entity, value): Entity.EXTRA_APPLE_MAX: func(entity: EntityBase, value: float):
entity.inventoryMax[ItemStore.ItemType.APPLE] += value entity.inventoryMax[ItemStore.ItemType.APPLE] += value
return true return true
, ,
Entity.MAX_ENERGY: func(entity: EntityBase, value: float):
entity.energy += value
return true
,
} }
static var entityViewCastMap = { static var entityViewCastMap = {
Entity.EXTRA_APPLE_MAX: func(entity, _value): Entity.EXTRA_APPLE_MAX: func(entity, _value):
-3
View File
@@ -1,3 +0,0 @@
# Todo
1. 完成Godockly UGC框架。