1
1
mirror of https://github.com/Rundll86/Dog-Lynx-And-HCN.git synced 2026-05-28 06:51:54 +08:00

feat(障碍物系统): 添加草墙障碍物及相关功能

实现草墙障碍物系统,包括以下主要变更:
- 新增草墙障碍物资源、脚本和场景
- 添加障碍物状态显示UI
- 扩展组件管理器支持障碍物类型
- 修改子弹系统以支持对障碍物的碰撞检测
- 调整实体碰撞层设置
- 为公鸡角色添加草墙武器

新增功能允许玩家放置可阻挡敌人的草墙障碍物,并显示其生命值状态
This commit is contained in:
2026-01-27 20:52:26 +08:00
parent 2f2a3fd04b
commit 3698127345
22 changed files with 400 additions and 18 deletions
+21 -5
View File
@@ -44,7 +44,8 @@ func _ready():
launcherSummoned = launcher
launcher = launcher.myMaster
register()
area_entered.connect(hit)
area_entered.connect(hitEntity)
body_entered.connect(hitObstacle)
spawnInWhen = WorldManager.getTime()
spawnInWhere = position
spawn()
@@ -107,16 +108,31 @@ func setupCuttable(cutSpeed: float):
)
func getDamage():
return baseDamage * damageMultipliers[usingDamageMultiplier]
func hit(target: Node):
func calculateDamage(crit: bool):
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
func determineCrit():
return MathTool.rate(launcher.fields.get(FieldStore.Entity.CRIT_RATE) + GameRule.critRateInfluenceByLuckValue * launcher.fields[FieldStore.Entity.LUCK_VALUE])
func hitEntity(target: Node):
var entity: EntityBase = EntityTool.fromHurtbox(target)
if !BulletTool.canDamage(self, entity): return
var resultDamage = entity.bulletHit(self, MathTool.rate(launcher.fields.get(FieldStore.Entity.CRIT_RATE) + GameRule.critRateInfluenceByLuckValue * launcher.fields[FieldStore.Entity.LUCK_VALUE]))
var resultDamage = entity.bulletHit(self, determineCrit())
succeedToHit(resultDamage, entity)
if MathTool.rate(fullPenerate()):
penerate -= entity.fields[FieldStore.Entity.PENARATION_RESISTANCE]
if MathTool.rate(fullPenerate() - entity.fields[FieldStore.Entity.PENARATION_RESISTANCE]):
baseDamage *= 1.0 - penerateDamageReduction
else:
tryDestroy()
func hitObstacle(target: Node):
if target is ObstacleBase:
var obstacle = target as ObstacleBase
if is_instance_valid(obstacle.launcher):
if not BulletTool.canDamage(self, obstacle.launcher): return
obstacle.takeDamage(calculateDamage(determineCrit()))
if MathTool.rate(fullPenerate() - obstacle.penerateResistance):
baseDamage *= 1.0 - penerateDamageReduction
else:
tryDestroy()
func forward(direction: Vector2):
position += direction.normalized() * speed * GameRule.bulletSpeedMultiplier
func fullPenerate():
+9 -2
View File
@@ -8,6 +8,10 @@ signal died()
signal energyChanged(energy: float, dontChangeDirection: bool)
enum Layers {
PLAYER = 1 << 2,
ENEMY = 1 << 1,
}
const TITLE_FLAG = INF
var fields = {
"生存": TITLE_FLAG,
@@ -143,8 +147,12 @@ func _ready():
)
if displayName == MultiplayerState.playerName:
rebuildWeaponIcons()
collision_layer = Layers.PLAYER
collision_mask = Layers.PLAYER
else:
applyLevel()
collision_layer = Layers.ENEMY
collision_mask = Layers.ENEMY
health = fields.get(FieldStore.Entity.MAX_HEALTH)
energy = fields.get(FieldStore.Entity.MAX_ENERGY)
if is_instance_valid(statebar):
@@ -242,8 +250,7 @@ func takeDamage(baseDamage: float, crit: bool = false, perfectMiss: bool = false
func bulletHit(bullet: BulletBase, crit: bool):
# 当受伤时
hurtAnimator.play("hurt")
var baseDamage: float = bullet.getDamage() * bullet.launcher.fields.get(FieldStore.Entity.DAMAGE_MULTIPILER) * randf_range(1 - GameRule.damageOffset, 1 + GameRule.damageOffset)
var damage = baseDamage + baseDamage * int(crit) * fields.get(FieldStore.Entity.CRIT_DAMAGE)
var damage = bullet.calculateDamage(crit)
var perfectMiss = false
if sprinting:
playSound("miss")
+1 -1
View File
@@ -1,4 +1,4 @@
extends Node
extends CanvasItem
class_name EntityStateBar
@export var entity: EntityBase
+64
View File
@@ -0,0 +1,64 @@
extends StaticBody2D
class_name ObstacleBase
signal healthChanged(newHealth: float)
@export var healthMax: float = 100
@export var penerateResistance: float = 0
@export var blockPlayer: bool = false
@export var blockEnemy: bool = false
@onready var statebar: ObstacleStateBar = $%statebar
@onready var texture: AnimatedSprite2D = $%texture
@onready var hitbox: CollisionShape2D = $%hitbox
var health: float = 0
var launcher: EntityBase = null
func _ready():
health = healthMax
if blockPlayer:
collision_layer |= EntityBase.Layers.PLAYER
collision_mask |= EntityBase.Layers.PLAYER
if blockEnemy:
collision_layer |= EntityBase.Layers.ENEMY
collision_mask |= EntityBase.Layers.ENEMY
healthChanged.connect(
func(newHealth):
statebar.healthBar.maxValue = healthMax
statebar.healthBar.setCurrent(newHealth)
)
func _process(_delta):
statebar.global_rotation = 0
statebar.applyText()
func initHealth(maxHealth: float):
healthMax = maxHealth
health = healthMax
statebar.forceSync()
statebar.applyText()
func takeDamage(damage: float):
health -= damage
healthChanged.emit(health)
if health <= 0:
tryDie()
func tryDie():
die()
queue_free()
func die():
pass
static func generate(
obstacle: PackedScene,
spawnPosition: Vector2,
spawnRotation: float = 0,
itsLauncher: EntityBase = null,
addToWorld: bool = true
) -> ObstacleBase:
var instance: ObstacleBase = obstacle.instantiate()
instance.position = spawnPosition
instance.rotation = spawnRotation
instance.launcher = itsLauncher
if addToWorld:
WorldManager.rootNode.spawn(instance)
return instance
+1
View File
@@ -0,0 +1 @@
uid://bj26s4i47bw0a
+17
View File
@@ -0,0 +1,17 @@
extends Node2D
class_name ObstacleStateBar
@export var obstacle: ObstacleBase
@onready var healthBar: ColorBar = $%health
@onready var currentLabel: Label = $%current
@onready var maxLabel: Label = $%max
@onready var levelLabelContainer: HBoxContainer = $%levelLabel
func forceSync():
healthBar.maxValue = obstacle.healthMax
healthBar.currentValue = obstacle.health
healthBar.forceSync()
func applyText():
currentLabel.text = "%d" % obstacle.health
maxLabel.text = "%d" % obstacle.healthMax
@@ -0,0 +1 @@
uid://dkt4yqm0juwic