mirror of
https://github.com/Rundll86/Dog-Lynx-And-HCN.git
synced 2026-06-01 17:01:53 +08:00
feat: 添加新武器彩虹旗和紫水晶簇,调整饲料属性和数值
refactor: 重构子弹生成逻辑,支持分裂和折射效果 fix: 修复掉落物拾取范围和碰撞检测问题 style: 优化UI显示,添加武器品质和类型标签 docs: 更新字段描述,调整部分饲料名称和分类 perf: 优化数学工具函数,添加随机数处理工具 test: 调整波次生成逻辑,添加新敌人类型 build: 添加新资源文件和相关导入配置
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
extends BulletBase
|
||||
class_name LGBTBullet
|
||||
|
||||
var myTracer: EntityBase = null
|
||||
|
||||
func spawn():
|
||||
findTracer()
|
||||
func register():
|
||||
speed = 1
|
||||
damage = 5
|
||||
func ai():
|
||||
texture.rotation_degrees += speed
|
||||
speed *= 1.05
|
||||
speed = clamp(speed, 0, 20)
|
||||
if is_instance_valid(myTracer):
|
||||
PresetAIs.trace(self, myTracer.position, clamp(speed / 150, 0, 1))
|
||||
else:
|
||||
findTracer()
|
||||
PresetAIs.forward(self, rotation)
|
||||
func findTracer():
|
||||
myTracer = EntityTool.findClosetEntity(position, get_tree(), false, true)
|
||||
@@ -5,3 +5,20 @@ func ai():
|
||||
PresetAIs.forward(self, rotation)
|
||||
func destroy(_beacuseMap: bool):
|
||||
EffectController.create(preload("res://components/Effects/PurpleCrystalExplosion.tscn"), global_position).shot()
|
||||
func split(index, total, _last):
|
||||
BulletBase.generate(
|
||||
preload("res://components/Bullets/PurpleCrystal.tscn"),
|
||||
launcher,
|
||||
position,
|
||||
deg_to_rad(360 / total * index),
|
||||
true
|
||||
)
|
||||
func refract(entity, _index, _total, _last):
|
||||
BulletBase.generate(
|
||||
preload("res://components/Bullets/PurpleCrystal.tscn"),
|
||||
launcher,
|
||||
position,
|
||||
position.angle_to_point(entity.position) if is_instance_valid(entity) else randf_range(0, deg_to_rad(360)),
|
||||
false,
|
||||
true
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ func ai():
|
||||
func attack(type):
|
||||
if type == 0:
|
||||
var weaponPos = findWeaponAnchor("normal")
|
||||
return BulletBase.generate(preload("res://components/Bullets/PurpleCrystal.tscn"), self, weaponPos, (get_global_mouse_position() - weaponPos).angle())
|
||||
BulletBase.generate(preload("res://components/Bullets/LGBTBullet.tscn"), self, weaponPos, deg_to_rad(randf_range(0, 360)))
|
||||
elif type == 1:
|
||||
var weaponPos = findWeaponAnchor("normal")
|
||||
return BulletBase.generate(preload("res://components/Bullets/BigLaser.tscn"), self, weaponPos, (get_global_mouse_position() - weaponPos).angle())
|
||||
|
||||
@@ -13,7 +13,7 @@ static var data: Array[Wave] = [
|
||||
# entity, minCount, maxCount, isBoss, from, to, per
|
||||
create(preload("res://components/Characters/Hen.tscn"), 1, 5, false, 0, INF, 1),
|
||||
create(preload("res://components/Characters/Chick.tscn"), 0, 0, true, 8, INF, 6),
|
||||
# create(preload("res://components/Characters/Chick.tscn"), 1, 1, true, 0, INF, 1),
|
||||
create(preload("res://components/Characters/Chick.tscn"), 1, 1, true, 0, INF, 1),
|
||||
]
|
||||
|
||||
static func create(
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
extends Weapon
|
||||
class_name PurpleCrystalWeapon
|
||||
|
||||
func update(to: int, origin: Dictionary, _entity: EntityBase):
|
||||
origin["atk"] += 5 * to
|
||||
return origin
|
||||
func attack(entity: EntityBase):
|
||||
var weaponPos = entity.findWeaponAnchor("normal")
|
||||
BulletBase.generate(preload("res://components/Bullets/PurpleCrystal.tscn"), entity, weaponPos, (get_global_mouse_position() - weaponPos).angle())
|
||||
@@ -25,6 +25,8 @@ var launcher: EntityBase = null
|
||||
var spawnInWhen: float = 0
|
||||
var spawnInWhere: Vector2 = Vector2.ZERO
|
||||
var destroying: bool = false
|
||||
var isChildSplit: bool = false
|
||||
var isChildRefract: bool = false
|
||||
|
||||
func _ready():
|
||||
register()
|
||||
@@ -59,6 +61,8 @@ func _physics_process(_delta: float) -> void:
|
||||
if is_instance_valid(launcher) and (launcher.isPlayer() or is_instance_valid(launcher.currentFocusedBoss)):
|
||||
launcher.position -= Vector2.from_angle(rotation) * recoil
|
||||
ai()
|
||||
else:
|
||||
tryDestroy()
|
||||
|
||||
func hit(target: Node):
|
||||
var entity: EntityBase = EntityTool.fromHurtbox(target)
|
||||
@@ -84,11 +88,24 @@ func dotLoop():
|
||||
func tryDestroy(becauseMap: bool = false):
|
||||
if destroying: return
|
||||
destroying = true
|
||||
trySplit()
|
||||
tryRefract()
|
||||
await destroy(becauseMap)
|
||||
if autoDestroyAnimation:
|
||||
animator.play("destroy")
|
||||
await animator.animation_finished
|
||||
queue_free()
|
||||
func trySplit():
|
||||
if is_instance_valid(launcher) and !isChildSplit:
|
||||
var launcherSplit = launcher.fields.get(FieldStore.Entity.BULLET_SPLIT)
|
||||
for i in range(MathTool.shrimpRate(launcherSplit)):
|
||||
split(i, launcherSplit, launcherSplit - floor(launcherSplit))
|
||||
func tryRefract():
|
||||
if is_instance_valid(launcher) and !isChildRefract:
|
||||
var value = launcher.fields.get(FieldStore.Entity.BULLET_REFRACTION)
|
||||
var entity = EntityTool.findClosetEntity(position, get_tree(), !launcher.isPlayer(), launcher.isPlayer())
|
||||
for i in range(MathTool.shrimpRate(value)):
|
||||
refract(entity, i, value, value - floor(value))
|
||||
|
||||
# 抽象方法
|
||||
func ai():
|
||||
@@ -103,24 +120,32 @@ func succeedToHit(_dmg: float):
|
||||
pass
|
||||
func register():
|
||||
pass
|
||||
func split(_index: int, _total: int, _lastBullet: float):
|
||||
pass
|
||||
func refract(_entity: EntityBase, _index: int, _total: int, _lastBullet: float):
|
||||
pass
|
||||
|
||||
static func generate(
|
||||
bullet: PackedScene,
|
||||
launchBy: EntityBase,
|
||||
spawnPosition: Vector2,
|
||||
spawnRotation: float,
|
||||
asChildSplit: bool = false,
|
||||
asChildRefract: bool = false,
|
||||
addToWorld: bool = true
|
||||
):
|
||||
var extraCount = launchBy.fields.get(FieldStore.Entity.EXTRA_BULLET_COUNT)
|
||||
var count = 1 + floor(extraCount) + int(MathTool.rate(extraCount - floor(extraCount)))
|
||||
var count = 1 + MathTool.shrimpRate(extraCount)
|
||||
var instances = []
|
||||
for i in range(count):
|
||||
var instance: BulletBase = bullet.instantiate()
|
||||
if launchBy.useEnergy(instance.needEnergy):
|
||||
instance.isChildSplit = asChildSplit
|
||||
instance.isChildRefract = asChildRefract
|
||||
instance.launcher = launchBy
|
||||
instance.position = spawnPosition
|
||||
instance.rotation = spawnRotation + deg_to_rad(randf_range(-launchBy.fields.get(FieldStore.Entity.OFFSET_SHOOT), launchBy.fields.get(FieldStore.Entity.OFFSET_SHOOT)))
|
||||
if addToWorld:
|
||||
WorldManager.rootNode.add_child(instance)
|
||||
WorldManager.rootNode.call_deferred("add_child", instance)
|
||||
instances.append(instance)
|
||||
return len(instances)
|
||||
|
||||
@@ -7,37 +7,39 @@ signal healthChanged(health: float)
|
||||
|
||||
signal energyChanged(energy: float)
|
||||
|
||||
const TITLE_FLAG = INF
|
||||
var fields = {
|
||||
# 数值上限
|
||||
"生存": TITLE_FLAG,
|
||||
FieldStore.Entity.HEAL_ABILITY: 1,
|
||||
FieldStore.Entity.MAX_HEALTH: 100,
|
||||
FieldStore.Entity.MAX_ENERGY: 200,
|
||||
FieldStore.Entity.EXTRA_APPLE_MAX: 0,
|
||||
FieldStore.Entity.EXTRA_BULLET_COUNT: 0,
|
||||
# 速度
|
||||
FieldStore.Entity.MOVEMENT_SPEED: 1,
|
||||
FieldStore.Entity.ATTACK_SPEED: 1,
|
||||
# 伤害
|
||||
FieldStore.Entity.DAMAGE_MULTIPILER: 1,
|
||||
FieldStore.Entity.CRIT_DAMAGE: 1,
|
||||
# 概率相关
|
||||
FieldStore.Entity.CRIT_RATE: 0.05,
|
||||
FieldStore.Entity.PENERATE: 0,
|
||||
FieldStore.Entity.OFFSET_SHOOT: 3,
|
||||
FieldStore.Entity.DROP_APPLE_RATE: 0,
|
||||
FieldStore.Entity.PENARATION_RESISTANCE: 0,
|
||||
FieldStore.Entity.LUCK_VALUE: 1,
|
||||
# 治疗
|
||||
FieldStore.Entity.HEAL_ABILITY: 1,
|
||||
# 价格减免
|
||||
FieldStore.Entity.PRICE_REDUCTION: 0,
|
||||
# 饲料
|
||||
FieldStore.Entity.FEED_COUNT_SHOW: 3,
|
||||
FieldStore.Entity.FEED_COUNT_CAN_MADE: 1,
|
||||
# 储能
|
||||
"储能": TITLE_FLAG,
|
||||
FieldStore.Entity.ENERGY_MULTIPILER: 1,
|
||||
FieldStore.Entity.SAVE_ENERGY: 1,
|
||||
FieldStore.Entity.ENERGY_REGENERATION: 1,
|
||||
# 掉落物
|
||||
FieldStore.Entity.MAX_ENERGY: 200,
|
||||
"子弹": TITLE_FLAG,
|
||||
FieldStore.Entity.PENERATE: 0,
|
||||
FieldStore.Entity.OFFSET_SHOOT: 3,
|
||||
FieldStore.Entity.EXTRA_BULLET_COUNT: 0,
|
||||
FieldStore.Entity.BULLET_SPLIT: 0,
|
||||
FieldStore.Entity.BULLET_REFRACTION: 0,
|
||||
"速度": TITLE_FLAG,
|
||||
FieldStore.Entity.MOVEMENT_SPEED: 1,
|
||||
FieldStore.Entity.ATTACK_SPEED: 1,
|
||||
"伤害": TITLE_FLAG,
|
||||
FieldStore.Entity.DAMAGE_MULTIPILER: 1,
|
||||
FieldStore.Entity.CRIT_RATE: 0.05,
|
||||
FieldStore.Entity.CRIT_DAMAGE: 1,
|
||||
"概率": TITLE_FLAG,
|
||||
FieldStore.Entity.LUCK_VALUE: 1,
|
||||
"饲料": TITLE_FLAG,
|
||||
FieldStore.Entity.PRICE_REDUCTION: 0,
|
||||
FieldStore.Entity.FEED_COUNT_SHOW: 3,
|
||||
FieldStore.Entity.FEED_COUNT_CAN_MADE: 1,
|
||||
"掉落物": TITLE_FLAG,
|
||||
FieldStore.Entity.DROPPED_ITEM_COLLECT_RADIUS: 60,
|
||||
}
|
||||
var attackCooldownMap = {
|
||||
@@ -211,6 +213,7 @@ func trySprint():
|
||||
await TickTool.until(func(): return !sprinting)
|
||||
trailing = false
|
||||
func tryDie(by: BulletBase):
|
||||
if is_queued_for_deletion(): return
|
||||
for drop in range(min(len(drops), len(dropCounts))):
|
||||
var item = drops[drop]
|
||||
var count = ceil(randf_range(dropCounts[drop].x, dropCounts[drop].y))
|
||||
@@ -233,6 +236,7 @@ func tryHeal(count: float):
|
||||
playSound("heal")
|
||||
healed.emit(heal(count * fields.get(FieldStore.Entity.HEAL_ABILITY)))
|
||||
healthChanged.emit(health)
|
||||
|
||||
func findWeaponAnchor(weaponName: String):
|
||||
var anchor = $"%weapons".get_node(weaponName)
|
||||
if anchor is Node2D:
|
||||
|
||||
@@ -9,9 +9,20 @@ enum Quality {
|
||||
EPIC,
|
||||
LEGENDARY,
|
||||
}
|
||||
enum Topic {
|
||||
SURVIVAL,
|
||||
ENERGY,
|
||||
BULLET,
|
||||
SPEED,
|
||||
DAMAGE,
|
||||
PROBABILITY,
|
||||
FEED,
|
||||
DROP,
|
||||
}
|
||||
|
||||
@export var displayName: String = "未命名饲料"
|
||||
@export var quality: Quality = Quality.COMMON
|
||||
@export var topic: Topic = Topic.SURVIVAL
|
||||
@export var qualityColorMap = {
|
||||
Quality.WASTE: Color(),
|
||||
Quality.COMMON: Color(),
|
||||
@@ -38,19 +49,45 @@ enum Quality {
|
||||
Quality.COMMON: - 1,
|
||||
Quality.RARE: 0,
|
||||
Quality.EPIC: 1,
|
||||
Quality.LEGENDARY: 2
|
||||
Quality.LEGENDARY: 2,
|
||||
}
|
||||
@export var topicNameMap = {
|
||||
Topic.SURVIVAL: "生存",
|
||||
Topic.ENERGY: "储能",
|
||||
Topic.BULLET: "子弹",
|
||||
Topic.SPEED: "速度",
|
||||
Topic.DAMAGE: "伤害",
|
||||
Topic.PROBABILITY: "概率",
|
||||
Topic.FEED: "饲料",
|
||||
Topic.DROP: "掉落物",
|
||||
}
|
||||
@export var topicColorMap = {
|
||||
Topic.SURVIVAL: Color(),
|
||||
Topic.ENERGY: Color(),
|
||||
Topic.BULLET: Color(),
|
||||
Topic.SPEED: Color(),
|
||||
Topic.DAMAGE: Color(),
|
||||
Topic.PROBABILITY: Color(),
|
||||
Topic.FEED: Color(),
|
||||
Topic.DROP: Color(),
|
||||
}
|
||||
|
||||
@onready var qualityLabel: Label = $"%quality"
|
||||
@onready var topicLabel: Label = $"%topic"
|
||||
@onready var nameLabel: RichTextLabel = $"%label"
|
||||
|
||||
func _ready():
|
||||
qualityLabel.label_settings = qualityLabel.label_settings.duplicate()
|
||||
topicLabel.label_settings = topicLabel.label_settings.duplicate()
|
||||
func _physics_process(_delta):
|
||||
qualityLabel.text = "[%s]" % qualityNameMap[quality]
|
||||
qualityLabel.label_settings.font_color = color()
|
||||
qualityLabel.label_settings.font_color = qualityColor()
|
||||
topicLabel.text = "[%s]" % topicNameMap[topic]
|
||||
topicLabel.label_settings.font_color = topicColor()
|
||||
nameLabel.text = "[b]%s[/b]" % displayName
|
||||
func color():
|
||||
func qualityColor():
|
||||
return qualityColorMap[quality] as Color
|
||||
func topicColor():
|
||||
return topicColorMap[topic] as Color
|
||||
func weight(player: EntityBase) -> int:
|
||||
return floor(clamp(qualityRandomWeight[quality] + luckInfluence[quality] * player.fields[FieldStore.Entity.LUCK_VALUE], 1, INF))
|
||||
|
||||
@@ -10,32 +10,34 @@ var collecting: bool = false
|
||||
@onready var animator: AnimationPlayer = $"%animator"
|
||||
|
||||
func _ready():
|
||||
apply_force(MathTool.randv2_range(30000), MathTool.randv2_range(10))
|
||||
await TickTool.millseconds(100)
|
||||
body_entered.connect(
|
||||
func(body):
|
||||
if body is ItemDropped and !body.collecting:
|
||||
if body.item == item:
|
||||
body.stackCount += stackCount
|
||||
collect()
|
||||
)
|
||||
func _process(_delta):
|
||||
texture.texture = ItemStore.getTexture(item)
|
||||
func _physics_process(_delta):
|
||||
if !is_instance_valid(targetPlayer):
|
||||
targetPlayer = findPlayer()
|
||||
targetPlayer = EntityTool.findClosetPlayer(position, WorldManager.tree)
|
||||
if is_instance_valid(targetPlayer):
|
||||
if collecting:
|
||||
linear_velocity = Vector2.ZERO
|
||||
else:
|
||||
var direction = (targetPlayer.position - position).normalized()
|
||||
var speed = 5000.0 / ((targetPlayer.position - position).length() ** (1 / 3.0))
|
||||
var speed = 10000.0 / ((targetPlayer.position - position).length() ** (1 / 3.0))
|
||||
apply_central_force(direction * speed)
|
||||
angular_velocity = linear_velocity.length() ** (1.0 / 2.25) # 角速度=线速度的2.25次根号
|
||||
if position.distance_to(targetPlayer.position) < targetPlayer.fields.get(FieldStore.Entity.DROPPED_ITEM_COLLECT_RADIUS):
|
||||
targetPlayer.collectItem(item, stackCount)
|
||||
collect()
|
||||
if targetPlayer.sprinting:
|
||||
apply_central_force((position - targetPlayer.texture.global_position).normalized() * targetPlayer.velocity.length() * 10)
|
||||
else:
|
||||
targetPlayer.collectItem(item, stackCount)
|
||||
collect()
|
||||
|
||||
func findPlayer() -> EntityBase:
|
||||
var result = null
|
||||
var lastDistance = INF
|
||||
for player in get_tree().get_nodes_in_group("players"):
|
||||
if player is EntityBase:
|
||||
if position.distance_to(player.position) < lastDistance:
|
||||
lastDistance = position.distance_to(player.position)
|
||||
result = player
|
||||
return result
|
||||
func collect():
|
||||
collecting = true
|
||||
animator.play("collect")
|
||||
|
||||
@@ -39,7 +39,10 @@ func _physics_process(_delta):
|
||||
for i in fields.get_children():
|
||||
fields.remove_child(i)
|
||||
for i in player.fields:
|
||||
fields.add_child(FieldShow.create(i, player.fields[i], false, player, true))
|
||||
if player.fields[i] == EntityBase.TITLE_FLAG:
|
||||
fields.add_child(QuickUI.graySmallText(i))
|
||||
else:
|
||||
fields.add_child(FieldShow.create(i, player.fields[i], false, player, true))
|
||||
fieldsAnimator.play("show")
|
||||
if Input.is_action_just_released("showFields"):
|
||||
fieldsAnimator.play("hide")
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
class_name WeaponName
|
||||
|
||||
enum Quality {
|
||||
WASTE,
|
||||
COMMON,
|
||||
RARE,
|
||||
EPIC,
|
||||
LEGENDARY,
|
||||
}
|
||||
enum TypeTopic {
|
||||
IMPACT,
|
||||
ENERGY,
|
||||
TEMPERATURE,
|
||||
}
|
||||
|
||||
@export var displayName: String = "未命名武器"
|
||||
@export var quality: Quality = Quality.COMMON
|
||||
@export var typeTopic: TypeTopic = TypeTopic.IMPACT
|
||||
@export var qualityColorMap = {
|
||||
Quality.WASTE: Color(),
|
||||
Quality.COMMON: Color(),
|
||||
Quality.RARE: Color(),
|
||||
Quality.EPIC: Color(),
|
||||
Quality.LEGENDARY: Color()
|
||||
}
|
||||
@export var qualityNameMap = {
|
||||
Quality.WASTE: "常见",
|
||||
Quality.COMMON: "普通",
|
||||
Quality.RARE: "稀有",
|
||||
Quality.EPIC: "史诗",
|
||||
Quality.LEGENDARY: "传说"
|
||||
}
|
||||
@export var qualityRandomWeight = {
|
||||
Quality.WASTE: 20,
|
||||
Quality.COMMON: 100,
|
||||
Quality.RARE: 30,
|
||||
Quality.EPIC: 10,
|
||||
Quality.LEGENDARY: 5
|
||||
}
|
||||
@export var luckInfluence = {
|
||||
Quality.WASTE: - 0.5,
|
||||
Quality.COMMON: - 1,
|
||||
Quality.RARE: 0,
|
||||
Quality.EPIC: 1,
|
||||
Quality.LEGENDARY: 2,
|
||||
}
|
||||
@export var typeTopicNameMap = {
|
||||
TypeTopic.IMPACT: "冲击",
|
||||
TypeTopic.ENERGY: "能量",
|
||||
TypeTopic.TEMPERATURE: "熔融",
|
||||
}
|
||||
@export var typeTopicColorMap = {
|
||||
TypeTopic.IMPACT: Color(),
|
||||
TypeTopic.ENERGY: Color(),
|
||||
TypeTopic.TEMPERATURE: Color(),
|
||||
}
|
||||
|
||||
@onready var qualityLabel: Label = $"%quality"
|
||||
@onready var typeTopicLabel: Label = $"%typeTopic"
|
||||
@onready var nameLabel: RichTextLabel = $"%label"
|
||||
|
||||
func _ready():
|
||||
qualityLabel.label_settings = qualityLabel.label_settings.duplicate()
|
||||
typeTopicLabel.label_settings = typeTopicLabel.label_settings.duplicate()
|
||||
func _physics_process(_delta):
|
||||
qualityLabel.text = "[%s]" % qualityNameMap[quality]
|
||||
qualityLabel.label_settings.font_color = qualityColor()
|
||||
typeTopicLabel.text = "[%s]" % typeTopicNameMap[typeTopic]
|
||||
typeTopicLabel.label_settings.font_color = typeTopicColor()
|
||||
nameLabel.text = "[b]%s[/b]" % displayName
|
||||
func qualityColor():
|
||||
return qualityColorMap[quality] as Color
|
||||
func typeTopicColor():
|
||||
return typeTopicColorMap[typeTopic] as Color
|
||||
func weight(player: EntityBase) -> int:
|
||||
return floor(clamp(qualityRandomWeight[quality] + luckInfluence[quality] * player.fields[FieldStore.Entity.LUCK_VALUE], 1, INF))
|
||||
@@ -7,6 +7,7 @@ signal selected(applied: bool)
|
||||
@export var avatarTexture: Texture2D = preload("res://icon.svg")
|
||||
@export var displayName: String = "未命名饲料"
|
||||
@export var quality: FeedName.Quality = FeedName.Quality.COMMON
|
||||
@export var topic: FeedName.Topic = FeedName.Topic.SURVIVAL
|
||||
@export var fields: Array[FieldStore.Entity] = []
|
||||
@export var fieldValues: Array[float] = []
|
||||
@export var costs: Array[ItemStore.ItemType] = []
|
||||
@@ -60,6 +61,7 @@ func rebuildInfo():
|
||||
avatarRect.texture = avatarTexture
|
||||
nameLabel.displayName = displayName
|
||||
nameLabel.quality = quality
|
||||
nameLabel.topic = topic
|
||||
for i in fieldsBox.get_children():
|
||||
i.queue_free()
|
||||
var noField = true
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
@tool
|
||||
extends PanelContainer
|
||||
class_name Weapon
|
||||
|
||||
signal selected(applied: bool)
|
||||
|
||||
@export var avatarTexture: Texture2D = preload("res://icon.svg")
|
||||
@export var displayName: String = "未命名饲料"
|
||||
@export var quality: WeaponName.Quality = WeaponName.Quality.COMMON
|
||||
@export var typeTopic: WeaponName.TypeTopic = WeaponName.TypeTopic.IMPACT
|
||||
@export var costs: Array[ItemStore.ItemType] = []
|
||||
@export var costCounts: Array[int] = []
|
||||
@export var store: Dictionary = {
|
||||
"atk": 10
|
||||
}
|
||||
@export var descriptionTemplate: String = "造成$atk点伤害。"
|
||||
@export var needEnergy: float = 0
|
||||
@export var cooldown: float = 100
|
||||
|
||||
@onready var avatarRect: TextureRect = $"%avatar"
|
||||
@onready var nameLabel: WeaponName = $"%name"
|
||||
@onready var energyLabel: Label = $"%energy"
|
||||
@onready var descriptionLabel: RichTextLabel = $"%description"
|
||||
@onready var costsBox: GridContainer = $"%costs"
|
||||
@onready var selectButton: Button = $"%selectBtn"
|
||||
|
||||
func _ready():
|
||||
selectButton.pressed.connect(
|
||||
func():
|
||||
apply(UIState.player)
|
||||
)
|
||||
rebuildInfo()
|
||||
func _physics_process(_delta: float):
|
||||
descriptionLabel.text = buildDescription()
|
||||
|
||||
func allHad(entity: EntityBase) -> bool:
|
||||
var allHave = true
|
||||
for i in range(min(costs.size(), costCounts.size())):
|
||||
var item = costs[i]
|
||||
var count = costCounts[i] * multipiler()
|
||||
if entity.inventory[item] < count:
|
||||
allHave = false
|
||||
break
|
||||
return allHave
|
||||
func apply(entity: EntityBase):
|
||||
var allHave = allHad(entity)
|
||||
if allHave:
|
||||
for i in range(min(costs.size(), costCounts.size())):
|
||||
var item = costs[i]
|
||||
var count = costCounts[i] * multipiler()
|
||||
entity.inventory[item] -= count
|
||||
hide()
|
||||
selected.emit(allHave)
|
||||
return allHave
|
||||
func multipiler() -> float:
|
||||
if is_instance_valid(UIState.player):
|
||||
return 1 - UIState.player.fields.get(FieldStore.Entity.PRICE_REDUCTION)
|
||||
else:
|
||||
return 1
|
||||
func rebuildInfo():
|
||||
avatarRect.texture = avatarTexture
|
||||
nameLabel.displayName = displayName
|
||||
nameLabel.quality = quality
|
||||
nameLabel.typeTopic = typeTopic
|
||||
energyLabel.text = "%.1f" % needEnergy
|
||||
for i in costsBox.get_children():
|
||||
i.queue_free()
|
||||
for i in range(min(costs.size(), costCounts.size())):
|
||||
var cost = costs[i]
|
||||
var count = costCounts[i]
|
||||
var costShow: ItemShow = preload("res://components/UI/ItemShow.tscn").instantiate()
|
||||
costShow.type = cost
|
||||
costShow.count = int(count * multipiler())
|
||||
costsBox.add_child(costShow)
|
||||
func buildDescription():
|
||||
var result = descriptionTemplate
|
||||
for key in store.keys():
|
||||
result = result.replace("$" + key, "[color=cyan]%.1f[/color]" % readStore(key))
|
||||
return result
|
||||
func readStore(key: String, default: Variant = null):
|
||||
return store.get(key, default)
|
||||
|
||||
# 抽象
|
||||
func update(_to: int, _origin: Dictionary, _entity: EntityBase):
|
||||
pass
|
||||
func attack(_entity: EntityBase):
|
||||
pass
|
||||
@@ -7,4 +7,20 @@ static func fromHurtbox(node: Node) -> EntityBase:
|
||||
var entity = texture.get_parent()
|
||||
if entity is EntityBase:
|
||||
return entity as EntityBase
|
||||
return null
|
||||
return null
|
||||
static func findClosetEntity(to: Vector2, fromTree: SceneTree, player: bool = false, mob: bool = false, excludes: Array[EntityBase] = []) -> EntityBase:
|
||||
var result = null
|
||||
var lastDistance = INF
|
||||
var nodes = []
|
||||
if player:
|
||||
nodes += fromTree.get_nodes_in_group("players")
|
||||
if mob:
|
||||
nodes += fromTree.get_nodes_in_group("mobs")
|
||||
for entity in nodes:
|
||||
if entity is EntityBase and entity not in excludes:
|
||||
if to.distance_to(entity.position) < lastDistance:
|
||||
lastDistance = to.distance_to(entity.position)
|
||||
result = entity
|
||||
return result
|
||||
static func findClosetPlayer(to: Vector2, fromTree: SceneTree, excludes: Array[EntityBase] = []) -> EntityBase:
|
||||
return findClosetEntity(to, fromTree, true, false, excludes)
|
||||
|
||||
@@ -28,7 +28,9 @@ enum Entity {
|
||||
LUCK_VALUE,
|
||||
SAVE_ENERGY,
|
||||
ENERGY_REGENERATION,
|
||||
DROPPED_ITEM_COLLECT_RADIUS
|
||||
DROPPED_ITEM_COLLECT_RADIUS,
|
||||
BULLET_SPLIT,
|
||||
BULLET_REFRACTION
|
||||
}
|
||||
static var entityMap = {
|
||||
Entity.MAX_HEALTH: "生命上限",
|
||||
@@ -44,7 +46,7 @@ static var entityMap = {
|
||||
Entity.ENERGY_MULTIPILER: "储能倍率",
|
||||
Entity.PENARATION_RESISTANCE: "穿透抗性",
|
||||
Entity.PRICE_REDUCTION: "饲料降价",
|
||||
Entity.EXTRA_BULLET_COUNT: "额外子弹",
|
||||
Entity.EXTRA_BULLET_COUNT: "多重射击",
|
||||
Entity.DROP_APPLE_RATE: "苹果掉落率",
|
||||
Entity.FEED_COUNT_SHOW: "饲料列表",
|
||||
Entity.FEED_COUNT_CAN_MADE: "可制作饲料",
|
||||
@@ -53,6 +55,8 @@ static var entityMap = {
|
||||
Entity.SAVE_ENERGY: "节能",
|
||||
Entity.ENERGY_REGENERATION: "能量再生效率",
|
||||
Entity.DROPPED_ITEM_COLLECT_RADIUS: "掉落物拾取距离",
|
||||
Entity.BULLET_SPLIT: "分裂",
|
||||
Entity.BULLET_REFRACTION: "折射",
|
||||
}
|
||||
static var entityMapType = {
|
||||
Entity.MAX_HEALTH: DataType.VALUE,
|
||||
@@ -77,6 +81,8 @@ static var entityMapType = {
|
||||
Entity.SAVE_ENERGY: DataType.PERCENT,
|
||||
Entity.ENERGY_REGENERATION: DataType.PERCENT,
|
||||
Entity.DROPPED_ITEM_COLLECT_RADIUS: DataType.VALUE,
|
||||
Entity.BULLET_SPLIT: DataType.VALUE,
|
||||
Entity.BULLET_REFRACTION: DataType.VALUE,
|
||||
}
|
||||
static var entityMaxValueMap = {
|
||||
Entity.CRIT_RATE: 0.8,
|
||||
@@ -96,7 +102,7 @@ static var entityApplier = {
|
||||
return true
|
||||
,
|
||||
Entity.EXTRA_BULLET_COUNT: func(entity, value):
|
||||
entity.fields[Entity.OFFSET_SHOOT] += value * 5
|
||||
entity.fields[Entity.OFFSET_SHOOT] += value * 3
|
||||
return true
|
||||
,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name MathTool
|
||||
|
||||
static func rate(value: float):
|
||||
static func rate(value: float) -> bool:
|
||||
return randf() < value
|
||||
static func randv2_range(offset: float):
|
||||
return Vector2(
|
||||
@@ -13,3 +13,5 @@ static func signBeforeStr(value: float):
|
||||
return ("+" if value > 0 else "-" if value < 0 else "") + str(abs(value))
|
||||
static func percent(value: float):
|
||||
return value / 100
|
||||
static func shrimpRate(value: float):
|
||||
return floor(value) + int(rate(value - floor(value)))
|
||||
|
||||
@@ -7,3 +7,12 @@ static func smallText(text: String, center: bool = true):
|
||||
if center:
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
return label
|
||||
static func graySmallText(text: String, center: bool = true):
|
||||
var label = Label.new()
|
||||
label.text = text
|
||||
label.label_settings = LabelSettings.new()
|
||||
label.label_settings.font_size = 12
|
||||
label.label_settings.font_color = Color(0.6, 0.6, 0.6, 1)
|
||||
if center:
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
return label
|
||||
Reference in New Issue
Block a user