1
1
mirror of https://github.com/Rundll86/Dog-Lynx-And-HCN.git synced 2026-07-09 19:42:54 +08:00

Compare commits

...

13 Commits

Author SHA1 Message Date
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
35 changed files with 394 additions and 102 deletions
+5 -5
View File
@@ -85,7 +85,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(150, -0.25, 0, 0.25, 0, 500, -0.083333336, -8.333334, 0, 0, 150, -0.01666667, 83.333336, 0, 0),
"times": PackedFloat32Array(0, 0.2, 0.3)
}
tracks/1/type = "bezier"
@@ -183,8 +183,8 @@ _data = {
&"spawn": SubResource("Animation_o5h0y")
}
[sub_resource type="CircleShape2D" id="CircleShape2D_1sgli"]
radius = 78.05767
[sub_resource type="RectangleShape2D" id="RectangleShape2D_1sgli"]
size = Vector2(23.999994, 124)
[node name="Volcano" unique_id=2046549391 instance=ExtResource("1_gd3m7")]
script = ExtResource("2_w1utg")
@@ -210,9 +210,9 @@ texture = ExtResource("2_wwxm2")
[node name="audio" type="AudioStreamPlayer2D" parent="texture/anchor/textureSword" index="0" unique_id=167399724]
[node name="hitbox" parent="." index="1" unique_id=175349408 node_paths=PackedStringArray("target")]
position = Vector2(299.99997, 0.1221919)
position = Vector2(149.99998, 0.06109595)
rotation = 0.00040731629
shape = SubResource("CircleShape2D_1sgli")
shape = SubResource("RectangleShape2D_1sgli")
script = ExtResource("7_1sgli")
target = NodePath("../texture/anchor/textureSword")
offsetRotation = 45.0
+2 -3
View File
@@ -21,9 +21,8 @@ displayName = "氰化氢"
slogan = "微量便可颠覆生机"
avatar = ExtResource("2_1yfsg")
description = "击杀敌人时储能。"
fields = Array[int]([18, 1])
fieldValues = Array[float]([3.0, -0.15])
clickToRebuild = true
fields = Array[int]([18, 10, 1])
fieldValues = Array[float]([3.0, 0.15, -0.15])
[node name="avatarTexture" parent="wrapper" parent_id_path=PackedInt32Array(2023039659) index="0" unique_id=1334645594]
texture = ExtResource("2_1yfsg")
+2 -2
View File
@@ -21,7 +21,7 @@ slogan = "恪守使命不离不弃"
avatar = ExtResource("2_fi2nw")
description = "每隔3秒发动格挡。"
fields = Array[int]([0, 8, 3])
fieldValues = Array[float]([50.0, 0.15, -0.2])
fieldValues = Array[float]([50.0, 0.15, -0.35])
[node name="avatarTexture" parent="wrapper" parent_id_path=PackedInt32Array(2023039659) index="0" unique_id=1334645594]
texture = ExtResource("2_fi2nw")
@@ -33,4 +33,4 @@ text = "牧羊犬"
text = "“恪守使命不离不弃”"
[node name="descriptionLabel" parent="wrapper/infoContainer" parent_id_path=PackedInt32Array(143242635) index="1" unique_id=808054282]
text = "每3秒发动格挡。"
text = "每3秒发动格挡。"
+3
View File
@@ -3,9 +3,12 @@
[ext_resource type="PackedScene" uid="uid://bs863g2s8r770" path="res://components/Abstracts/PlayerBase.tscn" id="1_eeneu"]
[ext_resource type="Script" uid="uid://bevc4f6apql4t" path="res://scripts/Contents/Characters/HCN.gd" id="2_f7uj3"]
[ext_resource type="PackedScene" uid="uid://yq7vmijwvgx1" path="res://components/Weapons/AcidWind.tscn" id="3_2fpmn"]
[ext_resource type="PackedScene" uid="uid://cxabqjo7skxev" path="res://components/Weapons/BigLaser.tscn" id="4_4gd8a"]
[node name="HCN" unique_id=1711205167 instance=ExtResource("1_eeneu")]
script = ExtResource("2_f7uj3")
displayName = "氰化氢"
[node name="AcidWind" parent="weaponStore" parent_id_path=PackedInt32Array(1319091445) index="0" unique_id=1599717188 instance=ExtResource("3_2fpmn")]
[node name="BigLaser" parent="weaponStore" parent_id_path=PackedInt32Array(1319091445) index="1" unique_id=906291376 instance=ExtResource("4_4gd8a")]
+2 -2
View File
@@ -2,10 +2,10 @@
[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://cx7nogfnv7s8t" path="res://components/Weapons/Tree.tscn" id="4_im5m5"]
[ext_resource type="PackedScene" uid="uid://u0djqwuuysp8" path="res://components/Weapons/Volcano.tscn" id="3_e236u"]
[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")]
+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:
+3 -2
View File
@@ -16,8 +16,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() * 500)
ParryEntityEffect.createEffectEntity(entity, self )
func generateShadow():
var startAngle = rotation - deg_to_rad(count * splitAngle / 2)
+1 -1
View File
@@ -3,6 +3,6 @@ extends PlayerBase
func register():
super.register()
killEnemy.connect(
func(_who, _by):
func(_w, _b):
storeEnergy(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
+6 -4
View File
@@ -141,10 +141,10 @@ func setupCuttable(cutSpeed: float):
)
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
+5 -5
View File
@@ -46,7 +46,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 +69,7 @@ var inventory = {
ItemStore.ItemType.BEACHBALL: 0,
ItemStore.ItemType.SOUL: 0,
ItemStore.ItemType.CRYSTAL: 0,
ItemStore.ItemType.DIAMOND: 0
ItemStore.ItemType.DIAMOND: 10
}
var inventoryMax = {
ItemStore.ItemType.BASEBALL: INF, # 无限
@@ -171,7 +171,7 @@ func _ready():
)
healthChanged.emit(health)
energyChanged.emit(energy, false)
killEnemy.connect(func(): return )
killEnemy.connect(func(_w, _b): return )
spawn()
func _process(_delta):
health = clamp(health, 0, fields.get(FieldStore.Entity.MAX_HEALTH))
@@ -282,10 +282,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")
+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: "召唤",