1
1
mirror of https://github.com/Rundll86/Dog-Lynx-And-HCN.git synced 2026-05-28 06:51:54 +08:00
Files
Dog-Lynx-And-HCN/scripts/Structs/Weapon.gd
T
fallingshrimp e8ee2932bb feat(游戏机制): 调整掉落物品数量和添加错误提示
增加boss和非boss敌人死亡时掉落水晶的数量范围
为武器镶嵌/移除灵魂操作添加错误提示
移除Starter场景中不必要的属性设置
新增CharacterCardBase基础组件
2026-05-04 18:14:21 +08:00

256 lines
7.9 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@tool
extends PanelContainer
class_name Weapon
enum EmitType {
HOLD_SHOOT,
CLICK_SHOOT,
CHARGE,
HOLD_LOOP
}
@export var avatarTexture: Texture2D = null
@export var displayName: String = "未命名武器"
@export var quality: WeaponName.Quality = WeaponName.Quality.COMMON
@export var typeTopic: WeaponName.TypeTopic = WeaponName.TypeTopic.IMPACT
@export var soulLevel: int = 1
@export var costBeachball: int = 500
@export var emitType: EmitType = EmitType.HOLD_SHOOT
@export var store: Dictionary = {
"atk": 10
}
@export var storeType: Dictionary = {
"atk": FieldStore.DataType.INTEGER
}
@export_multiline var descriptionTemplate: String = "造成$atk点伤害。"
@export var sources: Array[String] = []
@export var tease: String = ""
@export var needEnergy: float = 0
@export var cooldown: float = 100
@export var debugRebuild: bool = false
@export var level: int = 0
@onready var avatarRect: TextureRect = $%avatar
@onready var nameLabel: WeaponName = $%name
@onready var sourceLabel: Label = $%source
@onready var teaseLabel: Label = $%tease
@onready var energyLabel: Label = $%energy
@onready var beachball: ItemShow = $%beachball
@onready var soul: ItemShow = $%soul
@onready var descriptionLabel: RichTextLabel = $%description
@onready var sounds: Node2D = $%sounds
@onready var moveLeftBtn: Button = $%moveleft
@onready var moveRightBtn: Button = $%moveright
@onready var autoUpdateBtn: Button = $%autoUpdateBtn
@onready var onceUpdateBtn: Button = $%onceUpdateBtn
@onready var updateBtn: Button = $%updateBtn
@onready var extractBtn: Button = $%extractBtn
@onready var inlayBtn: Button = $%inlayBtn
var cooldownTimer: CooldownTimer = null
var originalStore: Dictionary = {}
var chargedTime: float = 0
var attackSpeed: float = 1
var looping: bool = false
var autoUpdate: bool = false
func _ready():
cooldownTimer = CooldownTimer.new()
cooldownTimer.cooldown = cooldown
originalStore = store
autoUpdateBtn.toggled.connect(
func(on: bool):
autoUpdate = on
)
onceUpdateBtn.pressed.connect(
func():
var count = 0
while canUpdate(UIState.player):
updateApply(UIState.player)
count += 1
if count > 0:
UIState.showTip("一键强化提升了[b]%d[/b]级!" % count)
else:
UIState.showTip("一键强化没有提升等级......", TipBox.MessageType.ERROR)
)
updateBtn.pressed.connect(
func():
updateApply(UIState.player)
)
extractBtn.pressed.connect(
func():
if soulLevel > WeaponName.SoulLevel.NORMALIZE:
UIState.player.getItem({
ItemStore.ItemType.SOUL: soulLevel - 1
})
soulLevel -= 1
updateStore(level, UIState.player)
rebuildInfo()
else:
UIState.showTip("[b]%s[/b]还未镶嵌任何灵魂!" % displayName, TipBox.MessageType.ERROR)
)
inlayBtn.pressed.connect(
func():
if soulLevel < WeaponName.SoulLevel.INFINITY:
if UIState.player.useItem({
ItemStore.ItemType.SOUL: soulLevel
}):
soulLevel += 1
updateStore(level, UIState.player)
rebuildInfo()
else:
UIState.showTip("持有的灵魂数量不足!", TipBox.MessageType.ERROR)
else:
UIState.showTip("[b]%s[/b]的灵魂槽位已满!" % displayName, TipBox.MessageType.ERROR)
)
moveLeftBtn.pressed.connect(
func():
if get_parent():
var myIndex = get_index()
var leftIndex = max(myIndex - 1, 0)
get_parent().move_child(self , leftIndex)
ArrayTool.swap(UIState.player.weapons, myIndex, leftIndex)
UIState.player.rebuildWeaponIcons()
)
moveRightBtn.pressed.connect(
func():
if get_parent():
var myIndex = get_index()
var rightIndex = min(myIndex + 1, get_parent().get_child_count() - 1)
get_parent().move_child(self , rightIndex)
ArrayTool.swap(UIState.player.weapons, myIndex, rightIndex)
UIState.player.rebuildWeaponIcons()
)
for i in sounds.get_children():
i.process_mode = ProcessMode.PROCESS_MODE_ALWAYS
debugRebuild = false # 只能在编辑器里打开
func _physics_process(_delta):
if debugRebuild:
rebuildInfo()
func canUpdate(entity: EntityBase):
return entity.hasItem({ItemStore.ItemType.BEACHBALL: costBeachball})
func canInlay():
return UIState.player.hasItem({ItemStore.ItemType.SOUL: soulLevel})
func updateApply(entity: EntityBase) -> bool:
if canUpdate(entity):
level += 1
entity.inventory[ItemStore.ItemType.BEACHBALL] -= costBeachball
updateStore(level, entity)
costBeachball = floor(GameRule.weaponUpdateCost * costBeachball)
rebuildInfo(true)
return true
else:
UIState.showTip("沙滩球不足!", TipBox.MessageType.ERROR)
return false
func updateStore(to: int, entity: EntityBase):
store = update(to, originalStore.duplicate(), entity)
func multipiler() -> float:
if is_instance_valid(UIState.player):
return 1 - UIState.player.fields.get(FieldStore.Entity.PRICE_REDUCTION)
else:
return 1
func rebuildInfo(showNext: bool = false):
avatarRect.texture = avatarTexture
nameLabel.displayName = displayName
nameLabel.quality = quality
nameLabel.typeTopic = typeTopic
nameLabel.soulLevel = soulLevel
nameLabel.level = level
sourceLabel.text = " × ".join(sources)
if len(tease) > 0:
teaseLabel.text = "%s" % tease
teaseLabel.show()
else:
teaseLabel.hide()
energyLabel.text = "%.1f" % needEnergy
beachball.count = costBeachball
soul.count = soulLevel
if is_instance_valid(UIState.player):
beachball.enough = canUpdate(UIState.player)
soul.enough = canInlay()
descriptionLabel.text = buildDescription(showNext && (canUpdate(UIState.player) || canInlay()))
func formatValue(value: Variant, type: FieldStore.DataType) -> String:
if type == FieldStore.DataType.VALUE:
return "%.2f" % value
elif type == FieldStore.DataType.INTEGER:
return "%d" % value
elif type == FieldStore.DataType.PERCENT:
return ("%.1f" % (value * 100)) + "%"
elif type == FieldStore.DataType.ANGLE:
return "%.1f°" % value
elif type == FieldStore.DataType.FREQUENCY:
return "%.1fHz" % value
else:
return str(value)
func buildDescription(showNext: bool = false) -> String:
var current = store
var next = update(level + 1, originalStore.duplicate(), UIState.player)
var result = descriptionTemplate
for key in store.keys():
var data = current[key]
var nextData = next[key]
var type = storeType.get(key, FieldStore.DataType.VALUE)
data = formatValue(data, type)
nextData = formatValue(nextData, type)
var text
if showNext:
text = "[color=cyan]%s[/color]→[color=yellow]%s[/color]" % [data, nextData]
else:
text = "[color=cyan]%s[/color]" % data
result = result.replace("$" + key, text)
return "[center]%s[/center]" % result
func readStore(key: String, default: Variant = null):
return store.get(key, default)
func playSound(sound: String):
var body = sounds.get_node_or_null(sound)
if body is AudioStreamPlayer2D:
var cloned = body.duplicate() as AudioStreamPlayer2D
add_child(cloned)
cloned.play()
await cloned.finished
cloned.queue_free()
func canAttackBy(entity: EntityBase):
cooldownTimer.speedScale = entity.fields.get(FieldStore.Entity.ATTACK_SPEED) * attackSpeed
return cooldownTimer.isCooldowned() and entity.isEnergyEnough(needEnergy) and checkAttack(entity)
func tryAttack(entity: EntityBase):
if looping:
if checkAttack(entity):
return await attack(entity)
else:
exitLoop(entity)
else:
if canAttackBy(entity):
if emitType == EmitType.HOLD_LOOP:
var result = await loopStart(entity)
if result:
looping = true
cooldownTimer.start()
entity.useEnergy(needEnergy)
return result
else:
var result = await attack(entity)
if result:
cooldownTimer.start()
entity.useEnergy(needEnergy)
return result
func charged(base: float, percent: float):
return base * sqrt(1 + chargedTime / 15 * percent)
func exitLoop(entity: EntityBase):
if !looping: return
looping = false
loopExit(entity)
# 抽象
func update(_to: int, origin: Dictionary, _entity: EntityBase):
return origin
func loopStart(_entity: EntityBase):
pass
func checkAttack(_entity: EntityBase) -> bool:
return true
func attack(_entity: EntityBase):
pass
func loopExit(_entity: EntityBase):
pass