diff --git a/CopyItems/crop_item.tscn b/CopyItems/crop_item.tscn index dcab7c4..6d086c1 100644 --- a/CopyItems/crop_item.tscn +++ b/CopyItems/crop_item.tscn @@ -46,7 +46,7 @@ texture = ExtResource("1_bns1c") [node name="crop_sprite" type="Sprite2D" parent="."] material = SubResource("ShaderMaterial_s5pb0") -position = Vector2(51, 45) +position = Vector2(51, 40) scale = Vector2(0.339844, 0.363281) [node name="ProgressBar" type="ProgressBar" parent="."] diff --git a/Data/pet_data.json b/Data/pet_data.json index f541c13..0b45615 100644 --- a/Data/pet_data.json +++ b/Data/pet_data.json @@ -1,1466 +1,87 @@ { - "小绿": { - "场景路径": "res://Scene/Pet/SmallGreen.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽小绿", - "队伍标识": "team1", - "宠物ID": "0001", - "宠物类型": "小绿", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "小蓝": { - "场景路径": "res://Scene/Pet/SmallBlue.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽小蓝", - "队伍标识": "team1", - "宠物ID": "0002", - "宠物类型": "小蓝", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "小黄": { - "场景路径": "res://Scene/Pet/SmallYellow.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽小黄", - "队伍标识": "team1", - "宠物ID": "0003", - "宠物类型": "小黄", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "小橙": { - "场景路径": "res://Scene/Pet/SmallOrange.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽小橙", - "队伍标识": "team1", - "宠物ID": "0004", - "宠物类型": "小橙", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "小粉": { - "场景路径": "res://Scene/Pet/SmallPink.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽小粉", - "队伍标识": "team1", - "宠物ID": "0005", - "宠物类型": "小粉", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "红史莱姆": { - "场景路径": "res://Scene/Pet/RedSlime.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽红史莱姆", - "队伍标识": "team1", - "宠物ID": "0006", - "宠物类型": "红史莱姆", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "绿史莱姆": { - "场景路径": "res://Scene/Pet/GreenSlime.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽绿史莱姆", - "队伍标识": "team1", - "宠物ID": "0007", - "宠物类型": "绿史莱姆", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "小骑士": { - "场景路径": "res://Scene/Pet/LittleKnight.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽小骑士", - "队伍标识": "team1", - "宠物ID": "0008", - "宠物类型": "小骑士", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "大甲虫": { - "场景路径": "res://Scene/Pet/BigBeetle.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽大甲虫", - "队伍标识": "team1", - "宠物ID": "0009", - "宠物类型": "大甲虫", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "小甲虫": { - "场景路径": "res://Scene/Pet/SmallBeetle.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽小甲虫", - "队伍标识": "team1", - "宠物ID": "0010", - "宠物类型": "小甲虫", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "飞鸟": { - "场景路径": "res://Scene/Pet/FlyingBird.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽飞鸟", - "队伍标识": "team1", - "宠物ID": "0011", - "宠物类型": "飞鸟", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - }, - "小钻头": { - "场景路径": "res://Scene/Pet/SmallDrillBit.tscn", - - "基本信息": { - "宠物主人": "树萌芽", - "宠物名称": "萌芽小钻头", - "队伍标识": "team1", - "宠物ID": "0012", - "宠物类型": "小钻头", - "生日": "", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - - "穿透属性": { - "穿透数量": 3 - }, - - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - - "品质系统": { - "宠物品质": "COMMON" - } - } -} \ No newline at end of file + "_id": { + "$oid": "687cf59b8e77ba00a7414bab" + }, + "updated_at": { + "$date": "2025-07-20T22:13:38.521Z" + }, + "烈焰鸟": { + "pet_name": "烈焰鸟", + "can_purchase":true, + "cost":1000, + "pet_image": "res://Scene/NewPet/PetType/flying_bird.tscn", + "pet_id": "0001", + "pet_type": "飞鸟", + "pet_level": 1, + "pet_experience": 500, + "pet_temperament": "勇猛", + "pet_birthday": "2023-03-15", + "pet_hobby": "喜欢战斗和烈火", + "pet_introduction": "我爱吃虫子", + "max_health": 300, + "enable_health_regen": true, + "health_regen": 2, + "enable_shield_regen": true, + "max_shield": 150, + "shield_regen": 1.5, + "max_armor": 120, + "base_attack_damage": 40, + "crit_rate": 0.15, + "crit_damage": 2, + "armor_penetration": 10, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 2, + "enable_berserker_skill": true, + "berserker_bonus": 1.8, + "berserker_duration": 6, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": false, + "enable_death_respawn_skill": true, + "respawn_health_percentage": 0.4, + "move_speed": 180, + "dodge_rate": 0.08, + "element_type": "FIRE", + "element_damage_bonus": 75, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "大蓝虫": { + "pet_name": "大蓝虫", + "can_purchase":true, + "cost":1000, + "pet_image": "res://Scene/NewPet/PetType/big_beetle.tscn", + "pet_id": "0002", + "pet_type": "大甲壳虫", + "pet_level": 8, + "pet_experience": 320, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和小甲壳虫玩", + "pet_introduction": "我是大蓝虫,不是大懒虫!", + "max_health": 180, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200, + "shield_regen": 2.5, + "max_armor": 80, + "base_attack_damage": 35, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + } +} \ No newline at end of file diff --git a/GUI/MainMenuPanel.tscn b/GUI/MainMenuPanel.tscn index 27afb16..a56bc9c 100644 --- a/GUI/MainMenuPanel.tscn +++ b/GUI/MainMenuPanel.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=11 format=3 uid="uid://bypjb28h4ntdr"] +[gd_scene load_steps=12 format=3 uid="uid://bypjb28h4ntdr"] [ext_resource type="Script" uid="uid://badqjgdfhg7vt" path="res://GUI/MainMenuPanel.gd" id="1_wpehy"] [ext_resource type="Texture2D" uid="uid://ddcmrh50o1y0q" path="res://assets/菜单UI/背景1.webp" id="2_eghpk"] @@ -7,6 +7,7 @@ [ext_resource type="Texture2D" uid="uid://dgdootc5bny5q" path="res://assets/菜单UI/QQ群.webp" id="4_eghpk"] [ext_resource type="Script" uid="uid://kj7v1uxk2i6h" path="res://GUI/GameUpdatePanel.gd" id="4_fys16"] [ext_resource type="Texture2D" uid="uid://ccav04woielxa" path="res://assets/菜单UI/柚小青装饰品.webp" id="5_6jmhb"] +[ext_resource type="Texture2D" uid="uid://be4fa6qo525y1" path="res://assets/菜单UI/灵创招新群.png" id="5_m77al"] [ext_resource type="Script" uid="uid://ciwjx67wjubdy" path="res://GUI/CheckUpdatePanel.gd" id="9_6jmhb"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_eghpk"] @@ -86,6 +87,18 @@ offset_bottom = 718.0 theme_override_font_sizes/font_size = 30 text = "版本号:v1.0.1" +[node name="Developer" type="RichTextLabel" parent="GUI"] +layout_mode = 0 +offset_left = 1194.0 +offset_top = 676.0 +offset_right = 1398.0 +offset_bottom = 718.0 +theme_override_font_sizes/normal_font_size = 30 +bbcode_enabled = true +text = "[rainbow freq=1 sat=2 val=100]By-树萌芽[/rainbow]" +horizontal_alignment = 1 +vertical_alignment = 1 + [node name="AddGroupLabel" type="Label" parent="GUI"] self_modulate = Color(1, 1, 0, 1) layout_mode = 0 @@ -105,6 +118,14 @@ offset_bottom = 1229.0 scale = Vector2(0.3, 0.3) texture = ExtResource("4_eghpk") +[node name="QQGroupImage2" type="TextureRect" parent="GUI/AddGroupLabel"] +layout_mode = 0 +offset_left = -832.0 +offset_right = 103.0 +offset_bottom = 1186.0 +scale = Vector2(0.3, 0.3) +texture = ExtResource("5_m77al") + [node name="YouXiaoQing" type="TextureRect" parent="GUI/AddGroupLabel"] layout_mode = 0 offset_left = 298.0 @@ -114,6 +135,19 @@ offset_bottom = 1268.0 scale = Vector2(0.14, 0.14) texture = ExtResource("5_6jmhb") +[node name="RichTextLabel" type="RichTextLabel" parent="GUI/AddGroupLabel"] +self_modulate = Color(0.580392, 1, 0, 1) +layout_mode = 0 +offset_left = -896.0 +offset_top = -47.0 +offset_right = -420.0 +offset_bottom = -7.0 +theme_override_font_sizes/normal_font_size = 30 +bbcode_enabled = true +text = "欢迎了解灵创新媒实验室" +horizontal_alignment = 1 +vertical_alignment = 1 + [node name="VBox" type="VBoxContainer" parent="."] layout_mode = 0 offset_top = 248.0 @@ -217,11 +251,17 @@ text = "玩法介绍: 3.电脑Windows平台按住wsad或者方向键可以移动视角,鼠标滚轮可以缩放视角;安卓则为拖动和双指缩放 3.注册账号一定一定要用QQ号,目前游戏的所有登录服务都是围绕着腾讯QQ来验证,注册时会向您输入的QQ号对应的QQ邮箱发送一封注册邮件。 4.不要一上来就把钱用完了(比如某某人一上来就十连抽),可以去偷别人的菜 +5.玩家排行榜有一些特殊农场,可以直接搜索访问: 杂交农场(666) 花卉农场(520) 稻香(111) 小麦谷(222) 访问有惊喜 +6.全服大喇叭也有一些小彩蛋 +7.记得在小卖部向其他玩家出售你不需要的东西 +8.玩家太多无法找到你的好友的农场?试试直接搜索QQ号 +9.如果有条件尽量还是玩电脑版吧,毕竟电脑版优化是最好的,手机版或多或少有些问题( 致谢名单: 程序牛马:(作物处理+抠图) -虚空领主:(抠图) -豆包:(万能的美术)" +虚空领主:(美术+抠图) +豆包:(万能的美术) +ChatGPT:(超级美术)" [node name="GameUpdatePanel" type="Panel" parent="."] visible = false diff --git a/MainGame.gd b/MainGame.gd index 8af966e..225a081 100644 --- a/MainGame.gd +++ b/MainGame.gd @@ -3,7 +3,7 @@ extends Node # 变量定义 @onready var grid_container : GridContainer = $GridContainer # 农场地块的 GridContainer @onready var crop_item : Button = $CopyNodes/CropItem -@onready var pet_patrol_path_line: Line2D = $PetPatrolPathLine #宠物农场巡逻线 + #显示信息栏 @onready var show_money : Label = $UI/GUI/GameInfoHBox1/money # 显示当前剩余的钱 @@ -26,6 +26,7 @@ extends Node #访问模式按钮 @onready var return_my_farm_button: Button = $UI/GUI/VisitVBox/ReturnMyFarmButton #返回我的农场 @onready var like_button: Button = $UI/GUI/VisitVBox/LikeButton #给别人点赞 +@onready var battle_button: Button = $UI/GUI/VisitVBox/BattleButton #发起对战 #和农场操作相关的按钮 @onready var one_click_harvestbutton: Button = $UI/GUI/FarmVBox/OneClickHarvestButton #一键收获 @@ -58,7 +59,7 @@ extends Node @onready var tcp_network_manager_panel: Panel = $UI/BigPanel/TCPNetworkManagerPanel #网络管理器面板 @onready var item_store_panel: Panel = $UI/BigPanel/ItemStorePanel #道具商店面板 @onready var item_bag_panel: Panel = $UI/BigPanel/ItemBagPanel #道具背包面板 -@onready var player_bag_panel: Panel = $UI/BigPanel/PlayerBagPanel #种子背包面板 +@onready var player_bag_panel: Panel = $UI/BigPanel/PlayerBagPanel #种子仓库面板 @onready var crop_warehouse_panel: Panel = $UI/BigPanel/CropWarehousePanel #作物仓库面板 @onready var crop_store_panel: Panel = $UI/BigPanel/CropStorePanel #种子商店面板 @onready var player_ranking_panel: Panel = $UI/BigPanel/PlayerRankingPanel #玩家排行榜面板 @@ -71,6 +72,7 @@ extends Node @onready var game_setting_panel: Panel = $UI/BigPanel/GameSettingPanel #游戏设置面板 + #小面板 @onready var land_panel: Panel = $UI/SmallPanel/LandPanel #地块面板 @onready var load_progress_panel: Panel = $UI/SmallPanel/LoadProgressPanel #加载进度面板 @@ -113,6 +115,16 @@ extends Node @onready var visit_v_box: VBoxContainer = $UI/GUI/VisitVBox @onready var other_v_box: VBoxContainer = $UI/GUI/OtherVBox +@onready var pet_battle_panel: PetBattlePanel = $UI/BigPanel/PetBattlePanel #新的宠物对战场景 + + +@onready var pet_patrol_points: Node = $PetPatrolPoints +@onready var pos_1: Marker2D = $PetPatrolPoints/Pos1 #生成点1 +@onready var pos_2: Marker2D = $PetPatrolPoints/Pos2#生成点2 +@onready var pos_3: Marker2D = $PetPatrolPoints/Pos3#生成点3 +@onready var pos_4: Marker2D = $PetPatrolPoints/Pos4#生成点4 + + #玩家基本信息 var money: int = 500 # 默认每个人初始为100元 @@ -147,6 +159,8 @@ var battle_pets : Array = [] var can_planted_crop : Dictionary = {} #道具配置数据 var item_config_data : Dictionary = {} +# 宠物配置数据 +var pet_config : Dictionary = {} # 新手大礼包领取状态 var new_player_gift_claimed : bool = false @@ -199,6 +213,11 @@ var climate_death_timer : int = 0 #=======================脚本基础方法======================= func _ready(): + DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED) + Engine.max_fps = 0 # 0 表示解锁 + print("V-Sync disabled,max_fps =", Engine.max_fps) + + # 显示加载进度面板,隐藏其他所有UI load_progress_panel.show() load_progress_bar.value = 0 @@ -253,6 +272,9 @@ func _ready(): # 连接AcceptDialog的确认信号 accept_dialog.confirmed.connect(_on_accept_dialog_confirmed) + # 连接宠物对战面板的battle_ended信号 + pet_battle_panel.battle_ended.connect(_on_pet_battle_ended) + # 启动在线人数更新定时器 _start_online_players_timer() @@ -263,6 +285,9 @@ func _ready(): # 启动后稍等片刻尝试从服务器获取最新数据 await get_tree().create_timer(0.5).timeout _try_load_from_server() + + # 初始化对战按钮显示状态 + _update_battle_button_visibility() func _process(delta: float) -> void: @@ -402,6 +427,9 @@ func _handle_visit_player_response(data): is_visiting_mode = true visited_player_data = target_player_data + # 控制对战按钮显示 + _update_battle_button_visibility() + # 更新显示数据 money = target_player_data.get("钱币", 0) experience = target_player_data.get("经验值", 0) @@ -442,6 +470,10 @@ func _handle_visit_player_response(data): pet_bag_panel.update_pet_bag_ui() # 初始化巡逻宠物(访问模式) + print("[访问模式] 准备初始化巡逻宠物,数据量: ", patrol_pets.size()) + for i in range(patrol_pets.size()): + var pet_data = patrol_pets[i] + print("[访问模式] 巡逻宠物", i, ": ", pet_data.get("pet_name", "未知"), " ID:", pet_data.get("pet_id", "无ID")) if has_method("init_patrol_pets"): init_patrol_pets() @@ -479,6 +511,9 @@ func _handle_return_my_farm_response(data): if success: var player_data = data.get("player_data", {}) + # 隐藏对战按钮(返回自己农场时) + battle_button.hide() + # 恢复玩家数据 money = player_data.get("钱币", 500) experience = player_data.get("经验值", 0) @@ -490,6 +525,7 @@ func _handle_return_my_farm_response(data): item_bag = player_data.get("道具背包", []) pet_bag = player_data.get("宠物背包", []) patrol_pets = player_data.get("巡逻宠物", []) + battle_pets = player_data.get("出战宠物", []) # 恢复UI显示 show_player_name.text = "玩家昵称:" + player_data.get("玩家昵称", "未知") @@ -525,6 +561,10 @@ func _handle_return_my_farm_response(data): pet_bag_panel.update_pet_bag_ui() # 初始化巡逻宠物(返回自己农场) + print("[返回农场] 准备初始化巡逻宠物,数据量: ", patrol_pets.size()) + for i in range(patrol_pets.size()): + var pet_data = patrol_pets[i] + print("[返回农场] 巡逻宠物", i, ": ", pet_data.get("pet_name", "未知"), " ID:", pet_data.get("pet_id", "无ID")) if has_method("init_patrol_pets"): init_patrol_pets() @@ -546,7 +586,6 @@ func _handle_return_my_farm_response(data): Toast.show("返回农场失败:" + message, Color.RED) print("返回农场失败:", message) - #访客模式下返回我的农场 func _on_return_my_farm_button_pressed() -> void: # 如果当前处于访问模式,返回自己的农场 @@ -643,13 +682,9 @@ func _handle_crop_update(update_data): # 更新UI显示 _update_farm_lots_state() -# 原来的修复背包数据函数已移除,因为不再需要quality字段 - # 处理登录成功 func handle_login_success(player_data: Dictionary): - # 背包数据兼容性处理已移除,品质信息直接从crop_data获取 - # 更新新手大礼包状态 new_player_gift_claimed = player_data.get("new_player_gift_claimed", false) @@ -666,6 +701,13 @@ func handle_login_success(player_data: Dictionary): tcp_network_manager_panel.sendGetOnlinePlayers() print("登录成功后请求在线人数更新") + # 登录成功后请求宠物配置数据 + if tcp_network_manager_panel and tcp_network_manager_panel.has_method("sendGetPetConfig"): + if tcp_network_manager_panel.sendGetPetConfig(): + print("登录成功后请求宠物配置数据") + else: + print("登录成功后请求宠物配置数据失败") + # 其他登录成功后的初始化逻辑可以在这里添加 start_game = true @@ -678,11 +720,12 @@ func handle_login_success(player_data: Dictionary): # 初始化智慧树显示 update_wisdom_tree_display() + # 初始化对战按钮显示状态 + _update_battle_button_visibility() + # 立即请求服务器历史消息以刷新显示 call_deferred("_request_server_history_for_refresh") - - #创建作物按钮 func _create_crop_button(crop_name: String, crop_quality: String) -> Button: # 根据品质选择相应的进度条 @@ -730,7 +773,6 @@ func _create_farm_buttons(): grid_container.add_child(button) - # 更新农场地块状态 func _update_farm_lots_state(): var digged_count = 0 @@ -840,13 +882,11 @@ func _update_farm_lots_state(): dig_money = digged_count * 1000 - # 仅在加载游戏或特定情况下完全刷新地块 - 用于与服务器同步时 func _refresh_farm_lots(): _create_farm_buttons() _update_farm_lots_state() - # 更新玩家信息显示 func _update_ui(): show_money.text = "钱币数:" + str(money) + " 元" @@ -857,8 +897,6 @@ func _update_ui(): var my_likes = login_data.get("点赞数", 0) show_like.text = "点赞数:" + str(int(my_likes)) - - #打开玩家排行榜面板 func _on_player_ranking_button_pressed() -> void: @@ -866,7 +904,6 @@ func _on_player_ranking_button_pressed() -> void: player_ranking_panel.request_player_rankings() pass - #打开设置面板 func _on_setting_button_pressed() -> void: game_setting_panel.show() @@ -878,8 +915,6 @@ func _on_watch_broadcast_button_pressed() -> void: global_server_broadcast_panel.show() global_server_broadcast_panel.move_to_front() - - # 处理AcceptDialog的确认信号 func _on_accept_dialog_confirmed() -> void: var action_type = accept_dialog.get_meta("action_type", "") @@ -897,7 +932,6 @@ func _on_accept_dialog_confirmed() -> void: # 处理其他类型的确认逻辑 pass - #打开一键种植面板 func _on_one_click_plant_button_pressed() -> void: # 如果处于访问模式,不允许操作 @@ -994,10 +1028,9 @@ func _on_pet_store_button_pressed() -> void: #===============================================初始化数据处理=============================================== # 从服务器获取作物数据 func _load_crop_data(): - var network_manager = get_node("/root/main/UI/TCPNerworkManager") - if network_manager and network_manager.is_connected_to_server(): + if tcp_network_manager_panel and tcp_network_manager_panel.is_connected_to_server(): # 从服务器请求作物数据 - network_manager.sendGetCropData() + tcp_network_manager_panel.sendGetCropData() else: # 如果无法连接服务器,尝试加载本地数据 print("无法连接服务器,尝试加载本地作物数据...") @@ -1155,6 +1188,24 @@ func _handle_item_config_response(response_data): print("从服务器获取道具配置数据失败:", message) _load_local_item_config() +# 处理服务器宠物配置响应 +func _handle_pet_config_response(response_data): + var success = response_data.get("success", false) + + if success: + var config_data = response_data.get("pet_config", {}) + if config_data: + # 设置全局变量 + pet_config = config_data + print("宠物配置数据已从服务器更新,宠物种类:", pet_config.size()) + else: + print("服务器返回的宠物配置数据为空") + pet_config = {} + else: + var message = response_data.get("message", "未知错误") + print("从服务器获取宠物配置数据失败:", message) + pet_config = {} + #===============================================初始化数据处理=============================================== @@ -2182,6 +2233,8 @@ func _handle_new_player_gift_response(data): # 更新宠物背包UI if updated_data.has("宠物背包"): pet_bag_panel.update_pet_bag_ui() + # 更新对战按钮显示 + _update_battle_button_visibility() # 显示成功消息 Toast.show(message, Color.GOLD, 3.0, 1.0) @@ -2221,7 +2274,7 @@ func _handle_global_broadcast_response(data: Dictionary): # 处理全服大喇叭历史消息响应 func _handle_broadcast_history_response(data: Dictionary): - print("收到历史消息响应: ", data.get("messages", []).size(), " 条消息") + #print("收到历史消息响应: ", data.get("messages", []).size(), " 条消息") if global_server_broadcast_panel and global_server_broadcast_panel.has_method("receive_history_messages"): global_server_broadcast_panel.receive_history_messages(data) @@ -2239,7 +2292,7 @@ func update_broadcast_display_from_message(data: Dictionary): # 优先显示玩家昵称 var display_name = player_name if player_name != "" else username global_server_broadcast.text = display_name + ": " + content - print("主界面大喇叭已更新为: ", global_server_broadcast.text) + #print("主界面大喇叭已更新为: ", global_server_broadcast.text) # 从面板获取最新消息更新大喇叭显示 func update_broadcast_display_from_panel(): @@ -2247,7 +2300,7 @@ func update_broadcast_display_from_panel(): var latest_message = global_server_broadcast_panel.get_latest_message() if latest_message != "暂无消息" and latest_message != "全服大喇叭": global_server_broadcast.text = latest_message - print("主界面大喇叭已更新为: ", latest_message) + #print("主界面大喇叭已更新为: ", latest_message) else: global_server_broadcast.text = "全服大喇叭" @@ -2278,8 +2331,17 @@ func _load_broadcast_from_local(): if parse_result == OK: var data = json.data if data is Array and data.size() > 0: - # 按时间戳排序 - data.sort_custom(func(a, b): return a.get("timestamp", 0) < b.get("timestamp", 0)) + # 按时间戳排序,确保类型一致 + data.sort_custom(func(a, b): + var timestamp_a = a.get("timestamp", 0) + var timestamp_b = b.get("timestamp", 0) + # 确保都是数字类型 + if timestamp_a is String: + timestamp_a = float(timestamp_a) + if timestamp_b is String: + timestamp_b = float(timestamp_b) + return timestamp_a < timestamp_b + ) # 获取最新消息 var latest = data[-1] var display_name = latest.get("display_name", "匿名") @@ -2327,11 +2389,19 @@ func _on_one_click_screen_shot_pressed() -> void: # 保存当前UI状态 var ui_state = _save_ui_visibility_state() + # 临时禁用UI自动显示逻辑 + var original_start_game = start_game + start_game = false + # 隐藏所有UI _hide_all_ui_for_screenshot() - - await get_tree().create_timer(10.0).timeout + # 等待5秒 + await get_tree().create_timer(5.0).timeout + + # 恢复游戏状态 + start_game = original_start_game + # 恢复UI显示 _restore_ui_visibility_state(ui_state) @@ -2505,6 +2575,8 @@ func _handle_use_pet_item_response(data: Dictionary): # 更新宠物背包UI if pet_bag_panel and pet_bag_panel.has_method("update_pet_bag_ui"): pet_bag_panel.update_pet_bag_ui() + # 更新对战按钮显示 + _update_battle_button_visibility() # 安全更新道具背包数据 if updated_data.has("道具背包"): @@ -2915,181 +2987,176 @@ func _clear_item_selection(): + #===============================================巡逻宠物管理=============================================== -var current_patrol_pet: CharacterBody2D = null +# 简单的巡逻宠物管理 +var patrol_pet_instances: Array[Area2D] = [] -# 初始化巡逻宠物(登录时调用) -func init_patrol_pets(): - if patrol_pets == null: - patrol_pets = [] - - if pet_patrol_path_line: - print("巡逻线节点找到,路径点数: " + str(pet_patrol_path_line.points.size())) - else: - print("错误:找不到巡逻线节点 PetPatrolPathLine") +func _create_single_patrol_pet(pet_data: Dictionary, position_index: int): + # 检查巡逻点节点 + if not pet_patrol_points: + print("错误:找不到巡逻点节点") return - update_patrol_pets() + # 获取四个巡逻点 + var patrol_positions = [] + if pos_1: patrol_positions.append(pos_1.global_position) + if pos_2: patrol_positions.append(pos_2.global_position) + if pos_3: patrol_positions.append(pos_3.global_position) + if pos_4: patrol_positions.append(pos_4.global_position) + -# 更新巡逻宠物显示 -func update_patrol_pets(): - clear_patrol_pets() - if patrol_pets == null or patrol_pets.size() == 0: + if patrol_positions.size() == 0: + print("错误:没有找到有效的巡逻点") return - # 目前只支持一个巡逻宠物 - var first_patrol_pet = patrol_pets[0] - var pet_id = first_patrol_pet.get("基本信息", {}).get("宠物ID", "") + # 确保位置索引有效 + if position_index >= patrol_positions.size(): + position_index = position_index % patrol_positions.size() - if pet_id != "": - _create_patrol_pet_instance(first_patrol_pet) - -# 清除巡逻宠物实例 -func clear_patrol_pets(): - if current_patrol_pet and is_instance_valid(current_patrol_pet): - current_patrol_pet.queue_free() - current_patrol_pet = null - - if pet_patrol_path_line: - for child in pet_patrol_path_line.get_children(): - if child is CharacterBody2D: - child.queue_free() - -# 根据宠物ID设置巡逻宠物 -func set_patrol_pet_by_id(pet_id: String): - if pet_id == "": - print("警告:宠物ID为空") + # 实例化NewPetBase场景 + var newpet_scene_path = "res://Scene/NewPet/NewPetBase.tscn" + if not ResourceLoader.exists(newpet_scene_path): + print("错误:找不到NewPetBase场景") return - var pet_data = _find_pet_by_id(pet_id) - if pet_data.is_empty(): - print("错误:找不到宠物ID: " + pet_id) - return + var pet_scene = load(newpet_scene_path) + var pet_instance = pet_scene.instantiate() - clear_patrol_pets() + # 设置基本属性 + var pet_name = pet_data.get("pet_name", pet_data.get("pet_type", "巡逻宠物")) + pet_instance.pet_name = pet_name + pet_instance.pet_id = pet_data.get("pet_id", "") + pet_instance.pet_type = pet_data.get("pet_type", "") + pet_instance.pet_level = pet_data.get("pet_level", 1) + + # 设置为巡逻状态 + pet_instance.is_patrolling = true + + # 添加到场景 + pet_patrol_points.add_child(pet_instance) + patrol_pet_instances.append(pet_instance) + + # 在添加到场景后设置位置(避免坐标系转换问题) + pet_instance.global_position = patrol_positions[position_index] + + # 设置巡逻中心点 + pet_instance.set_patrol_center(patrol_positions[position_index]) + + # 应用宠物图片 + var pet_type = pet_data.get("pet_type", "") + var image_scene_path = _get_pet_image_path(pet_type) + print("应用宠物图片: " + pet_type + " -> " + image_scene_path) + + # 等待一帧确保节点完全添加到场景树 await get_tree().process_frame - _create_patrol_pet_instance(pet_data) + if pet_instance.has_method("apply_pet_image") and image_scene_path != "": + pet_instance.apply_pet_image(pet_instance, image_scene_path) + print("宠物图片应用完成") + else: + print("无法应用宠物图片: " + str(pet_instance.has_method("apply_pet_image")) + ", " + image_scene_path) + + print("创建巡逻宠物成功: " + pet_instance.pet_name + " 在位置" + str(position_index + 1)) -# 查找宠物数据 -func _find_pet_by_id(pet_id: String) -> Dictionary: +# 获取宠物图片路径 +func _get_pet_image_path(pet_type: String) -> String: + print("[调试] 获取宠物图片路径,宠物类型: " + pet_type) + print("[调试] 服务器pet_config大小: " + str(pet_config.size())) + + # 类型映射表 + var type_mapping = { + "小绿": "大蓝虫", "小蓝": "大蓝虫", "小红": "烈焰鸟", "小黄": "烈焰鸟", + "小紫": "大蓝虫", "小橙": "烈焰鸟", "小粉": "大蓝虫", "小黑": "大蓝虫", + "小白": "大蓝虫", "小灰": "大蓝虫", "大甲壳虫": "大蓝虫", "小甲壳虫": "大蓝虫", + "飞鸟": "烈焰鸟", "小骑士": "大蓝虫", "绿史莱姆": "大蓝虫", "小钻头": "大蓝虫" + } + + # 优先使用本地PetConfig配置(更可靠) + var local_pet_config = PetConfig.new() + + # 首先尝试直接匹配宠物类型 + var config_data = local_pet_config.get_pet_config(pet_type) + if config_data.has("pet_image") and config_data["pet_image"] != "": + print("[调试] 从本地配置直接获取图片路径: " + pet_type + " -> " + config_data["pet_image"]) + return config_data["pet_image"] + + # 如果直接匹配失败,使用类型映射 + var mapped_type = type_mapping.get(pet_type, "大蓝虫") + #print("[调试] 使用类型映射: " + pet_type + " -> " + mapped_type) + + config_data = local_pet_config.get_pet_config(mapped_type) + if config_data.has("pet_image") and config_data["pet_image"] != "": + #print("[调试] 从本地配置获取映射图片路径: " + mapped_type + " -> " + config_data["pet_image"]) + return config_data["pet_image"] + + # 检查服务器配置(作为备选) + if pet_config.has(pet_type): + var server_config_data = pet_config[pet_type] + if server_config_data.has("pet_image") and server_config_data["pet_image"] != "": + #print("[调试] 从服务器配置获取图片路径: " + pet_type + " -> " + server_config_data["pet_image"]) + return server_config_data["pet_image"] + + if pet_config.has(mapped_type): + var server_config_data = pet_config[mapped_type] + if server_config_data.has("pet_image") and server_config_data["pet_image"] != "": + #print("[调试] 从服务器配置获取映射图片路径: " + mapped_type + " -> " + server_config_data["pet_image"]) + return server_config_data["pet_image"] + + # 默认返回大蓝虫场景 + print("[调试] 使用默认图片路径: big_beetle.tscn") + return "res://Scene/NewPet/PetType/big_beetle.tscn" + +# 更新巡逻宠物(从服务器数据) +func update_patrol_pets(patrol_pets_data: Array): + # 清除现有巡逻宠物 + clear_patrol_pets() + + print("[update_patrol_pets] 开始更新巡逻宠物,输入数据量: ", patrol_pets_data.size()) + print("[update_patrol_pets] 当前访问模式: ", is_visiting_mode) + + # 限制最多4个巡逻宠物 + var max_pets = min(patrol_pets_data.size(), 4) + + # 为每个巡逻宠物创建实例 + for i in range(max_pets): + var pet_data = patrol_pets_data[i] + print("[update_patrol_pets] 处理宠物", i, ": ", pet_data.get("pet_name", "未知") if pet_data else "空数据") + if pet_data and pet_data.has("pet_id"): + _create_single_patrol_pet(pet_data, i) + else: + print("[update_patrol_pets] 跳过无效宠物数据") + + print("更新巡逻宠物完成,共创建 " + str(max_pets) + " 个巡逻宠物") + +# 清除所有巡逻宠物 +func clear_patrol_pets(): + for pet_instance in patrol_pet_instances: + if pet_instance and is_instance_valid(pet_instance): + pet_instance.queue_free() + patrol_pet_instances.clear() + +# 根据宠物ID查找宠物数据 +func get_pet_data_by_id(pet_id: String) -> Dictionary: if pet_bag == null: return {} for pet_data in pet_bag: - var current_id = pet_data.get("基本信息", {}).get("宠物ID", "") + var current_id = pet_data.get("pet_id", "") if current_id == pet_id: return pet_data - return {} -# 创建巡逻宠物实例(统一的创建逻辑) -func _create_patrol_pet_instance(pet_data: Dictionary): - if not _validate_patrol_prerequisites(): - return - - var scene_path = pet_data.get("场景路径", "") - if scene_path == "" or not ResourceLoader.exists(scene_path): - print("错误:无效的场景路径: " + scene_path) - return - - var pet_scene = load(scene_path) - if not pet_scene: - print("错误:无法加载宠物场景: " + scene_path) - return - - var pet_instance = pet_scene.instantiate() - if not pet_instance: - print("错误:无法创建宠物实例") - return - - _setup_patrol_pet(pet_instance, pet_data) - - pet_patrol_path_line.add_child(pet_instance) - current_patrol_pet = pet_instance - pet_instance.position = pet_patrol_path_line.points[0] - - var pet_name = pet_data.get("基本信息", {}).get("宠物名称", "未知") - print("创建巡逻宠物成功: " + pet_name) - -# 验证巡逻前提条件 -func _validate_patrol_prerequisites() -> bool: - if not pet_patrol_path_line: - print("错误:找不到巡逻线节点") - return false - - if pet_patrol_path_line.points.size() < 2: - print("警告:巡逻路径点数少于2个") - return false - - return true - -# 设置巡逻宠物属性 -func _setup_patrol_pet(pet_instance: CharacterBody2D, pet_data: Dictionary): - var basic_info = pet_data.get("基本信息", {}) - var level_exp = pet_data.get("等级经验", {}) - var health_defense = pet_data.get("生命与防御", {}) - - # 基本信息 - var original_name = basic_info.get("宠物名称", basic_info.get("宠物类型", "未知宠物")) - pet_instance.pet_name = "[巡逻] " + original_name - pet_instance.pet_id = basic_info.get("宠物ID", "") - pet_instance.pet_type = basic_info.get("宠物类型", "") - pet_instance.pet_birthday = basic_info.get("生日", "") - pet_instance.pet_personality = basic_info.get("性格", "活泼") - pet_instance.pet_team = "patrol" - - # 等级经验 - pet_instance.pet_level = level_exp.get("宠物等级", 1) - pet_instance.pet_experience = level_exp.get("当前经验", 0.0) - pet_instance.max_experience = level_exp.get("最大经验", 100.0) - pet_instance.pet_intimacy = level_exp.get("亲密度", 0.0) - - # 生命防御 - pet_instance.max_health = health_defense.get("最大生命值", 100.0) - pet_instance.current_health = health_defense.get("当前生命值", pet_instance.max_health) - pet_instance.max_shield = health_defense.get("最大护盾值", 0.0) - pet_instance.current_shield = health_defense.get("当前护盾值", 0.0) - pet_instance.max_armor = health_defense.get("最大护甲值", 0.0) - pet_instance.current_armor = health_defense.get("当前护甲值", 0.0) - - # 巡逻设置 - pet_instance.is_patrolling = true - pet_instance.patrol_path = pet_patrol_path_line.points.duplicate() - pet_instance.patrol_speed = 80.0 - pet_instance.current_patrol_index = 0 - pet_instance.patrol_wait_time = 0.0 - pet_instance.current_state = pet_instance.PetState.PATROLLING - - # 禁用战斗行为 - if pet_instance.has_method("set_combat_enabled"): - pet_instance.set_combat_enabled(false) - - # 显示状态栏和名称 - if pet_instance.has_node("PetInformVBox"): - pet_instance.get_node("PetInformVBox").visible = true - - if pet_instance.pet_name_rich_text: - pet_instance.pet_name_rich_text.text = pet_instance.pet_name - pet_instance.pet_name_rich_text.modulate = Color.YELLOW - pet_instance.pet_name_rich_text.visible = true - +# 初始化巡逻宠物 +func init_patrol_pets(): + # 使用新的更新函数来初始化巡逻宠物 + update_patrol_pets(patrol_pets) # 检查出战宠物和巡逻宠物是否冲突 func check_battle_patrol_conflict(battle_pet_id: String, patrol_pet_id: String) -> bool: - if battle_pet_id == "" or patrol_pet_id == "": - return false return battle_pet_id == patrol_pet_id -# 根据宠物ID获取完整的宠物数据 -func get_pet_data_by_id(pet_id: String) -> Dictionary: - for pet_data in pet_bag: - var current_id = pet_data.get("基本信息", {}).get("宠物ID", "") - if current_id == pet_id: - return pet_data - return {} - #===============================================巡逻宠物管理=============================================== @@ -3138,22 +3205,30 @@ func _show_steal_caught_dialog(message: String, patrol_pet_data: Dictionary, bat print("错误:找不到AcceptDialog") return - # 获取巡逻宠物和出战宠物信息 - var patrol_pet_name = patrol_pet_data.get("基本信息", {}).get("宠物名称", "未知宠物") - var patrol_pet_level = patrol_pet_data.get("等级经验", {}).get("宠物等级", 1) - var patrol_pet_type = patrol_pet_data.get("基本信息", {}).get("宠物类型", "未知类型") - - var battle_pet_name = battle_pet_data.get("基本信息", {}).get("宠物名称", "未知宠物") - var battle_pet_level = battle_pet_data.get("等级经验", {}).get("宠物等级", 1) - var battle_pet_type = battle_pet_data.get("基本信息", {}).get("宠物类型", "未知类型") - # 构建对话框内容 var dialog_content = message + "\n\n" + + # 显示对方的巡逻宠物(所有) dialog_content += "🛡️ " + target_username + "的巡逻宠物:\n" - dialog_content += " " + patrol_pet_name + " (类型:" + patrol_pet_type + ", 等级:" + str(patrol_pet_level) + ")\n\n" - dialog_content += "⚔️ 你的出战宠物:\n" - dialog_content += " " + battle_pet_name + " (类型:" + battle_pet_type + ", 等级:" + str(battle_pet_level) + ")\n\n" - dialog_content += "请选择你的行动:\n" + var defender_pets = visited_player_data.get("巡逻宠物", [patrol_pet_data]) + for i in range(min(4, defender_pets.size())): + var pet = defender_pets[i] + var pet_name = pet.get("pet_name", "未知宠物") + var pet_level = pet.get("pet_level", 1) + var pet_type = pet.get("pet_type", "未知类型") + dialog_content += " %d. %s (类型:%s, 等级:%d)\n" % [i+1, pet_name, pet_type, pet_level] + + dialog_content += "\n⚔️ 你的出战宠物:\n" + # 显示自己的出战宠物(所有) + var attacker_pets = battle_pets if battle_pets.size() > 0 else [battle_pet_data] + for i in range(min(4, attacker_pets.size())): + var pet = attacker_pets[i] + var pet_name = pet.get("pet_name", "未知宠物") + var pet_level = pet.get("pet_level", 1) + var pet_type = pet.get("pet_type", "未知类型") + dialog_content += " %d. %s (类型:%s, 等级:%d)\n" % [i+1, pet_name, pet_type, pet_level] + + dialog_content += "\n请选择你的行动:\n" dialog_content += "💰 逃跑:支付 " + str(escape_cost) + " 金币\n" dialog_content += "⚔️ 对战:如果失败支付 " + str(battle_cost) + " 金币" @@ -3189,9 +3264,9 @@ func _show_steal_caught_dialog(message: String, patrol_pet_data: Dictionary, bat func _on_steal_battle_confirmed(patrol_pet_data: Dictionary, battle_pet_data: Dictionary, target_username: String): print("玩家选择宠物对战") - # 验证宠物数据完整性 - var battle_pet_id = battle_pet_data.get("基本信息", {}).get("宠物ID", "") - var patrol_pet_id = patrol_pet_data.get("基本信息", {}).get("宠物ID", "") + # 验证宠物数据完整性(新格式) + var battle_pet_id = battle_pet_data.get("pet_id", "") + var patrol_pet_id = patrol_pet_data.get("pet_id", "") if battle_pet_id == "" or patrol_pet_id == "": Toast.show("宠物数据不完整,无法开始对战", Color.RED, 3.0) @@ -3203,15 +3278,18 @@ func _on_steal_battle_confirmed(patrol_pet_data: Dictionary, battle_pet_data: Di return # 停止宠物对战面板的自动对战逻辑 - if pet_fight_panel and pet_fight_panel.has_method("stop_auto_battle"): - pet_fight_panel.stop_auto_battle() + if pet_battle_panel and pet_battle_panel.has_method("stop_auto_battle"): + pet_battle_panel.stop_auto_battle() # 加载双方宠物数据到对战面板 - if pet_fight_panel and pet_fight_panel.has_method("setup_steal_battle"): - pet_fight_panel.setup_steal_battle(battle_pet_data, patrol_pet_data, user_name, target_username) + if pet_battle_panel and pet_battle_panel.has_method("setup_steal_battle"): + # 传递完整的出战宠物数组和巡逻宠物数组 + var attacker_pets = battle_pets if battle_pets.size() > 0 else [battle_pet_data] + var defender_pets = visited_player_data.get("巡逻宠物", [patrol_pet_data]) + pet_battle_panel.setup_steal_battle(attacker_pets, defender_pets, user_name, target_username) # 显示宠物对战面板 - pet_fight_panel.show() + pet_battle_panel.show() GlobalVariables.isZoomDisabled = true Toast.show("准备进入宠物对战!", Color.YELLOW, 2.0) @@ -3229,6 +3307,142 @@ func _on_steal_escape_confirmed(escape_cost: int): #====================================偷菜被发现-宠物对战处理========================================= +#========================访问模式直接向巡逻宠物发起战斗======================== +func _on_battle_button_pressed() -> void: + # 检查是否为访问模式 + if not is_visiting_mode: + Toast.show("只有在访问模式下才能向巡逻宠物发起对战", Color.ORANGE, 3.0) + return + + # 检查对方是否有巡逻宠物 + if not visited_player_data.has("巡逻宠物") or visited_player_data["巡逻宠物"].size() == 0: + Toast.show("对方没有巡逻宠物,无法发起对战", Color.RED, 3.0) + return + + # 检查自己是否有出战宠物 + if not battle_pets or battle_pets.size() == 0: + Toast.show("你没有出战宠物,无法发起对战", Color.RED, 3.0) + return + + # 获取对方的巡逻宠物数据(取第一个)- 新格式 + var target_patrol_pet = visited_player_data["巡逻宠物"][0] + var target_patrol_pet_name = target_patrol_pet.get("pet_name", "未知宠物") + var target_patrol_pet_level = target_patrol_pet.get("pet_level", 1) + var target_patrol_pet_type = target_patrol_pet.get("pet_type", "未知类型") + + # 获取自己的出战宠物数据(取第一个)- 新格式 + var my_battle_pet = battle_pets[0] + var my_battle_pet_name = my_battle_pet.get("pet_name", "未知宠物") + var my_battle_pet_level = my_battle_pet.get("pet_level", 1) + var my_battle_pet_type = my_battle_pet.get("pet_type", "未知类型") + + # 检查是否为同一个宠物(防止冲突)- 新格式 + var my_battle_pet_id = my_battle_pet.get("pet_id", "") + var target_patrol_pet_id = target_patrol_pet.get("pet_id", "") + if check_battle_patrol_conflict(my_battle_pet_id, target_patrol_pet_id): + Toast.show("出战宠物和巡逻宠物不能为同一个!", Color.RED, 3.0) + return + + # 显示对战确认弹窗 + _show_battle_confirmation_dialog( + target_patrol_pet, + my_battle_pet, + visited_player_data.get("玩家昵称", "未知玩家") + ) + +# 显示对战确认弹窗 +func _show_battle_confirmation_dialog(target_patrol_pet: Dictionary, my_battle_pet: Dictionary, target_player_name: String) -> void: + # 构建对话框内容 + var dialog_content = "确定要向 %s 的巡逻宠物发起对战吗?\n\n" % target_player_name + + # 显示对方的巡逻宠物(所有) + dialog_content += "🛡️ 对方巡逻宠物:\n" + var defender_pets = visited_player_data.get("巡逻宠物", [target_patrol_pet]) + for i in range(min(4, defender_pets.size())): + var pet = defender_pets[i] + var pet_name = pet.get("pet_name", "未知宠物") + var pet_level = pet.get("pet_level", 1) + var pet_type = pet.get("pet_type", "未知类型") + dialog_content += " %d. %s (类型:%s, 等级:%d)\n" % [i+1, pet_name, pet_type, pet_level] + + dialog_content += "\n⚔️ 你的出战宠物:\n" + # 显示自己的出战宠物(所有) + var attacker_pets = battle_pets if battle_pets.size() > 0 else [my_battle_pet] + for i in range(min(4, attacker_pets.size())): + var pet = attacker_pets[i] + var pet_name = pet.get("pet_name", "未知宠物") + var pet_level = pet.get("pet_level", 1) + var pet_type = pet.get("pet_type", "未知类型") + dialog_content += " %d. %s (类型:%s, 等级:%d)\n" % [i+1, pet_name, pet_type, pet_level] + + dialog_content += "\n⚠️ 注意:对战失败可能会有惩罚!" + + # 使用现有的accept_dialog + if not accept_dialog: + Toast.show("对话框不可用", Color.RED, 2.0) + return + + # 清除之前的信号连接 + if accept_dialog.confirmed.is_connected(_on_direct_battle_confirmed): + accept_dialog.confirmed.disconnect(_on_direct_battle_confirmed) + if accept_dialog.canceled.is_connected(_on_direct_battle_canceled): + accept_dialog.canceled.disconnect(_on_direct_battle_canceled) + + # 设置对话框内容 + accept_dialog.set_dialog_title("宠物对战确认") + accept_dialog.set_dialog_content(dialog_content) + accept_dialog.set_ok_text("发起对战") + accept_dialog.set_cancel_text("取消") + + # 连接信号 + accept_dialog.confirmed.connect(_on_direct_battle_confirmed.bind(target_patrol_pet, my_battle_pet)) + accept_dialog.canceled.connect(_on_direct_battle_canceled) + + # 显示对话框 + accept_dialog.popup_centered() + +# 确认发起对战 +func _on_direct_battle_confirmed(target_patrol_pet: Dictionary, my_battle_pet: Dictionary) -> void: + # 获取玩家名称 + var my_name = show_player_name.text.replace("玩家昵称:", "") + var target_name = visited_player_data.get("玩家昵称", "对方") + + # 设置对战面板数据 + pet_battle_panel.setup_steal_battle( + battle_pets, # 传递完整的出战宠物数组 + visited_player_data.get("巡逻宠物", [target_patrol_pet]), # 传递完整的巡逻宠物数组 + my_name, + target_name + ) + + # 显示对战面板 + pet_battle_panel.show() + GlobalVariables.isZoomDisabled = true + + Toast.show("对战开始!", Color.GREEN, 2.0) + +# 取消发起对战 +func _on_direct_battle_canceled() -> void: + Toast.show("已取消对战", Color.GRAY, 2.0) + +# 更新对战按钮显示状态 +func _update_battle_button_visibility() -> void: + + # 检查对方是否有巡逻宠物 + var has_patrol_pets = visited_player_data.has("巡逻宠物") and visited_player_data["巡逻宠物"].size() > 0 + + # 检查自己是否有出战宠物 + var has_battle_pets = battle_pets and battle_pets.size() > 0 + + # 只有当对方有巡逻宠物且自己有出战宠物时才显示对战按钮 + if has_patrol_pets and has_battle_pets: + battle_button.show() + else: + battle_button.hide() +#========================访问模式直接向巡逻宠物发起战斗======================== + + + #=======================================智慧树系统========================================= #智慧树按钮点击 @@ -3238,7 +3452,6 @@ func _on_wisdom_tree_pressed() -> void: return wisdom_tree_panel.show() - # 更新智慧树显示 func update_wisdom_tree_display(): var config = login_data.get("智慧树配置", {}) @@ -3258,8 +3471,6 @@ func _update_wisdom_tree_display(config: Dictionary): tree_status.text = "等级lv:" + str(level) + " 高度:" + str(height) + "cm" if wisdom_tree_image: - var scale_factor = 0.5 + min((height - 20.0) / 80.0, 1.1) - wisdom_tree_image.scale = Vector2(scale_factor, scale_factor) if current_health <= 0: wisdom_tree_image.self_modulate = Color(0.5, 0.5, 0.5) @@ -3299,8 +3510,6 @@ func handle_wisdom_tree_response(data: Dictionary): # 确保智慧树配置格式正确 func _ensure_wisdom_tree_config_format(config: Dictionary) -> Dictionary: var new_config = config.duplicate() - - # 确保必需字段 for key in ["当前生命值", "最大生命值", "当前经验值"]: if not new_config.has(key): @@ -3361,22 +3570,129 @@ func _handle_save_game_settings_response(data): #打开小卖部面板 func _on_my_store_button_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人小卖部",Color.RED) + return player_store_panel.show() pass #打开小卖部面板 func _on_player_store_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人小卖部",Color.RED) + return player_store_panel.show() pass +#打开种子商店 func _on_seed_store_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人种子商店",Color.RED) + return crop_store_panel.show() pass +#打开道具商店 func _on_item_store_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人道具商店",Color.RED) + return item_store_panel.show() pass +#打开宠物商店 func _on_pet_store_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人宠物商店",Color.RED) + return pet_store_panel.show() pass + +#打开作物仓库 +func _on_crop_warehouse_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人作物仓库",Color.RED) + return + crop_warehouse_panel.show() + pass + +#打开种子仓库 +func _on_seed_warehouse_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人种子仓库",Color.RED) + return + crop_store_panel.show() + pass + +#打开玩家排行榜 +func _on_player_rank_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人玩家排行榜",Color.RED) + return + player_ranking_panel.show() + pass + +#打开每日签到 +func _on_daily_checkin_gift_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人每日签到礼包",Color.RED) + return + daily_check_in_panel.show() + pass + +#打开在线礼包 +func _on_online_time_gift_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人在线时长礼包",Color.RED) + return + online_gift_panel.show() + pass + +#打开宠物背包 +func _on_pet_bag_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人宠物背包",Color.RED) + return + pet_bag_panel.show() + pass + +#打开道具背包 +func _on_item_bag_pressed() -> void: + if is_visiting_mode: + Toast.show("您不能访问他人道具背包",Color.RED) + return + item_bag_panel.show() + pass + +# ======================================= 宠物对战系统 ========================================= +# 处理宠物对战结束 +func _on_pet_battle_ended(winner_team: String, battle_data: Dictionary): + """处理宠物对战结束后的逻辑""" + print("[宠物对战] 对战结束,获胜方: ", winner_team) + print("[宠物对战] 对战数据: ", battle_data) + + # 准备发送到服务器的对战结果数据 + var battle_result = { + "winner_team": winner_team, + "attacker_name": user_name, # 攻击方(玩家自己) + "defender_name": visited_player_data.get("玩家昵称", "未知玩家"), # 防守方(被访问玩家) + "battle_type": "steal_battle", # 对战类型:偷菜对战 + "attacker_pets": battle_data.get("attacker_pets", []), # 攻击方宠物数据 + "defender_pets": battle_data.get("defender_pets", []), # 防守方宠物数据 + "battle_duration": battle_data.get("battle_duration", 0), # 对战持续时间 + "timestamp": Time.get_unix_time_from_system() # 对战时间戳 + } + + # 发送对战结果到服务器 + if tcp_network_manager_panel and tcp_network_manager_panel.has_method("send_pet_battle_result"): + tcp_network_manager_panel.send_pet_battle_result(battle_result) + print("[宠物对战] 对战结果已发送到服务器") + else: + print("[宠物对战] 无法发送对战结果到服务器") + + # 显示对战结果提示 + if winner_team == "attacker": + Toast.show("恭喜!您在偷菜对战中获胜!", Color.GREEN) + else: + Toast.show("很遗憾,您在偷菜对战中失败了。", Color.RED) +# ======================================= 宠物对战系统 ========================================= diff --git a/MainGame.tscn b/MainGame.tscn index b8fb95d..f267d49 100644 --- a/MainGame.tscn +++ b/MainGame.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=97 format=3 uid="uid://dgh61dttaas5a"] +[gd_scene load_steps=105 format=3 uid="uid://dgh61dttaas5a"] [ext_resource type="Script" uid="uid://2pt11sfcaxf7" path="res://MainGame.gd" id="1_v3yaj"] [ext_resource type="Texture2D" uid="uid://du2pyiojliasy" path="res://assets/游戏UI/经验球.webp" id="2_6jgly"] @@ -39,6 +39,7 @@ [ext_resource type="Script" uid="uid://bvhupqlw2h1j8" path="res://Script/SmallPanel/DebugPanel.gd" id="28_8kysg"] [ext_resource type="Script" uid="uid://ca2chgx5w3g1y" path="res://Components/GameBGMPlayer.gd" id="28_m6fch"] [ext_resource type="Script" uid="uid://d4fvv2sjngajr" path="res://Script/Dialog/BatchBuyPopup.gd" id="29_5b81d"] +[ext_resource type="PackedScene" uid="uid://diwbnwhnq026" path="res://Scene/NewPet/PetBattlePanel.tscn" id="29_mw3xw"] [ext_resource type="PackedScene" uid="uid://dos15dmc1b6bt" path="res://GUI/GameSettingPanel.tscn" id="30_game_setting"] [ext_resource type="Texture2D" uid="uid://2sdfbvf1isif" path="res://icon.svg" id="31_uc6q1"] [ext_resource type="Script" uid="uid://doo34ll0yb078" path="res://Script/SmallPanel/PetInformPanel.gd" id="31_vygm6"] @@ -70,7 +71,14 @@ [ext_resource type="Texture2D" uid="uid://cqqyc3ddwtvpn" path="res://assets/天气系统图片/栀子花3.webp" id="55_iluto"] [ext_resource type="Texture2D" uid="uid://d4e54gh5iccul" path="res://assets/装饰物图片/道具商店.webp" id="56_rlmnt"] [ext_resource type="Texture2D" uid="uid://deow5dqdm412v" path="res://assets/装饰物图片/宠物商店.webp" id="57_rlmnt"] +[ext_resource type="Texture2D" uid="uid://dfeqilibb6ecs" path="res://assets/装饰物图片/作物仓库.webp" id="58_mjfri"] +[ext_resource type="Texture2D" uid="uid://b738esrtjlho7" path="res://assets/装饰物图片/种子仓库.webp" id="59_612dn"] +[ext_resource type="Texture2D" uid="uid://cbj1s6ctp0utb" path="res://assets/装饰物图片/玩家排行榜.webp" id="60_2fqxv"] [ext_resource type="Script" uid="uid://o4mcuqoivqri" path="res://GameManager/WeatherSystem.gd" id="62_uyv6e"] +[ext_resource type="Texture2D" uid="uid://deh0dnprkw155" path="res://assets/装饰物图片/每日签到礼包.webp" id="63_ekowe"] +[ext_resource type="Texture2D" uid="uid://ic2nvi3xlwl4" path="res://assets/装饰物图片/在线时长礼包.webp" id="64_crc4a"] +[ext_resource type="Texture2D" uid="uid://1xgmal8sw6il" path="res://assets/装饰物图片/宠物背包.webp" id="67_onvxb"] +[ext_resource type="Texture2D" uid="uid://ywdg7xgq7hm8" path="res://assets/装饰物图片/道具背包.webp" id="68_bpbm8"] [ext_resource type="Texture2D" uid="uid://bnv6wb0k443fv" path="res://assets/天气系统图片/柳叶2.webp" id="69_uyv6e"] [ext_resource type="Script" uid="uid://di8wjflimodb0" path="res://GameManager/DayNightSystem.gd" id="73_6fhdl"] @@ -277,6 +285,20 @@ fog_density = 1.0 [node name="main" type="Node"] script = ExtResource("1_v3yaj") +[node name="PetPatrolPoints" type="Node" parent="."] + +[node name="Pos1" type="Marker2D" parent="PetPatrolPoints"] +position = Vector2(-276, -151) + +[node name="Pos2" type="Marker2D" parent="PetPatrolPoints"] +position = Vector2(-276, 1052) + +[node name="Pos3" type="Marker2D" parent="PetPatrolPoints"] +position = Vector2(1391, 1052) + +[node name="Pos4" type="Marker2D" parent="PetPatrolPoints"] +position = Vector2(1391, -184) + [node name="UI" type="CanvasLayer" parent="."] [node name="GUI" type="Control" parent="UI"] @@ -602,6 +624,18 @@ layout_mode = 2 theme_override_font_sizes/font_size = 40 text = "点赞" +[node name="BattleButton" type="Button" parent="UI/GUI/VisitVBox"] +modulate = Color(0, 0.784782, 0.487639, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "发起对战" + +[node name="UseItemButton" type="Button" parent="UI/GUI/VisitVBox"] +modulate = Color(0.37232, 0.679193, 1, 1) +layout_mode = 2 +theme_override_font_sizes/font_size = 40 +text = "使用道具" + [node name="ReturnMyFarmButton" type="Button" parent="UI/GUI/VisitVBox"] modulate = Color(1, 1, 0.721569, 1) layout_mode = 2 @@ -916,6 +950,9 @@ visible = false [node name="GameSettingPanel" parent="UI/BigPanel" instance=ExtResource("30_game_setting")] visible = false +[node name="PetBattlePanel" parent="UI/BigPanel" instance=ExtResource("29_mw3xw")] +visible = false + [node name="SmallPanel" type="CanvasLayer" parent="UI"] [node name="LoadProgressPanel" parent="UI/SmallPanel" instance=ExtResource("27_vygm6")] @@ -1427,7 +1464,9 @@ offset_right = 1322.0 offset_bottom = 280.0 theme_override_colors/font_color = Color(0.654902, 1, 1, 1) theme_override_font_sizes/font_size = 30 -text = "只需花费一点点钱币,就可以匿名发送一句话,由于智慧树强大的链接性,其他玩家在浇水施肥时就有概率收到这句话。向他人发送一句祝福吧(,,・ω・,,) ,只要你不断的给智慧树浇水施肥,它就会长得越来越高" +text = "只需花费一点点钱币,就可以匿名发送一句话,由于智慧树强大的链接性, +其他玩家在浇水施肥时就有概率收到这句话。向他人发送一句祝福吧(,,・ω・,,) , +只要你不断的给智慧树浇水施肥,它就会长得越来越高" horizontal_alignment = 1 vertical_alignment = 1 autowrap_mode = 2 @@ -1881,14 +1920,6 @@ script = ExtResource("17_0igvr") [node name="Timer" type="Timer" parent="BackgroundUI/BackgroundSwitcher"] -[node name="PetPatrolPathLine" type="Line2D" parent="."] -position = Vector2(-52.4841, -115.095) -scale = Vector2(0.92426, 0.816774) -points = PackedVector2Array(-200, -100, -200, 1500, 1500, 1500, 1500, -100) -closed = true -width = 5.0 -default_color = Color(0, 1, 1, 1) - [node name="GridContainer" type="GridContainer" parent="."] z_as_relative = false custom_minimum_size = Vector2(100, 100) @@ -2056,6 +2087,7 @@ layout_mode = 0 offset_left = 13.0 offset_right = 390.0 offset_bottom = 113.0 +theme_override_constants/outline_size = 20 theme_override_font_sizes/bold_italics_font_size = 35 theme_override_font_sizes/italics_font_size = 35 theme_override_font_sizes/mono_font_size = 35 @@ -2207,7 +2239,7 @@ text = "宠物商店" horizontal_alignment = 1 vertical_alignment = 1 -[node name="Decoration5" type="Button" parent="Decoration"] +[node name="CropWarehouse" type="Button" parent="Decoration"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 610.0 @@ -2216,18 +2248,17 @@ offset_right = 874.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/Decoration5"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/CropWarehouse"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration/Decoration5"] +[node name="Image" type="Sprite2D" parent="Decoration/CropWarehouse"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) -texture = ExtResource("49_xjiif") +texture = ExtResource("58_mjfri") -[node name="Name" type="RichTextLabel" parent="Decoration/Decoration5"] -visible = false +[node name="Name" type="RichTextLabel" parent="Decoration/CropWarehouse"] layout_mode = 0 offset_left = -65.0 offset_top = -145.0 @@ -2239,11 +2270,11 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true -text = "稻草人" +text = "作物仓库" horizontal_alignment = 1 vertical_alignment = 1 -[node name="Decoration6" type="Button" parent="Decoration"] +[node name="SeedWarehouse" type="Button" parent="Decoration"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 732.0 @@ -2252,18 +2283,17 @@ offset_right = 996.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/Decoration6"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/SeedWarehouse"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration/Decoration6"] +[node name="Image" type="Sprite2D" parent="Decoration/SeedWarehouse"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) -texture = ExtResource("49_xjiif") +texture = ExtResource("59_612dn") -[node name="Name" type="RichTextLabel" parent="Decoration/Decoration6"] -visible = false +[node name="Name" type="RichTextLabel" parent="Decoration/SeedWarehouse"] layout_mode = 0 offset_left = -65.0 offset_top = -145.0 @@ -2275,11 +2305,11 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true -text = "稻草人" +text = "种子仓库" horizontal_alignment = 1 vertical_alignment = 1 -[node name="Decoration7" type="Button" parent="Decoration"] +[node name="PlayerRank" type="Button" parent="Decoration"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 854.0 @@ -2288,18 +2318,17 @@ offset_right = 1118.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/Decoration7"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration/PlayerRank"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration/Decoration7"] +[node name="Image" type="Sprite2D" parent="Decoration/PlayerRank"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) -texture = ExtResource("50_sqnmr") +texture = ExtResource("60_2fqxv") -[node name="Name" type="RichTextLabel" parent="Decoration/Decoration7"] -visible = false +[node name="Name" type="RichTextLabel" parent="Decoration/PlayerRank"] layout_mode = 0 offset_left = -65.0 offset_top = -145.0 @@ -2311,7 +2340,7 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true -text = "稻草人" +text = "[rainbow]玩家排行榜[/rainbow]" horizontal_alignment = 1 vertical_alignment = 1 @@ -2330,16 +2359,17 @@ scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") [node name="WisdomTreeImage" type="Sprite2D" parent="Decoration/WisdomTree"] -position = Vector2(126, 82) -scale = Vector2(0.5, 0.5) +position = Vector2(135, -107.5) +scale = Vector2(2.5, 2.5) texture = ExtResource("45_xvovi") [node name="TreeName" type="Label" parent="Decoration/WisdomTree"] self_modulate = Color(2, 2, 2, 1) layout_mode = 0 -offset_top = -145.0 -offset_right = 265.0 -offset_bottom = -96.0 +offset_left = 37.0 +offset_top = -450.0 +offset_right = 302.0 +offset_bottom = -401.0 theme_override_font_sizes/font_size = 35 text = "智慧树" horizontal_alignment = 1 @@ -2348,10 +2378,10 @@ vertical_alignment = 1 [node name="TreeStatus" type="Label" parent="Decoration/WisdomTree"] self_modulate = Color(2, 2, 2, 1) layout_mode = 0 -offset_left = -122.0 -offset_top = -190.0 -offset_right = 370.0 -offset_bottom = -141.0 +offset_left = -85.0 +offset_top = -495.0 +offset_right = 407.0 +offset_bottom = -446.0 theme_override_font_sizes/font_size = 35 text = "等级lv:99 高度:99m" horizontal_alignment = 1 @@ -2359,10 +2389,10 @@ vertical_alignment = 1 [node name="BackgroundPanel" type="Panel" parent="Decoration/WisdomTree"] layout_mode = 0 -offset_left = -212.0 -offset_top = -350.0 -offset_right = 491.0 -offset_bottom = -195.0 +offset_left = -175.0 +offset_top = -655.0 +offset_right = 528.0 +offset_bottom = -500.0 theme_override_styles/panel = SubResource("StyleBoxFlat_6ylhg") [node name="AnonymousTalk" type="RichTextLabel" parent="Decoration/WisdomTree/BackgroundPanel"] @@ -2370,6 +2400,8 @@ layout_mode = 0 offset_left = 13.0 offset_right = 695.0 offset_bottom = 155.0 +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 15 theme_override_font_sizes/bold_italics_font_size = 35 theme_override_font_sizes/italics_font_size = 35 theme_override_font_sizes/mono_font_size = 35 @@ -3107,7 +3139,7 @@ vertical_alignment = 1 [node name="Decoration4" type="Node2D" parent="."] position = Vector2(0, 976) -[node name="ScareCrow" type="Button" parent="Decoration4"] +[node name="DailyCheckinGift" type="Button" parent="Decoration4"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_top = -108.0 @@ -3115,16 +3147,17 @@ offset_right = 264.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/ScareCrow"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/DailyCheckinGift"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration4/ScareCrow"] +[node name="Image" type="Sprite2D" parent="Decoration4/DailyCheckinGift"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) +texture = ExtResource("63_ekowe") -[node name="Name" type="RichTextLabel" parent="Decoration4/ScareCrow"] +[node name="Name" type="RichTextLabel" parent="Decoration4/DailyCheckinGift"] layout_mode = 0 offset_left = -65.0 offset_top = -145.0 @@ -3136,10 +3169,11 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true +text = "每日签到礼包" horizontal_alignment = 1 vertical_alignment = 1 -[node name="Decoration1" type="Button" parent="Decoration4"] +[node name="OnlineTimeGift" type="Button" parent="Decoration4"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 122.0 @@ -3148,18 +3182,17 @@ offset_right = 386.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/Decoration1"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/OnlineTimeGift"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration4/Decoration1"] +[node name="Image" type="Sprite2D" parent="Decoration4/OnlineTimeGift"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) -texture = ExtResource("49_xjiif") +texture = ExtResource("64_crc4a") -[node name="Name" type="RichTextLabel" parent="Decoration4/Decoration1"] -visible = false +[node name="Name" type="RichTextLabel" parent="Decoration4/OnlineTimeGift"] layout_mode = 0 offset_left = -65.0 offset_top = -145.0 @@ -3171,7 +3204,7 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true -text = "稻草人" +text = "在线时长礼包" horizontal_alignment = 1 vertical_alignment = 1 @@ -3355,7 +3388,7 @@ text = "稻草人" horizontal_alignment = 1 vertical_alignment = 1 -[node name="Decoration7" type="Button" parent="Decoration4"] +[node name="PetBag" type="Button" parent="Decoration4"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 854.0 @@ -3364,18 +3397,17 @@ offset_right = 1118.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/Decoration7"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/PetBag"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Image" type="Sprite2D" parent="Decoration4/Decoration7"] +[node name="Image" type="Sprite2D" parent="Decoration4/PetBag"] position = Vector2(132, 48) scale = Vector2(1.2, 1.2) -texture = ExtResource("50_sqnmr") +texture = ExtResource("67_onvxb") -[node name="Name" type="RichTextLabel" parent="Decoration4/Decoration7"] -visible = false +[node name="Name" type="RichTextLabel" parent="Decoration4/PetBag"] layout_mode = 0 offset_left = -65.0 offset_top = -145.0 @@ -3387,11 +3419,11 @@ theme_override_font_sizes/mono_font_size = 40 theme_override_font_sizes/normal_font_size = 40 theme_override_font_sizes/bold_font_size = 40 bbcode_enabled = true -text = "稻草人" +text = "宠物背包" horizontal_alignment = 1 vertical_alignment = 1 -[node name="WisdomTree" type="Button" parent="Decoration4"] +[node name="ItemBag" type="Button" parent="Decoration4"] self_modulate = Color(1, 1, 1, 0) custom_minimum_size = Vector2(100, 100) offset_left = 976.0 @@ -3400,18 +3432,24 @@ offset_right = 1240.0 offset_bottom = 156.0 scale = Vector2(0.4, 0.4) -[node name="Image" type="Sprite2D" parent="Decoration4/WisdomTree"] +[node name="GrassGroundImage" type="Sprite2D" parent="Decoration4/ItemBag"] position = Vector2(132, 134) scale = Vector2(1.4, 1.4) texture = ExtResource("48_2i8fe") -[node name="Name" type="Label" parent="Decoration4/WisdomTree"] +[node name="Image" type="Sprite2D" parent="Decoration4/ItemBag"] +position = Vector2(132, 48) +scale = Vector2(1.2, 1.2) +texture = ExtResource("68_bpbm8") + +[node name="Name" type="Label" parent="Decoration4/ItemBag"] self_modulate = Color(2, 2, 2, 1) layout_mode = 0 offset_top = -145.0 offset_right = 265.0 offset_bottom = -96.0 theme_override_font_sizes/font_size = 35 +text = "道具背包" horizontal_alignment = 1 vertical_alignment = 1 @@ -3568,6 +3606,7 @@ environment = SubResource("Environment_uyv6e") [connection signal="pressed" from="UI/GUI/FarmVBox/OneClickPlantButton" to="." method="_on_one_click_plant_button_pressed"] [connection signal="pressed" from="UI/GUI/FarmVBox/AddNewGroundButton" to="." method="_on_add_new_ground_button_pressed"] [connection signal="pressed" from="UI/GUI/VisitVBox/LikeButton" to="." method="_on_like_button_pressed"] +[connection signal="pressed" from="UI/GUI/VisitVBox/BattleButton" to="." method="_on_battle_button_pressed"] [connection signal="pressed" from="UI/GUI/VisitVBox/ReturnMyFarmButton" to="." method="_on_return_my_farm_button_pressed"] [connection signal="pressed" from="UI/GUI/OtherVBox/MyStoreButton" to="." method="_on_my_store_button_pressed"] [connection signal="pressed" from="UI/GUI/OtherVBox/AccountSettingButton" to="." method="_on_account_setting_button_pressed"] @@ -3590,6 +3629,11 @@ environment = SubResource("Environment_uyv6e") [connection signal="pressed" from="Decoration/SeedStore" to="." method="_on_seed_store_pressed"] [connection signal="pressed" from="Decoration/ItemStore" to="." method="_on_item_store_pressed"] [connection signal="pressed" from="Decoration/PetStore" to="." method="_on_pet_store_pressed"] +[connection signal="pressed" from="Decoration/CropWarehouse" to="." method="_on_crop_warehouse_pressed"] +[connection signal="pressed" from="Decoration/SeedWarehouse" to="." method="_on_seed_warehouse_pressed"] +[connection signal="pressed" from="Decoration/PlayerRank" to="." method="_on_player_rank_pressed"] [connection signal="pressed" from="Decoration/WisdomTree" to="." method="_on_wisdom_tree_pressed"] -[connection signal="pressed" from="Decoration4/ScareCrow" to="." method="_on_scare_crow_pressed"] -[connection signal="pressed" from="Decoration4/WisdomTree" to="." method="_on_wisdom_tree_pressed"] +[connection signal="pressed" from="Decoration4/DailyCheckinGift" to="." method="_on_daily_checkin_gift_pressed"] +[connection signal="pressed" from="Decoration4/OnlineTimeGift" to="." method="_on_online_time_gift_pressed"] +[connection signal="pressed" from="Decoration4/PetBag" to="." method="_on_pet_bag_pressed"] +[connection signal="pressed" from="Decoration4/ItemBag" to="." method="_on_item_bag_pressed"] diff --git a/Network/TCPNetworkManager.gd b/Network/TCPNetworkManager.gd index e9ce6fe..e532e70 100644 --- a/Network/TCPNetworkManager.gd +++ b/Network/TCPNetworkManager.gd @@ -64,6 +64,9 @@ func _ready(): # 创建TCP客户端实例 self.add_child(client) + # 禁用自动重连,避免频繁的本地连接 + client.auto_reconnect = false + # 连接信号 client.connected_to_server.connect(_on_connected) client.connection_failed.connect(_on_connection_failed) @@ -345,12 +348,24 @@ func _on_data_received(data): elif action_type == "set_patrol_pet": if success: main_game.patrol_pets = updated_data["巡逻宠物"] - main_game.update_patrol_pets() + main_game.update_patrol_pets(updated_data["巡逻宠物"]) pet_inform_panel._refresh_patrol_button() Toast.show(message, Color.GREEN) else: Toast.show(message, Color.RED) + # 设置出战宠物响应 + elif action_type == "set_battle_pet": + if success: + main_game.battle_pets = updated_data["出战宠物"] + pet_inform_panel._refresh_battle_button() + # 更新对战按钮显示状态 + if main_game.has_method("_update_battle_button_visibility"): + main_game._update_battle_button_visibility() + Toast.show(message, Color.GREEN) + else: + Toast.show(message, Color.RED) + # 使用道具响应 elif action_type == "use_item": if success: @@ -533,6 +548,11 @@ func _on_data_received(data): main_game._handle_crop_data_response(data) elif message_type == "item_config_response": main_game._handle_item_config_response(data) + elif message_type == "pet_config_response": + # 保存宠物配置到MainGame + main_game._handle_pet_config_response(data) + # 同时传递给宠物商店面板 + pet_store_panel._on_pet_config_received(data) elif message_type == "visit_player_response": main_game._handle_visit_player_response(data) elif message_type == "return_my_farm_response": @@ -934,6 +954,16 @@ func sendGetItemConfig(): }) return true +#发送获取宠物配置数据请求 +func sendGetPetConfig(): + if not client.is_client_connected(): + return false + + client.send_data({ + "type": "request_pet_config" + }) + return true + #发送访问玩家请求 func sendVisitPlayer(target_username): if not client.is_client_connected(): @@ -1169,6 +1199,18 @@ func send_get_wisdom_tree_config(): }) return true +#发送宠物对战结果 +func send_pet_battle_result(battle_result: Dictionary): + if not client.is_client_connected(): + return false + + client.send_data({ + "type": "pet_battle_result", + "battle_result": battle_result, + "timestamp": Time.get_unix_time_from_system() + }) + return true + #检查是否连接到服务器 func is_connected_to_server(): return client.is_client_connected() diff --git a/Scene/BigPanel/PlayerRankingPanel.tscn b/Scene/BigPanel/PlayerRankingPanel.tscn index 7fda763..cdd9f89 100644 --- a/Scene/BigPanel/PlayerRankingPanel.tscn +++ b/Scene/BigPanel/PlayerRankingPanel.tscn @@ -69,7 +69,7 @@ offset_top = 21.25 offset_right = 378.0 offset_bottom = 78.25 theme_override_font_sizes/font_size = 35 -text = "输入要搜索的人" +placeholder_text = "输入要搜索的人" [node name="RefreshButton" type="Button" parent="."] custom_minimum_size = Vector2(55, 55) diff --git a/Scene/NewPet/BulletBase.gd b/Scene/NewPet/BulletBase.gd new file mode 100644 index 0000000..47be326 --- /dev/null +++ b/Scene/NewPet/BulletBase.gd @@ -0,0 +1,368 @@ +extends Area2D +class_name BulletBase + +# 简化子弹系统 - 辅助攻击用 +# 基础功能:移动、碰撞检测、伤害 + +signal bullet_hit(bullet: BulletBase, target: NewPetBase) + +@onready var sprite: Sprite2D = $Sprite +@onready var collision_shape: CollisionShape2D = $CollisionShape2D + +const bullet = { + "小蓝弹":{ + "图片":"res://assets/子弹图片/01.png", + "函数":"create_blue_bullet" + }, + "小红弹":{ + "图片":"res://assets/子弹图片/02.png", + "函数":"create_red_bullet" + }, + "小粉弹":{ + "图片":"res://assets/子弹图片/03.png", + "函数":"create_pink_bullet" + }, + "小紫弹":{ + "图片":"res://assets/子弹图片/04.png", + "函数":"create_purple_bullet" + }, + "长橙弹":{ + "图片":"res://assets/子弹图片/21.png", + "函数":"create_long_orange_bullet" + }, + "长紫弹":{ + "图片":"res://assets/子弹图片/22.png", + "函数":"create_long_purple_bullet" + }, + "长绿弹":{ + "图片":"res://assets/子弹图片/25.png", + "函数":"create_long_green_bullet" + }, + "黄色闪电":{ + "图片":"res://assets/子弹图片/36.png", + "函数":"create_yellow_lightning_bullet" + }, + "绿色闪电":{ + "图片":"res://assets/子弹图片/35.png", + "函数":"create_green_lightning_bullet" + }, + "红色闪电":{ + "图片":"res://assets/子弹图片/33.png", + "函数":"create_red_lightning_bullet" + }, + "紫色闪电":{ + "图片":"res://assets/子弹图片/32.png", + "函数":"create_purple_lightning_bullet" + }, +} + + +# 基础子弹属性 +var direction: Vector2 = Vector2.RIGHT +var speed: float = 300.0 +var damage: float = 25.0 +var owner_pet: NewPetBase = null +var max_distance: float = 800.0 +var traveled_distance: float = 0.0 +var is_active: bool = true + +# 生存时间 +var lifetime: float = 3.0 +var current_lifetime: float = 0.0 + + +#==================基础函数========================= +func _ready(): + # 连接碰撞信号 + area_entered.connect(_on_area_entered) + + # 设置碰撞层 + collision_layer = 4 # 子弹层 + collision_mask = 1 # 只检测宠物层 + + # 添加到子弹组 + add_to_group("bullets") + +func _physics_process(delta): + if not is_active: + return + + # 更新生存时间 + current_lifetime += delta + if current_lifetime >= lifetime: + call_deferred("destroy_bullet") + return + + # 检查长紫弹分裂计时 + if has_meta("bullet_type") and get_meta("bullet_type") == "长紫弹": + var current_split_time = get_meta("current_split_time", 0.0) + var split_timer = get_meta("split_timer", 2.0) + current_split_time += delta + set_meta("current_split_time", current_split_time) + + if current_split_time >= split_timer: + # 时间到了,分裂并销毁 + call_deferred("create_split_bullets", global_position) + call_deferred("destroy_bullet") + return + + # 移动子弹 + var movement = direction * speed * delta + position += movement + traveled_distance += movement.length() + + # 检查最大距离 + if traveled_distance >= max_distance: + call_deferred("destroy_bullet") + return + + # 检查是否超出屏幕边界 + var viewport_rect = get_viewport().get_visible_rect() + if not viewport_rect.has_point(global_position): + call_deferred("destroy_bullet") +#==================基础函数========================= + + +#=============================每个子弹单独效果============================ +# 小蓝弹 +func create_blue_bullet(): + sprite.texture = load(bullet["小蓝弹"]["图片"]) + speed = 250.0 + damage = 20.0 + lifetime = 2.5 + sprite.modulate = Color(0.5, 0.8, 1.0, 1.0) # 蓝色调 + +# 小红弹 +func create_red_bullet(): + sprite.texture = load(bullet["小红弹"]["图片"]) + speed = 300.0 + damage = 25.0 + lifetime = 3.0 + sprite.modulate = Color(1.0, 0.5, 0.5, 1.0) # 红色调 + +# 小粉弹 +func create_pink_bullet(): + sprite.texture = load(bullet["小粉弹"]["图片"]) + speed = 280.0 + damage = 22.0 + lifetime = 2.8 + sprite.modulate = Color(1.0, 0.7, 0.9, 1.0) # 粉色调 + +# 小紫弹 +func create_purple_bullet(): + sprite.texture = load(bullet["小紫弹"]["图片"]) + speed = 320.0 + damage = 28.0 + lifetime = 3.2 + sprite.modulate = Color(0.8, 0.5, 1.0, 1.0) # 紫色调 + +# 长橙弹 +func create_long_orange_bullet(): + sprite.texture = load(bullet["长橙弹"]["图片"]) + speed = 350.0 + damage = 35.0 + lifetime = 4.0 + max_distance = 1000.0 + sprite.modulate = Color(1.0, 0.7, 0.3, 1.0) # 橙色调 + +# 长紫弹 +func create_long_purple_bullet(): + sprite.texture = load(bullet["长紫弹"]["图片"]) + speed = 330.0 + damage = 32.0 + lifetime = 3.8 + max_distance = 950.0 + sprite.modulate = Color(0.9, 0.4, 1.0, 1.0) # 深紫色调 + # 标记为长紫弹,2秒后自动分裂 + set_meta("bullet_type", "长紫弹") + set_meta("split_timer", 2.0) # 2秒后分裂 + set_meta("current_split_time", 0.0) # 当前计时 + +# 创建分裂子弹(长紫弹2秒后的效果) +func create_split_bullets(hit_position: Vector2): + """在指定位置生成4个小弹向四周发射""" + var bullet_types = ["小蓝弹", "小红弹", "小粉弹", "小紫弹"] + var bullet_scene = preload("res://Scene/NewPet/BulletBase.tscn") + + # 生成4个方向的子弹 + for i in range(4): + # 计算方向角度(每90度一个方向) + var angle = i * PI / 2.0 + var direction_vector = Vector2(cos(angle), sin(angle)) + + # 选择子弹类型(每种类型1个) + var bullet_type = bullet_types[i] + + # 创建子弹实例 + var new_bullet = bullet_scene.instantiate() + get_parent().add_child(new_bullet) + + # 设置子弹位置 + new_bullet.global_position = hit_position + + # 设置子弹属性(5个参数:方向、速度、伤害、所有者、子弹类型) + new_bullet.setup(direction_vector, 200.0, 15.0, owner_pet, bullet_type) + + # 分裂子弹生成完成 + +# 长绿弹 +func create_long_green_bullet(): + sprite.texture = load(bullet["长绿弹"]["图片"]) + speed = 310.0 + damage = 30.0 + lifetime = 3.5 + max_distance = 900.0 + sprite.modulate = Color(0.4, 1.0, 0.5, 1.0) # 绿色调 + +# 黄色闪电 +func create_yellow_lightning_bullet(): + sprite.texture = load(bullet["黄色闪电"]["图片"]) + speed = 400.0 + damage = 40.0 + lifetime = 2.0 + max_distance = 600.0 + sprite.modulate = Color(1.0, 1.0, 0.3, 1.0) # 黄色闪电 + +# 绿色闪电 +func create_green_lightning_bullet(): + sprite.texture = load(bullet["绿色闪电"]["图片"]) + speed = 380.0 + damage = 38.0 + lifetime = 2.2 + max_distance = 650.0 + sprite.modulate = Color(0.3, 1.0, 0.3, 1.0) # 绿色闪电 + +# 红色闪电 +func create_red_lightning_bullet(): + sprite.texture = load(bullet["红色闪电"]["图片"]) + speed = 420.0 + damage = 45.0 + lifetime = 1.8 + max_distance = 550.0 + sprite.modulate = Color(1.0, 0.3, 0.3, 1.0) # 红色闪电 + +# 紫色闪电 +func create_purple_lightning_bullet(): + sprite.texture = load(bullet["紫色闪电"]["图片"]) + speed = 450.0 + damage = 50.0 + lifetime = 1.5 + max_distance = 500.0 + sprite.modulate = Color(0.8, 0.3, 1.0, 1.0) # 紫色闪电 +#=============================每个子弹单独效果============================ + + + +#=========================通用子弹函数============================== +# 通用子弹创建函数 +func create_bullet_by_name(bullet_name: String): + """根据子弹名称创建对应类型的子弹""" + if not bullet.has(bullet_name): + return + + var bullet_data = bullet[bullet_name] + var function_name = bullet_data.get("函数", "") + + if function_name != "": + call(function_name) + + +# 获取子弹图标 +func get_bullet_icon(bullet_name: String) -> Texture2D: + if bullet.has(bullet_name) and bullet[bullet_name].has("图片"): + return load(bullet[bullet_name]["图片"]) + else: + return null + +# 获取所有子弹名称列表 +func get_all_bullet_names() -> Array: + return bullet.keys() + +# 创建击中特效 +func create_hit_effect(pos: Vector2): + pass + +#销毁子弹 +func destroy_bullet(): + """销毁子弹""" + is_active = false + remove_from_group("bullets") + queue_free() + +#初始化子弹 +func setup(dir: Vector2, spd: float, dmg: float, owner: NewPetBase, bullet_type: String = ""): + """初始化子弹""" + direction = dir.normalized() + owner_pet = owner + + # 如果指定了子弹类型,使用对应的创建函数 + if bullet_type != "" and bullet.has(bullet_type): + create_bullet_by_name(bullet_type) + else: + # 使用传入的参数作为默认值 + speed = spd + damage = dmg + # 简单的视觉效果 + sprite.modulate = Color.YELLOW + + # 设置子弹旋转 + rotation = direction.angle() + +#碰撞检测 +func _on_area_entered(area: Area2D): + """碰撞检测""" + if not is_active: + return + + # 检查是否是宠物 + if not area is NewPetBase: + return + + var pet_target = area as NewPetBase + + # 检查是否是有效目标 + if not is_valid_target(pet_target): + return + + # 造成伤害并延迟销毁子弹(避免在物理查询期间修改状态) + hit_target(pet_target) + call_deferred("destroy_bullet") + +#检查是否是有效攻击目标 +func is_valid_target(target: NewPetBase) -> bool: + """检查是否是有效攻击目标""" + # 检查owner_pet是否有效 + if not is_instance_valid(owner_pet): + owner_pet = null + + # 不能攻击自己的主人 + if target == owner_pet: + return false + + # 不能攻击同队伍 + if owner_pet != null and target.pet_team == owner_pet.pet_team: + return false + + # 不能攻击已死亡的宠物 + if not target.is_alive: + return false + + return true + +#击中目标 +func hit_target(target: NewPetBase): + """击中目标""" + # 检查owner_pet是否有效(防止已释放对象错误) + if not is_instance_valid(owner_pet): + owner_pet = null + + # 造成伤害 + target.take_damage(damage, owner_pet) + + # 发射信号 + bullet_hit.emit(self, target) + + # 创建击中特效 + create_hit_effect(target.global_position) + +#=========================通用子弹函数============================== diff --git a/Scene/NewPet/BulletBase.gd.uid b/Scene/NewPet/BulletBase.gd.uid new file mode 100644 index 0000000..aaac367 --- /dev/null +++ b/Scene/NewPet/BulletBase.gd.uid @@ -0,0 +1 @@ +uid://bt57qac8hmg1u diff --git a/Scene/NewPet/BulletBase.tscn b/Scene/NewPet/BulletBase.tscn new file mode 100644 index 0000000..fc4bc22 --- /dev/null +++ b/Scene/NewPet/BulletBase.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=4 format=3 uid="uid://cqtv1dob3dm8b"] + +[ext_resource type="Script" uid="uid://bt57qac8hmg1u" path="res://Scene/NewPet/BulletBase.gd" id="1_guena"] +[ext_resource type="Texture2D" uid="uid://by01joyt7e4qh" path="res://assets/子弹图片/01.png" id="2_2q4gn"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_1"] +radius = 8.0 + +[node name="BulletBase" type="Area2D"] +script = ExtResource("1_guena") + +[node name="Sprite" type="Sprite2D" parent="."] +modulate = Color(1, 1, 0, 0.8) +scale = Vector2(0.5, 0.5) +texture = ExtResource("2_2q4gn") + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CircleShape2D_1") + +[node name="Trail" type="Line2D" parent="."] +width = 2.0 +default_color = Color(1, 1, 0, 0.5) + +[node name="HitEffect" type="Node2D" parent="."] + +[node name="LifeTimer" type="Timer" parent="."] +wait_time = 5.0 +one_shot = true +autostart = true diff --git a/Scene/NewPet/NewPetBase.gd b/Scene/NewPet/NewPetBase.gd new file mode 100644 index 0000000..00daf40 --- /dev/null +++ b/Scene/NewPet/NewPetBase.gd @@ -0,0 +1,1198 @@ +extends Area2D +class_name NewPetBase + +#============================信号管理============================== +signal pet_died(pet: NewPetBase) +signal pet_attacked(attacker: NewPetBase, target: NewPetBase, damage: float) +signal pet_skill_used(pet: NewPetBase, skill_name: String) +#============================信号管理============================== + + +#============================节点引用=============================== +# 节点引用 +@onready var pet_image: AnimatedSprite2D = $PetImage +@onready var left_tool_image: Sprite2D = $PetImage/LeftToolImage +@onready var right_tool_image: Sprite2D = $PetImage/RightToolImage +@onready var volume_collision: CollisionShape2D = $VolumeCollision + +# UI节点引用 +@onready var pet_inform_vbox: VBoxContainer = $PetInformVBox +@onready var pet_name_rich_text: RichTextLabel = $PetInformVBox/PetNameRichText +@onready var armor_bar: ProgressBar = $PetInformVBox/ArmorBar +@onready var armor_label: Label = $PetInformVBox/ArmorBar/ArmorLabel +@onready var shield_bar: ProgressBar = $PetInformVBox/ShieldBar +@onready var shield_label: Label = $PetInformVBox/ShieldBar/ShieldLabel +@onready var health_bar: ProgressBar = $PetInformVBox/HealthBar +@onready var health_label: Label = $PetInformVBox/HealthBar/HealthLabel +#============================节点引用=============================== + + +#============================枚举=============================== +# 攻击类型枚举 +enum AttackType { + MELEE # 近战攻击 +} + +enum ElementType { + NONE, METAL, WOOD, WATER, FIRE, EARTH, THUNDER +} + +enum PetState { + IDLE, # 待机 + MOVING, # 移动 + ATTACKING, # 攻击中 + SKILL_CASTING, # 释放技能 + PATROLLING, # 巡逻 + DEAD # 死亡 +} +#============================枚举=============================== + + +#============================宠物所有属性=============================== +# 基本属性 +var pet_name: String = "萌芽小绿" # 宠物名称 +var pet_team: String = "attacker" # 所属队伍(attacker进攻方 或 defender防守方) +var pet_id: String = "0001" # 宠物唯一编号 +var pet_type: String = "小绿" # 宠物种类 +var pet_level: int = 50 # 宠物等级 +var pet_image_path: String = "" # 宠物图片路径 + +# 生命与防御 +var max_health: float = 200.0 # 最大生命值 +var current_health: float = 200.0 # 当前生命值 +var enable_health_regen: bool = true # 是否开启生命恢复 +var health_regen: float = 1.0 # 每秒生命恢复大小 +var enable_shield_regen: bool = true # 是否开启护盾恢复 +var max_shield: float = 100.0 # 最大护盾值 +var current_shield: float = 100.0 # 当前护盾值 +var shield_regen: float = 1.0 # 每秒护盾恢复大小 +var max_armor: float = 100.0 # 最大护甲值 +var current_armor: float = 100.0 # 当前护甲值 + +# 攻击属性 +var attack_type: AttackType = AttackType.MELEE # 攻击类型(仅近战) +var base_attack_damage: float = 25.0 # 基础攻击力 +var attack_range: float = 100.0 # 攻击范围(近战或远程都适用) +var attack_speed: float = 1.0 # 每秒攻击次数(攻速) +var crit_rate: float = 0.1 # 暴击几率(0~1) +var crit_damage: float = 1.5 # 暴击伤害倍率(1.5 = 150%伤害) +var armor_penetration: float = 0.0 # 护甲穿透值(无视对方部分护甲) + +# 技能-多发射击 +var enable_multi_projectile_skill: bool = false +var projectile_speed: float = 300.0 # 投射物飞行速度 +var multi_projectile_count: int = 0 # 多发射击触发标记(0=未触发,1=已触发) +var multi_projectile_delay: float = 2 # 多发射击延迟时间(秒) +var multi_projectile_spread: float = 10.0 # 多发射击角度范围(度) +var spawn_time: float = 0.0 # 宠物生成时间 + +# 技能-狂暴模式 +var enable_berserker_skill: bool = false +var berserker_threshold: float = 0.3 # 狂暴触发阈值(生命值百分比) +var berserker_bonus: float = 1.5 # 狂暴伤害加成 +var berserker_duration: float = 5.0 # 狂暴持续时间(秒) +var berserker_triggered: bool = false # 是否已触发过狂暴(防止重复触发) +var is_berserker: bool = false # 是否处于狂暴状态 +var berserker_end_time: float = 0.0 # 狂暴结束时间 + +#技能-自爆 +var enable_self_destruct_skill: bool = false +var self_destruct_damage: float = 50.0 # 自爆伤害值 + +#技能-召唤小弟 +var enable_summon_pet_skill: bool = false +var summon_health_threshold: float = 0.5 # 召唤触发阈值(生命值百分比) +var summon_count: int = 1 # 召唤小弟数量 +var summon_triggered: bool = false # 是否已触发过召唤(防止重复触发) +var summon_scale: float = 0.1 # 召唤小弟属性缩放比例(10%) + +#技能-死亡重生 +var enable_death_respawn_skill: bool = false +var respawn_health_percentage: float = 0.3 # 重生时恢复的血量百分比(30%) +var max_respawn_count: int = 1 # 最大重生次数 +var current_respawn_count: int = 0 # 当前已重生次数 + +#击退效果 +var enable_knockback: bool = true # 是否启用击退效果 +var knockback_force: float = 300.0 # 击退力度(像素/秒) +var knockback_duration: float = 0.8 # 击退持续时间(秒) +var knockback_velocity: Vector2 = Vector2.ZERO # 当前击退速度 +var knockback_end_time: float = 0.0 # 击退结束时间 +var is_being_knocked_back: bool = false # 是否正在被击退 + +# 边界限制 +var boundary_min: Vector2 = Vector2(0, 0) # 边界最小坐标 +var boundary_max: Vector2 = Vector2(1400, 720) # 边界最大坐标 +var boundary_damage: float = 10.0 # 碰撞边界时受到的伤害 +var boundary_bounce_force: float = 200.0 # 边界反弹力度 + + +# 移动属性 +var move_speed: float = 150.0 # 移动速度(像素/秒) +var dodge_rate: float = 0.05 # 闪避概率(0~1) + +# 元素属性 +var element_type: ElementType = ElementType.NONE # 元素类型(例如火、水、雷等) +var element_damage_bonus: float = 50.0 # 元素伤害加成(额外元素伤害) + + +# 武器系统 +var left_weapon: String = "" # 左手武器名称 +var right_weapon: String = "" # 右手武器名称 +var weapon_system: WeaponBase # 武器系统引用 + +# 巡逻状态 +var is_patrolling: bool = false # 是否正在巡逻 +var patrol_path: PackedVector2Array = [] # 巡逻路径点 +var patrol_speed: float = 80.0 # 巡逻移动速度 +var current_patrol_index: int = 0 # 当前巡逻目标点索引 +var patrol_wait_time: float = 0.0 # 在巡逻点的等待时间 +var patrol_max_wait_time: float = 1.0 # 在巡逻点的最大等待时间 + +# 巡逻随机走动 +var patrol_center_position: Vector2 = Vector2.ZERO # 巡逻中心点位置 +var patrol_radius: float = 150.0 # 巡逻半径 +var patrol_target_position: Vector2 = Vector2.ZERO # 当前巡逻目标位置 +var patrol_move_timer: float = 0.0 # 巡逻移动计时器 +var patrol_move_interval: float = 2.0 # 巡逻移动间隔(秒) + +# 战斗控制 +var combat_enabled: bool = true # 是否启用战斗行为 + +#============================宠物所有属性=============================== + + + +#============================杂项未处理=============================== +# 状态变量 +var current_state: PetState = PetState.IDLE # 当前状态(待机、移动、攻击等) +var current_target: NewPetBase = null # 当前目标(敌方宠物对象) +var last_attack_time: float = 0.0 # 上次攻击时间(用于计算攻速冷却) +var velocity: Vector2 = Vector2.ZERO # 当前移动速度(方向与速度) +var is_alive: bool = true # 是否存活 + +# 子弹场景 +var bullet_scene: PackedScene = preload("res://Scene/NewPet/BulletBase.tscn") + +# 性能优化变量 +var update_ui_timer: float = 0.0 +var ui_update_interval: float = 0.2 # UI更新间隔,减少频繁更新 +var ai_update_timer: float = 0.0 +var ai_update_interval: float = 0.05 # AI更新间隔,平衡性能和反应速度 +#============================杂项未处理=============================== + + +#====================基础方法========================= +func _ready(): + # 记录生成时间 + spawn_time = Time.get_ticks_msec() / 1000.0 + #默认佩戴武器测试武器系统 + equip_weapon("钻石剑", "left") + equip_weapon("铁镐", "right") + + # 初始化武器系统 + init_weapon_system() + + # 初始化UI + update_ui() + # 设置碰撞层和掩码 + setup_collision_layers() + # 延迟一帧后开始AI,确保所有宠物都已生成 + await get_tree().process_frame + _start_ai() + +func _physics_process(delta): + if not is_alive: + return + + # 更新计时器 + update_ui_timer += delta + ai_update_timer += delta + + # 生命恢复 + if enable_health_regen and current_health < max_health: + current_health = min(max_health, current_health + health_regen * delta) + + # 护盾恢复 + if enable_shield_regen and max_shield > 0 and current_shield < max_shield: + current_shield = min(max_shield, current_shield + shield_regen * delta) + + # 巡逻逻辑已简化为静态生成,无需移动处理 + + # AI更新 + if ai_update_timer >= ai_update_interval: + update_ai() + ai_update_timer = 0.0 + + # 检查技能-多发射击触发 + if enable_multi_projectile_skill: + check_multi_projectile_skill() + + # 检查技能-狂暴模式触发 + if enable_berserker_skill: + check_berserker_skill() + + # 检查技能-召唤小弟触发 + if enable_summon_pet_skill: + check_summon_pet_skill() + + # 击退效果处理 + if is_being_knocked_back: + var current_time = Time.get_ticks_msec() / 1000.0 + if current_time >= knockback_end_time: + # 击退结束 + is_being_knocked_back = false + knockback_velocity = Vector2.ZERO + else: + # 应用击退速度,逐渐衰减 + var remaining_time = knockback_end_time - current_time + var decay_factor = remaining_time / knockback_duration + position += knockback_velocity * decay_factor * delta + + # 移动处理(击退时不应用普通移动) + if not is_being_knocked_back and velocity.length() > 0: + position += velocity * delta + velocity = velocity.move_toward(Vector2.ZERO, 500 * delta) # 摩擦力 + + # 边界检测和反弹处理 + check_boundary_collision() + + # UI更新 + if update_ui_timer >= ui_update_interval: + update_ui() + update_ui_timer = 0.0 +#====================基础方法========================= + + + +#=========================宠物系统通用函数================================== +#设置碰撞体积(对远程攻击还是有用的) +func setup_collision_layers(): + collision_layer = 1 + collision_mask = 1 + +#开启宠物ai系统 +func _start_ai(): + """启动AI,立即寻找目标""" + if not is_alive: + return + + # 立即寻找最近的敌人 + current_target = find_nearest_enemy() + + # 如果找到目标,开始移动或攻击 + if current_target != null: + current_state = PetState.MOVING + # 找到攻击目标 + +#AI逻辑更新 +func update_ai(): + """AI逻辑更新""" + if current_state == PetState.DEAD or is_being_knocked_back: + return + + # 如果正在巡逻,执行巡逻AI逻辑 + if is_patrolling: + update_patrol_ai() + return + + # 如果启用了战斗且不在巡逻状态,执行战斗AI + if combat_enabled and not is_patrolling: + # 寻找目标(即使在攻击状态也要检查目标有效性) + if current_target == null or not current_target.is_alive: + current_target = find_nearest_enemy() + + if current_target == null: + # 没有目标时,继续搜索而不是待机 + current_state = PetState.MOVING + pet_image.animation = "walk" + # 随机移动寻找敌人 + var random_direction = Vector2(randf_range(-1, 1), randf_range(-1, 1)).normalized() + velocity = random_direction * move_speed * 0.5 + return + + # 如果正在攻击,等待攻击完成 + if current_state == PetState.ATTACKING: + return + + var distance_to_target = global_position.distance_to(current_target.global_position) + + # 优先近战攻击(包含武器攻击范围加成) + var total_attack_range = attack_range + if distance_to_target <= total_attack_range: + if can_attack(): + perform_melee_attack() + else: + current_state = PetState.IDLE + pet_image.animation = "idle" + else: + # 移动到目标 + move_towards_target() + +#寻找最近的敌人 +func find_nearest_enemy() -> NewPetBase: + """寻找最近的敌人""" + var enemies = get_tree().get_nodes_in_group("pets") + var nearest_enemy: NewPetBase = null + var min_distance = INF + + for enemy in enemies: + if enemy == self or not enemy.is_alive or enemy.pet_team == pet_team: + continue + + var distance = global_position.distance_to(enemy.global_position) + if distance < min_distance: + min_distance = distance + nearest_enemy = enemy + + return nearest_enemy + +#移动到目标 +func move_towards_target(): + """移动到目标""" + if current_target == null: + return + + current_state = PetState.MOVING + pet_image.animation = "walk" + + var direction = (current_target.global_position - global_position).normalized() + velocity = direction * move_speed + + # 翻转精灵 + if direction.x < 0: + pet_image.flip_h = false + left_tool_image.flip_h = true + right_tool_image.flip_h = true + left_tool_image.position = Vector2(-12.5,3.5) + right_tool_image.position = Vector2(-7.5,-6.25) + #left_tool_image.rotation = 21.8 + #right_tool_image.rotation = -14.5 + + + else: + pet_image.flip_h = true + left_tool_image.flip_h = false + right_tool_image.flip_h = false + left_tool_image.position = Vector2(12.5,3.5) + right_tool_image.position = Vector2(7.5,-6.25) + #left_tool_image.rotation = -21.8 + #right_tool_image.rotation = 14.5 + +#检查边界碰撞并处理反弹和伤害 +func check_boundary_collision(): + """检查边界碰撞并处理反弹和伤害""" + if not is_alive: + return + + # 巡逻宠物不受边界限制 + if is_patrolling: + return + + var collision_occurred = false + var bounce_direction = Vector2.ZERO + + # 检查X轴边界 + if position.x < boundary_min.x: + position.x = boundary_min.x + bounce_direction.x = 1.0 # 向右反弹 + collision_occurred = true + elif position.x > boundary_max.x: + position.x = boundary_max.x + bounce_direction.x = -1.0 # 向左反弹 + collision_occurred = true + + # 检查Y轴边界 + if position.y < boundary_min.y: + position.y = boundary_min.y + bounce_direction.y = 1.0 # 向下反弹 + collision_occurred = true + elif position.y > boundary_max.y: + position.y = boundary_max.y + bounce_direction.y = -1.0 # 向上反弹 + collision_occurred = true + + # 如果发生碰撞,应用反弹和伤害 + if collision_occurred: + # 应用反弹效果 + if bounce_direction.length() > 0: + bounce_direction = bounce_direction.normalized() + velocity = bounce_direction * boundary_bounce_force + # 如果正在被击退,也要修改击退方向 + if is_being_knocked_back: + knockback_velocity = bounce_direction * knockback_force + + # 造成边界伤害 + take_damage(boundary_damage, null) + # 边界碰撞处理 + +#检查是否可以攻击 +func can_attack() -> bool: + """检查是否可以攻击""" + var current_time = Time.get_ticks_msec() / 1000.0 + var time_since_last_attack = current_time - last_attack_time + # 计算总攻击速度(包含武器加成) + var total_attack_speed = attack_speed + var attack_cooldown = 1.0 / max(0.1, total_attack_speed) # 防止除零错误 + return time_since_last_attack >= attack_cooldown + +#执行近战攻击 +func perform_melee_attack(): + """执行近战攻击""" + current_state = PetState.ATTACKING + pet_image.animation = "idle" # 可以添加攻击动画 + last_attack_time = Time.get_ticks_msec() / 1000.0 + + # 显示武器 + left_tool_image.visible = true + right_tool_image.visible = true + + # 直接攻击当前目标(如果在攻击范围内) + if current_target != null and current_target.is_alive: + var distance_to_target = global_position.distance_to(current_target.global_position) + var total_attack_range = attack_range + if distance_to_target <= total_attack_range and current_target.pet_team != pet_team: + deal_damage_to(current_target) + # 执行近战攻击 + + # 隐藏武器并重置状态(延迟) + get_tree().create_timer(0.2).timeout.connect(func(): + #left_tool_image.visible = false + #right_tool_image.visible = false + current_state = PetState.IDLE + ) + + +#应用宠物外观图片 +func apply_pet_image(pet: NewPetBase, image_path: String): + """应用宠物外观图片""" + if image_path == "" or not ResourceLoader.exists(image_path): + return + + # 加载新的宠物场景 + var new_pet_scene = load(image_path) + if not new_pet_scene: + return + + # 实例化新场景以获取图片组件 + var temp_instance = new_pet_scene.instantiate() + # 根节点本身就是PetImage + var new_pet_image = temp_instance + var new_left_tool = temp_instance.get_node_or_null("LeftToolImage") + var new_right_tool = temp_instance.get_node_or_null("RightToolImage") + + if new_pet_image and new_pet_image is AnimatedSprite2D: + # 复制动画帧到现有宠物 + if new_pet_image.sprite_frames: + pet.pet_image.sprite_frames = new_pet_image.sprite_frames + #pet.pet_image.animation = new_pet_image.animation + pet.pet_image.scale = new_pet_image.scale + # 确保动画播放 + pet.pet_image.play() + + # 复制工具图片 + if new_left_tool and pet.left_tool_image: + pet.left_tool_image.texture = new_left_tool.texture + pet.left_tool_image.position = new_left_tool.position + pet.left_tool_image.flip_h = new_left_tool.flip_h + pet.left_tool_image.z_index = new_left_tool.z_index + pet.left_tool_image.visible = true + + if new_right_tool and pet.right_tool_image: + pet.right_tool_image.texture = new_right_tool.texture + pet.right_tool_image.position = new_right_tool.position + pet.right_tool_image.flip_h = new_right_tool.flip_h + pet.right_tool_image.show_behind_parent = new_right_tool.show_behind_parent + pet.right_tool_image.visible = true + + # 外观应用成功 + else: + pass # 静默处理错误 + + # 清理临时实例 + temp_instance.queue_free() + + # 重新更新武器图标(因为外观应用可能覆盖了武器图标) + if pet.weapon_system != null: + pet.update_weapon_icons() + +#对目标造成伤害 +func deal_damage_to(target: NewPetBase): + """对目标造成伤害""" + var damage = calculate_damage(target) + target.take_damage(damage, self) + + # 应用击退效果 + if enable_knockback and target.enable_knockback and target.is_alive: + apply_knockback_to(target) + + # 发射信号 + pet_attacked.emit(self, target, damage) + +#对目标应用击退效果 +func apply_knockback_to(target: NewPetBase): + """对目标应用击退效果""" + if not target.is_alive or target.is_being_knocked_back: + return + + # 计算击退方向(从攻击者指向目标) + var knockback_direction = (target.global_position - global_position).normalized() + + # 如果距离太近,使用随机方向避免除零错误 + if knockback_direction.length() < 0.1: + knockback_direction = Vector2(randf_range(-1, 1), randf_range(-1, 1)).normalized() + + # 设置击退参数(包含武器击退力加成) + var total_knockback_force = target.knockback_force + target.knockback_velocity = knockback_direction * total_knockback_force + target.is_being_knocked_back = true + var current_time = Time.get_ticks_msec() / 1000.0 + target.knockback_end_time = current_time + target.knockback_duration + + # 击退时暂停AI行为 + if target.current_state != PetState.DEAD: + target.current_state = PetState.IDLE + + # 击退效果生效 + +#计算伤害 +func calculate_damage(target: NewPetBase) -> float: + """计算伤害""" + var damage = base_attack_damage + + # 添加元素伤害加成(固定额外伤害) + damage += element_damage_bonus + + # 狂暴模式伤害加成 + if is_berserker: + damage *= berserker_bonus + + # 暴击计算(包含武器暴击率加成) + var total_crit_rate = crit_rate + if randf() < total_crit_rate: + damage *= crit_damage + + # 元素克制倍数(相克关系的倍数加成) + var element_multiplier = get_element_multiplier(element_type, target.element_type) + damage *= element_multiplier + + # 护甲减伤计算(新系统:护甲值直接减免伤害,但最少保留1点伤害) + # 包含武器护甲穿透加成 + var total_armor_penetration = armor_penetration + var effective_armor = max(0, target.current_armor - total_armor_penetration) + # 护甲值直接减免伤害 + damage = max(1.0, damage - effective_armor) # 最少保留1点伤害 + + return damage + +#获取元素克制倍数 +func get_element_multiplier(attacker_element: ElementType, defender_element: ElementType) -> float: + """获取元素克制倍数""" + # 简化的元素克制系统 + if attacker_element == ElementType.FIRE and defender_element == ElementType.WOOD: + return 1.5 + elif attacker_element == ElementType.WATER and defender_element == ElementType.FIRE: + return 1.5 + elif attacker_element == ElementType.WOOD and defender_element == ElementType.EARTH: + return 1.5 + else: + return 1.0 + +#受到伤害 +func take_damage(damage: float, attacker: NewPetBase): + """受到伤害""" + if not is_alive: + return + + # 检查攻击者是否有效(防止已释放对象错误) + var attacker_name = "未知攻击者" + if attacker != null and is_instance_valid(attacker): + attacker_name = attacker.pet_name + + # 闪避检查 + if randf() < dodge_rate: + # 闪避成功 + return # 闪避成功 + + # 护盾优先吸收伤害 + if current_shield > 0: + var shield_damage = min(current_shield, damage) + current_shield -= shield_damage + damage -= shield_damage + # 护盾吸收伤害 + + # 护盾消耗完后,剩余伤害扣除生命值 + if damage > 0: + current_health -= damage + # 受到伤害 + + # 受伤视觉效果(短暂变红) + if not is_berserker: # 狂暴状态下不覆盖红色效果 + pet_image.modulate = Color(1.3, 0.7, 0.7, 1.0) + get_tree().create_timer(0.15).timeout.connect(func(): + if not is_berserker and is_alive: + pet_image.modulate = Color(1.0, 1.0, 1.0, 1.0) + ) + + # 检查死亡 + if current_health <= 0: + die() + +#治疗 +func heal(amount: float): + """治疗""" + current_health = min(max_health, current_health + amount) + +#死亡处理 +func die(): + """死亡处理""" + # 防止重复死亡处理 + if current_state == PetState.DEAD: + return + + # 检查死亡重生技能 + if enable_death_respawn_skill and current_respawn_count < max_respawn_count: + trigger_death_respawn_skill() + return # 重生成功,不执行死亡逻辑 + + # 确认死亡状态 + is_alive = false + current_state = PetState.DEAD + current_health = 0 # 确保生命值为0 + velocity = Vector2.ZERO # 停止移动 + current_target = null # 清除目标 + + # 死亡视觉效果 + modulate = Color(0.5, 0.5, 0.5, 0.7) # 变灰 + + # 触发自爆技能 + if enable_self_destruct_skill: + trigger_self_destruct_skill() + + # 发射死亡信号 + pet_died.emit(self) + + # 从宠物组中移除 + remove_from_group("pets") + +#更新UI显示 +func update_ui(): + """更新UI显示""" + if pet_name_rich_text: + pet_name_rich_text.text = pet_name + + update_health_bar() + update_shield_bar() + update_armor_bar() + +#更新生命值条 +func update_health_bar(): + """更新生命值条""" + if health_bar and health_label: + health_bar.value = (current_health / max_health) * 100 + health_label.text = "生命值:%d/%d" % [current_health, max_health] + +#更新护盾条 +func update_shield_bar(): + """更新护盾条""" + if shield_bar and shield_label: + if max_shield > 0: + shield_bar.visible = true + shield_label.visible = true + shield_bar.value = (current_shield / max_shield) * 100 + shield_label.text = "护盾值:%d/%d" % [current_shield, max_shield] + else: + shield_bar.visible = false + shield_label.visible = false + +#更新护甲条 +func update_armor_bar(): + """更新护甲条""" + if armor_bar and armor_label: + if max_armor > 0: + armor_bar.visible = true + armor_label.visible = true + armor_bar.value = (current_armor / max_armor) * 100 + armor_label.text = "护甲值:%d/%d" % [current_armor, max_armor] + else: + armor_bar.visible = false + armor_label.visible = false +#=========================宠物系统通用函数================================== + + + + +#=======================宠物技能系统=================================== + + +#==================特殊技能-多发射击===================== +func check_multi_projectile_skill(): + """检查多发射击技能触发条件""" + if multi_projectile_count > 0 or not is_alive: + return + + # 宠物生成后按配置的延迟时间触发多发射击(只触发一次) + var current_time = Time.get_ticks_msec() / 1000.0 + if current_time - spawn_time >= multi_projectile_delay: # 使用配置的延迟时间 + trigger_multi_projectile_skill() + multi_projectile_count = 1 # 标记已触发,防止重复触发 + +func trigger_multi_projectile_skill(): + """触发多发射击技能:发射3枚不同类型的平行子弹""" + # 触发多发射击技能 + + # 获取宠物朝向(基于精灵翻转状态) + var forward_direction = Vector2.RIGHT if pet_image.flip_h else Vector2.LEFT + + # 定义三种不同的子弹类型 + var bullet_types = ["小蓝弹", "小红弹", "长紫弹"] + + # 发射3枚不同类型的平行子弹 + for i in range(3): + var bullet = bullet_scene.instantiate() + self.get_parent().add_child(bullet) + + # 计算子弹位置偏移(平行排列) + var offset_y = (i - 1) * 30 # 上中下三枚子弹,间距30像素 + var bullet_position = global_position + Vector2(0, offset_y) + bullet.global_position = bullet_position + + # 设置子弹属性,使用不同类型的子弹 + bullet.setup(forward_direction, projectile_speed, base_attack_damage * 0.6, self, bullet_types[i]) + + # 稍微延迟发射,创造连发效果 + await get_tree().create_timer(0.05).timeout +#==================特殊技能-多发射击===================== + + +#==================特殊技能-狂暴模式===================== +func check_berserker_skill(): + """检查狂暴技能触发条件""" + if berserker_triggered or not is_alive: + return + + # 检查生命值是否低于阈值 + var health_percentage = current_health / max_health + if health_percentage <= berserker_threshold: + trigger_berserker_skill() + berserker_triggered = true + + # 检查狂暴是否结束 + if is_berserker: + var current_time = Time.get_ticks_msec() / 1000.0 + if current_time >= berserker_end_time: + end_berserker_skill() + +func trigger_berserker_skill(): + """触发狂暴技能:提升攻击力和攻击速度""" + # 触发狂暴技能 + + is_berserker = true + var current_time = Time.get_ticks_msec() / 1000.0 + berserker_end_time = current_time + berserker_duration + + # 视觉效果:宠物变红 + pet_image.modulate = Color(1.5, 0.8, 0.8, 1.0) + + # 提升攻击速度 + attack_speed *= 1.3 + + # 发射技能信号 + pet_skill_used.emit(self, "狂暴模式") + +func end_berserker_skill(): + """结束狂暴技能""" + # 狂暴模式结束 + + is_berserker = false + + # 恢复正常颜色 + pet_image.modulate = Color(1.0, 1.0, 1.0, 1.0) + + # 恢复攻击速度 + attack_speed /= 1.3 +#==================特殊技能-狂暴模式===================== + + +#==================特殊技能-自爆===================== +func trigger_self_destruct_skill(): + """触发自爆技能:死亡时向周围360度发射12枚闪电子弹""" + # 触发自爆技能 + + # 计算12枚子弹的角度间隔(360度 / 12 = 30度) + var bullet_count = 12 + var angle_step = 360.0 / bullet_count + + # 定义四种闪电子弹类型,循环使用 + var lightning_types = ["黄色闪电", "绿色闪电", "红色闪电", "紫色闪电"] + + # 发射12枚闪电子弹 + for i in range(bullet_count): + var bullet = bullet_scene.instantiate() + get_tree().current_scene.add_child(bullet) + + # 计算子弹发射角度(度转弧度) + var angle_degrees = i * angle_step + var angle_radians = deg_to_rad(angle_degrees) + + # 计算子弹发射方向 + var direction = Vector2(cos(angle_radians), sin(angle_radians)) + + # 设置子弹位置(从宠物中心发射) + bullet.global_position = global_position + + # 设置子弹属性(自爆伤害),使用循环的闪电类型 + var bullet_type = lightning_types[i % lightning_types.size()] + bullet.setup(direction, projectile_speed, self_destruct_damage, self, bullet_type) + + # 稍微延迟发射,创造爆炸效果 + await get_tree().create_timer(0.02).timeout + + # 发射技能信号 + pet_skill_used.emit(self, "自爆") +#==================特殊技能-自爆===================== + + +#==================特殊技能-召唤小弟===================== +func check_summon_pet_skill(): + """检查召唤小弟技能触发条件""" + if summon_triggered or not is_alive: + return + + # 检查生命值是否低于阈值 + var health_percentage = current_health / max_health + if health_percentage <= summon_health_threshold: + trigger_summon_pet_skill() + summon_triggered = true + +func trigger_summon_pet_skill(): + """触发召唤小弟技能:召唤迷你版自己""" + # 触发召唤技能 + + # 获取NewPetBase场景 + var pet_scene = preload("res://Scene/NewPet/NewPetBase.tscn") + + # 召唤指定数量的小弟 + for i in range(summon_count): + var minion = pet_scene.instantiate() + self.get_parent().add_child(minion) + + # 设置小弟位置(在召唤者周围随机位置) + var offset_angle = randf() * 2 * PI + var offset_distance = 80 + i * 30 # 避免重叠 + var offset = Vector2(cos(offset_angle), sin(offset_angle)) * offset_distance + minion.global_position = global_position + offset + + # 设置小弟属性(原版的10%) + minion.pet_name = pet_name + "的小弟" + str(i + 1) + minion.pet_team = pet_team # 同队伍 + minion.pet_id = pet_id + "_minion_" + str(i + 1) + minion.pet_type = pet_type + "(迷你)" + minion.pet_level = max(1, int(pet_level * summon_scale)) + + # 复制宠物图片路径 + minion.pet_image_path = pet_image_path + + # 复制武器配置 + if left_weapon != "": + minion.equip_weapon(left_weapon, "left") + if right_weapon != "": + minion.equip_weapon(right_weapon, "right") + + # 生命与防御属性缩放 + minion.max_health = max_health * summon_scale + minion.current_health = minion.max_health + minion.health_regen = health_regen * summon_scale + minion.max_shield = max_shield * summon_scale + minion.current_shield = minion.max_shield + minion.shield_regen = shield_regen * summon_scale + minion.max_armor = max_armor * summon_scale + minion.current_armor = minion.max_armor + + # 攻击属性缩放 + minion.base_attack_damage = base_attack_damage * summon_scale + minion.attack_range = attack_range * summon_scale + minion.attack_speed = attack_speed * summon_scale + minion.crit_rate = crit_rate * summon_scale + minion.crit_damage = crit_damage + minion.armor_penetration = armor_penetration * summon_scale + + # 移动属性缩放 + minion.move_speed = move_speed * summon_scale + minion.dodge_rate = dodge_rate * summon_scale + + # 元素属性缩放 + minion.element_type = element_type + minion.element_damage_bonus = element_damage_bonus * summon_scale + + # 技能属性缩放 + minion.self_destruct_damage = self_destruct_damage * summon_scale + minion.berserker_bonus = berserker_bonus + minion.berserker_duration = berserker_duration * summon_scale + + # 禁用小弟的所有技能 + minion.enable_multi_projectile_skill = false # 禁用多发射击技能 + minion.enable_berserker_skill = false # 禁用狂暴技能 + minion.enable_self_destruct_skill = false # 禁用自爆技能 + minion.enable_summon_pet_skill = false # 禁用召唤技能 + minion.enable_death_respawn_skill = false # 禁用死亡重生技能 + + # 应用宠物图片(如果有的话) + if pet_image_path != "": + apply_pet_image(minion, pet_image_path) + + # 设置小弟的缩放(视觉上更小,原宠物的二分之一) + minion.scale = Vector2(0.5, 0.5) + + # 将小弟加入宠物组 + minion.add_to_group("pets") + + # 小弟召唤成功 + + # 发射技能信号 + pet_skill_used.emit(self, "召唤小弟") +#==================特殊技能-召唤小弟===================== + + +#==================特殊技能-死亡重生===================== +func trigger_death_respawn_skill(): + """触发死亡重生技能:重生并恢复30%血量""" + # 增加重生次数 + current_respawn_count += 1 + + # 恢复生命值到指定百分比 + current_health = max_health * respawn_health_percentage + + # 确保宠物处于存活状态 + is_alive = true + current_state = PetState.IDLE + velocity = Vector2.ZERO # 重置速度 + + # 恢复正常外观 + modulate = Color(1.0, 1.0, 1.0, 1.0) + + # 重生视觉效果:短暂发光 + pet_image.modulate = Color(1.5, 1.5, 1.0, 1.0) # 金色光芒 + get_tree().create_timer(1.0).timeout.connect(func(): + if is_alive and is_instance_valid(self): + pet_image.modulate = Color(1.0, 1.0, 1.0, 1.0) # 恢复正常颜色 + ) + + # 重生时清除所有负面状态 + is_being_knocked_back = false + knockback_velocity = Vector2.ZERO + + # 重新加入宠物组(如果被移除了) + if not is_in_group("pets"): + add_to_group("pets") + + # 更新UI显示 + update_ui() + + # 发射技能信号 + pet_skill_used.emit(self, "死亡重生") +#==================特殊技能-死亡重生===================== + + +#=======================宠物技能系统=================================== + + + + + +#==========================巡逻系统函数================================= +# 巡逻AI逻辑更新 +func update_patrol_ai(): + """巡逻AI逻辑:在巡逻点周围随机走动""" + current_target = null # 清除攻击目标 + + # 如果还没有设置巡逻中心点,使用当前位置作为中心点 + if patrol_center_position == Vector2.ZERO: + patrol_center_position = global_position + patrol_target_position = global_position + + # 更新巡逻移动计时器 + patrol_move_timer += get_physics_process_delta_time() + + # 检查是否到达目标位置或需要更换目标 + var distance_to_target = global_position.distance_to(patrol_target_position) + if distance_to_target < 10.0 or patrol_move_timer >= patrol_move_interval: + # 生成新的随机目标位置(在巡逻半径内) + var random_angle = randf() * 5 * PI + var random_distance = randf() * patrol_radius + patrol_target_position = patrol_center_position + Vector2( + cos(random_angle) * random_distance, + sin(random_angle) * random_distance + ) + patrol_move_timer = 0.0 + + # 移动到目标位置 + var direction = (patrol_target_position - global_position).normalized() + if direction.length() > 0.1: # 避免抖动 + current_state = PetState.MOVING + pet_image.animation = "walk" + velocity = direction * patrol_speed + + # 翻转精灵 + if direction.x < 0: + pet_image.flip_h = false + else: + pet_image.flip_h = true + else: + # 到达目标位置,待机 + current_state = PetState.IDLE + pet_image.animation = "idle" + velocity = Vector2.ZERO + +# 设置巡逻中心点 +func set_patrol_center(center_pos: Vector2): + """设置巡逻中心点位置""" + patrol_center_position = center_pos + patrol_target_position = center_pos + +# 设置战斗启用状态 +func set_combat_enabled(enabled: bool): + combat_enabled = enabled + if not enabled: + # 禁用战斗时,清除当前目标 + current_target = null + current_state = PetState.IDLE +#==========================巡逻系统函数================================= + + + +#==========================武器系统函数================================= + +func init_weapon_system(): + """初始化武器系统""" + weapon_system = WeaponBase.new() + +func equip_weapon(weapon_name: String, slot: String) -> bool: + """装备武器到指定槽位""" + if weapon_system == null: + init_weapon_system() + + # 检查武器是否存在 + if not weapon_system.weapon_data.has(weapon_name): + return false + + # 检查槽位是否有效 + if slot != "left" and slot != "right": + return false + + # 卸下当前武器(如果有) + if slot == "left" and left_weapon != "": + unequip_weapon("left") + elif slot == "right" and right_weapon != "": + unequip_weapon("right") + + # 装备新武器 + if slot == "left": + left_weapon = weapon_name + elif slot == "right": + right_weapon = weapon_name + + # 应用武器效果 + weapon_system.apply_weapon_effect(self, weapon_name) + + # 更新武器图标 + update_weapon_icons() + + # 武器装备完成 + return true + +func unequip_weapon(slot: String) -> bool: + """卸下指定槽位的武器""" + if weapon_system == null: + return false + + var weapon_name = "" + if slot == "left": + weapon_name = left_weapon + left_weapon = "" + elif slot == "right": + weapon_name = right_weapon + right_weapon = "" + else: + return false + + if weapon_name == "": + return false + + # 移除武器效果 + weapon_system.remove_weapon_effect(self, weapon_name) + + # 更新武器图标 + update_weapon_icons() + + # 武器卸载完成 + return true + +func update_weapon_icons(): + """更新武器图标显示""" + if weapon_system == null: + return + + # 更新左手武器图标 + if left_tool_image != null: + if left_weapon != "": + var icon_path = weapon_system.get_weapon_icon(left_weapon) + if icon_path != "": + left_tool_image.texture = load(icon_path) + left_tool_image.visible = true + else: + left_tool_image.visible = false + else: + left_tool_image.visible = false + + # 更新右手武器图标 + if right_tool_image != null: + if right_weapon != "": + var icon_path = weapon_system.get_weapon_icon(right_weapon) + if icon_path != "": + right_tool_image.texture = load(icon_path) + right_tool_image.visible = true + else: + right_tool_image.visible = false + else: + right_tool_image.visible = false + + # 武器图标更新完成 + +func get_equipped_weapons() -> Array: + """获取当前装备的武器列表""" + var weapons = [] + if left_weapon != "": + weapons.append({"slot": "left", "weapon": left_weapon}) + if right_weapon != "": + weapons.append({"slot": "right", "weapon": right_weapon}) + return weapons + +func has_weapon_type(weapon_type: String) -> bool: + """检查是否装备了指定类型的武器""" + if weapon_system == null: + return false + + # 根据武器名称判断类型 + var weapons_to_check = [left_weapon, right_weapon] + for weapon_name in weapons_to_check: + if weapon_name == "": + continue + + # 根据武器名称判断类型 + if weapon_type == "sword" and (weapon_name.contains("剑")): + return true + elif weapon_type == "axe" and (weapon_name.contains("斧")): + return true + elif weapon_type == "pickaxe" and (weapon_name.contains("镐")): + return true + + return false + +func test_weapon_system(): + """测试武器系统功能""" + # 装备武器测试 + equip_weapon("钻石剑", "left") + equip_weapon("铁镐", "right") + +#==========================武器系统函数================================= diff --git a/Scene/NewPet/NewPetBase.gd.uid b/Scene/NewPet/NewPetBase.gd.uid new file mode 100644 index 0000000..fd5bdb6 --- /dev/null +++ b/Scene/NewPet/NewPetBase.gd.uid @@ -0,0 +1 @@ +uid://cn6a0803t1bmu diff --git a/Scene/NewPet/NewPetBase.tscn b/Scene/NewPet/NewPetBase.tscn new file mode 100644 index 0000000..0272779 --- /dev/null +++ b/Scene/NewPet/NewPetBase.tscn @@ -0,0 +1,146 @@ +[gd_scene load_steps=8 format=3 uid="uid://cfwj8rnm2j8s3"] + +[ext_resource type="Script" uid="uid://cn6a0803t1bmu" path="res://Scene/NewPet/NewPetBase.gd" id="1_bfbjx"] +[ext_resource type="Texture2D" uid="uid://lx0l12qrituk" path="res://assets/宠物图片/一堆小怪.png" id="2_gnd2w"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="3_bfbjx"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_h4hw6"] +atlas = ExtResource("2_gnd2w") +region = Rect2(0, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_51c25"] +atlas = ExtResource("2_gnd2w") +region = Rect2(24, 0, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_wmdx5"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_h4hw6") +}], +"loop": true, +"name": &"idle", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_h4hw6") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_51c25") +}], +"loop": true, +"name": &"walk", +"speed": 8.0 +}] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_h4hw6"] +size = Vector2(79, 92) + +[node name="PetBase" type="Area2D"] +script = ExtResource("1_bfbjx") + +[node name="PetImage" type="AnimatedSprite2D" parent="."] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_wmdx5") +animation = &"walk" +autoplay = "walk" + +[node name="LeftToolImage" type="Sprite2D" parent="PetImage"] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("3_bfbjx") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="PetImage"] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("3_bfbjx") +flip_h = true + +[node name="VolumeCollision" type="CollisionShape2D" parent="."] +position = Vector2(0.5, 2) +shape = SubResource("RectangleShape2D_h4hw6") + +[node name="PetInformVBox" type="VBoxContainer" parent="."] +offset_left = -72.0 +offset_top = -261.0 +offset_right = 430.0 +offset_bottom = 453.0 +scale = Vector2(0.3, 0.3) +alignment = 2 + +[node name="PetNameRichText" type="RichTextLabel" parent="PetInformVBox"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_font_sizes/normal_font_size = 40 +bbcode_enabled = true +text = "萌芽小绿人" +horizontal_alignment = 1 +vertical_alignment = 2 + +[node name="ArmorBar" type="ProgressBar" parent="PetInformVBox"] +modulate = Color(0.758192, 0.758192, 0.758192, 1) +custom_minimum_size = Vector2(0, 60) +layout_mode = 2 +show_percentage = false + +[node name="ArmorLabel" type="Label" parent="PetInformVBox/ArmorBar"] +layout_mode = 0 +offset_right = 502.0 +offset_bottom = 60.0 +theme_override_font_sizes/font_size = 30 +text = "盔甲值:100/100" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ShieldBar" type="ProgressBar" parent="PetInformVBox"] +modulate = Color(0, 1, 1, 1) +custom_minimum_size = Vector2(0, 60) +layout_mode = 2 +show_percentage = false + +[node name="ShieldLabel" type="Label" parent="PetInformVBox/ShieldBar"] +layout_mode = 0 +offset_right = 502.0 +offset_bottom = 60.0 +theme_override_font_sizes/font_size = 30 +text = "护盾值:100/100" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="HealthBar" type="ProgressBar" parent="PetInformVBox"] +modulate = Color(0, 1, 0, 1) +custom_minimum_size = Vector2(0, 60) +layout_mode = 2 +show_percentage = false + +[node name="HealthLabel" type="Label" parent="PetInformVBox/HealthBar"] +layout_mode = 0 +offset_right = 502.0 +offset_bottom = 60.0 +theme_override_font_sizes/font_size = 30 +text = "生命值:100/100" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="StatusEffects" type="HBoxContainer" parent="."] +offset_left = -30.0 +offset_top = -65.0 +offset_right = 30.0 +offset_bottom = -55.0 + +[node name="AttackTimer" type="Timer" parent="."] + +[node name="MoveTimer" type="Timer" parent="."] +wait_time = 0.1 +autostart = true + +[node name="StatusTimer" type="Timer" parent="."] +autostart = true + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] + +[node name="HitEffect" type="Node2D" parent="."] + +[node name="DeathEffect" type="Node2D" parent="."] diff --git a/Scene/NewPet/PetBattlePanel.gd b/Scene/NewPet/PetBattlePanel.gd new file mode 100644 index 0000000..f974b1b --- /dev/null +++ b/Scene/NewPet/PetBattlePanel.gd @@ -0,0 +1,1225 @@ +extends Panel +class_name PetBattlePanel + +# 宠物对战系统管理器 +# 支持PVP、PVE,最多20个宠物同时对战 +# 高性能设计,适合移动端 + +signal battle_started +signal battle_ended(winner_team: String, battle_data: Dictionary) +signal pet_spawned(pet: NewPetBase) + +# UI节点引用 +@onready var map_background: TextureRect = $MapBackGround +@onready var title_label: Label = $Title +@onready var team_a_node: Node2D = $TeamA +@onready var team_b_node: Node2D = $TeamB +@onready var battle_end_panel: Panel = $BattleEndPanel +@onready var battle_details_panel: Panel = $PetBattleDetailsPanel +@onready var battle_details_text: RichTextLabel = $PetBattleDetailsPanel/BattleDetails +@onready var return_farm_button: Button = $BattleEndPanel/ReturnFarmButton +@onready var time: Label = $Time #剩余对战时间 +@onready var confirm_dialog: ConfirmationDialog = $ConfirmDialog #确认弹窗,每当操作需要确认时出现 + +# 辅助功能按钮引用 +@onready var team_a_heal_button: Button = $PlayerSkillPanel/TeamASkills/TeamAHeal +@onready var team_a_rage_button: Button = $PlayerSkillPanel/TeamASkills/TeamARage +@onready var team_a_shield_button: Button = $PlayerSkillPanel/TeamASkills/TeamAShield + +@onready var tcp_network_manager_panel: Panel = $'../TCPNetworkManagerPanel'#客户端通信函数 + + +# 队伍名称 +var team_a_name: String = "" +var team_b_name: String = "" + +# 战斗状态 +enum BattleState { + PREPARING, # 准备阶段 + BATTLING, # 战斗中 + ENDED # 战斗结束 +} + +var current_battle_state: BattleState = BattleState.PREPARING +var battle_time: float = 0.0 +var max_battle_time: float = 300.0 # 5分钟最大战斗时间 + +# 队伍管理 +var team_a_pets: Array[NewPetBase] = [] +var team_b_pets: Array[NewPetBase] = [] +var all_pets: Array[NewPetBase] = [] + +# 战斗统计 +var battle_log: Array[String] = [] +var damage_dealt: Dictionary = {} # 记录每个宠物造成的伤害 +var damage_taken: Dictionary = {} # 记录每个宠物受到的伤害 +var kills: Dictionary = {} # 记录每个宠物的击杀数 + +# 性能优化 +var update_timer: float = 0.0 +var update_interval: float = 0.2 # 战斗状态更新间隔(降低频率) +var cleanup_timer: float = 0.0 +var cleanup_interval: float = 2.0 # 清理死亡宠物和子弹的间隔(降低频率) + +# 宠物配置系统 +var pet_config: PetConfig + +# 辅助功能冷却系统 +var assist_cooldown_time: float = 5.0 # 冷却时间5秒 +var heal_cooldown_timer: float = 0.0 +var rage_cooldown_timer: float = 0.0 +var shield_cooldown_timer: float = 0.0 +var current_assist_operation: String = "" # 当前待执行的辅助操作 + +#========================基础函数====================== +func _ready(): + # 初始化UI + battle_end_panel.visible = false + return_farm_button.pressed.connect(_on_return_farm_pressed) + # 初始化宠物配置系统 + pet_config = PetConfig.new() + # 等待一帧确保PetConfig的_ready函数执行完毕 + await get_tree().process_frame + # 初始化战斗日志 + battle_details_text.text = "[color=green]战斗准备中...[/color]\n" + + # 连接确认弹窗信号 + confirm_dialog.confirmed.connect(_on_assist_confirmed) + confirm_dialog.canceled.connect(_on_assist_canceled) + + # 美化确认弹窗 + setup_confirm_dialog() + + # 延迟一帧后设置演示数据,确保所有节点都已准备好 + await get_tree().process_frame + #setup_farm_battle() + +func _process(delta): + # 更新时间显示(无论什么状态都显示) + update_time_display() + + # 更新辅助功能冷却计时器 + update_assist_cooldowns(delta) + + if current_battle_state != BattleState.BATTLING: + return + + # 更新战斗时间 + battle_time += delta + + # 检查时间是否到达 + var remaining_time = max_battle_time - battle_time + if remaining_time <= 0: + # 时间到,立即清理所有宠物并结束为平局 + clear_all_pets_immediately() + end_battle("平局") + return + + # 更新计时器 + update_timer += delta + cleanup_timer += delta + + # 定期更新战斗状态 + if update_timer >= update_interval: + update_battle_state() + update_timer = 0.0 + + # 定期清理 + if cleanup_timer >= cleanup_interval: + cleanup_dead_objects() + cleanup_timer = 0.0 +#========================基础函数====================== + + +#=====================UI显示=========================== +#更新时间显示 +func update_time_display(): + """更新时间显示""" + var remaining_time: float + + if current_battle_state == BattleState.BATTLING: + remaining_time = max_battle_time - battle_time + else: + remaining_time = max_battle_time + + # 确保时间不为负数 + remaining_time = max(0, remaining_time) + + # 更新时间显示(格式:分:秒) + var minutes = int(remaining_time) / 60 + var seconds = int(remaining_time) % 60 + time.text = "剩余时间: %02d:%02d" % [minutes, seconds] + + # 根据剩余时间设置颜色 + if current_battle_state == BattleState.BATTLING: + if remaining_time <= 30: + time.modulate = Color.RED + elif remaining_time <= 60: + time.modulate = Color.ORANGE + else: + time.modulate = Color.WHITE + else: + time.modulate = Color.WHITE + +#显示战斗结果 +func show_battle_result(winner: String): + """显示战斗结果""" + battle_end_panel.visible = true + + var title_label = battle_end_panel.get_node("Title") + var contents_label = battle_end_panel.get_node("Contents") + + # 设置标题 + match winner: + "attacker": + title_label.text = "进攻方获胜!" + "defender": + title_label.text = "防守方获胜!" + _: + title_label.text = "平局!" + + # 生成战斗统计 + var stats_text = generate_battle_stats() + contents_label.text = stats_text + +#生成战斗统计信息 +func generate_battle_stats() -> String: + """生成战斗统计信息""" + var stats = "战斗时间: %.1f秒\n\n" % battle_time + + # MVP统计 + var max_damage = 0.0 + var mvp_pet = "" + for pet_id in damage_dealt: + if damage_dealt[pet_id] > max_damage: + max_damage = damage_dealt[pet_id] + mvp_pet = pet_id + + if mvp_pet != "": + stats += "MVP: %s (造成伤害: %.0f)\n" % [mvp_pet, max_damage] + + stats += "\n战斗详情:\n" + for log_entry in battle_log.slice(-10): # 显示最后10条记录 + stats += log_entry + "\n" + + return stats + +#添加战斗日志 +func add_battle_log(message: String): + """添加战斗日志""" + battle_log.append(message) + + # 限制日志数量以优化内存 + if battle_log.size() > 50: + battle_log = battle_log.slice(-30) # 保留最后30条 + + # 减少UI更新频率 + if battle_log.size() % 5 == 0: # 每5条日志更新一次UI + var display_logs = battle_log.slice(-15) # 只显示最后15条 + battle_details_text.text = "\n".join(display_logs) + +#返回农场按钮 +func _on_return_farm_pressed(): + """返回农场按钮""" + # 清理战斗场景 + clear_all_pets() + + # 清理子弹 + var bullets = get_tree().get_nodes_in_group("bullets") + for bullet in bullets: + bullet.queue_free() + + # 隐藏面板 + visible = false + +#获取战斗总结数据 +func get_battle_summary() -> Dictionary: + """获取战斗总结数据""" + return { + "battle_time": battle_time, + "damage_dealt": damage_dealt, + "damage_taken": damage_taken, + "kills": kills, + "battle_log": battle_log + } + +#直接认输逃跑 +func _on_stop_battle_button_pressed() -> void: + # 检查战斗是否已经结束 + if current_battle_state == BattleState.ENDED: + return + + # 添加逃跑日志 + add_battle_log("[color=yellow]玩家选择认输逃跑![/color]") + + # 立即结束战斗,设置防守方获胜(玩家认输) + end_battle("defender") + + # 清理所有宠物的AI状态并移除宠物 + for pet in all_pets: + if is_instance_valid(pet): + pet.current_state = NewPetBase.PetState.DEAD # 设置为死亡状态 + pet.current_target = null # 清除目标 + pet.is_alive = false # 设置为死亡 + pet.queue_free() # 删除宠物 + + # 清理所有召唤的仆从小弟 + clear_all_minions() + + # 清空宠物数组 + team_a_pets.clear() + team_b_pets.clear() + all_pets.clear() + + # 清理子弹 + var bullets = get_tree().get_nodes_in_group("bullets") + for bullet in bullets: + bullet.queue_free() + +#=====================UI显示=========================== + + +#开始战斗 +func start_battle(team_a_data: Array, team_b_data: Array): + """开始战斗""" + current_battle_state = BattleState.PREPARING + battle_time = 0.0 + battle_log.clear() + damage_dealt.clear() + damage_taken.clear() + kills.clear() + + # 清理现有宠物 + clear_all_pets() + # 生成队伍A的宠物(进攻方) + spawn_team(team_a_data, "attacker", team_a_node) + # 生成队伍B的宠物(防守方) + spawn_team(team_b_data, "defender", team_b_node) + + # 开始战斗 + current_battle_state = BattleState.BATTLING + battle_started.emit() + + add_battle_log("[color=yellow]战斗开始![/color]") + +#生成队伍宠物 +func spawn_team(team_data: Array, team_name: String, team_node: Node2D): + """生成队伍宠物""" + var positions = get_team_positions(team_node) + + for i in range(min(team_data.size(), positions.size())): + var pet_info = team_data[i] + var pet = spawn_pet(pet_info, team_name, positions[i]) + if pet: + if team_name == "attacker": + team_a_pets.append(pet) + else: + team_b_pets.append(pet) + all_pets.append(pet) + +#获取队伍位置点 +func get_team_positions(team_node: Node2D) -> Array[Vector2]: + """获取队伍位置点""" + var positions: Array[Vector2] = [] + for child in team_node.get_children(): + if child is Marker2D: + positions.append(team_node.global_position + child.position) + return positions + +#生成单个宠物 +func spawn_pet(pet_info: Dictionary, team: String, pos: Vector2) -> NewPetBase: + """生成单个宠物""" + var pet_scene = preload("res://Scene/NewPet/NewPetBase.tscn") + var pet = pet_scene.instantiate() + + # 添加到场景 + add_child(pet) + pet.global_position = pos + pet.pet_team = team + + # 处理服务器返回的完整宠物数据或配置键值 + var config_key = pet_info.get("config_key", "") + var pet_type = pet_info.get("pet_type", "") + var config_data: Dictionary + + if config_key != "" and pet_config: + # 使用指定的配置键值 + config_data = pet_config.get_pet_config(config_key) + apply_pet_config(pet, config_data) + apply_level_scaling(pet) + elif pet_type != "" and pet_config and pet_config.has_pet_config(pet_type): + # 使用宠物类型作为配置键值 + config_data = pet_config.get_pet_config(pet_type) + apply_pet_config(pet, config_data) + # 覆盖配置中的数据为服务器返回的实际数据 + apply_server_pet_data(pet, pet_info) + apply_level_scaling(pet) + else: + # 直接使用服务器返回的宠物数据 + apply_server_pet_data(pet, pet_info) + apply_level_scaling(pet) + + # 连接信号 + pet.pet_died.connect(_on_pet_died) + pet.pet_attacked.connect(_on_pet_attacked) + pet.pet_skill_used.connect(_on_pet_skill_used) + + # 添加到宠物组 + pet.add_to_group("pets") + + # 初始化统计数据 + damage_dealt[pet.pet_id] = 0.0 + damage_taken[pet.pet_id] = 0.0 + kills[pet.pet_id] = 0 + + pet_spawned.emit(pet) + return pet + +#应用服务器返回的宠物数据 +func apply_server_pet_data(pet: NewPetBase, pet_data: Dictionary): + """应用服务器返回的完整宠物数据""" + if pet_data.is_empty(): + return + + # 基本属性 + if pet_data.has("pet_name"): + pet.pet_name = pet_data["pet_name"] + if pet_data.has("pet_id"): + pet.pet_id = pet_data["pet_id"] + if pet_data.has("pet_type"): + pet.pet_type = pet_data["pet_type"] + if pet_data.has("pet_level"): + pet.pet_level = pet_data["pet_level"] + + # 生命与防御 + if pet_data.has("max_health"): + pet.max_health = pet_data["max_health"] + pet.current_health = pet.max_health + if pet_data.has("enable_health_regen"): + pet.enable_health_regen = pet_data["enable_health_regen"] + if pet_data.has("health_regen"): + pet.health_regen = pet_data["health_regen"] + if pet_data.has("enable_shield_regen"): + pet.enable_shield_regen = pet_data["enable_shield_regen"] + if pet_data.has("max_shield"): + pet.max_shield = pet_data["max_shield"] + pet.current_shield = pet.max_shield + if pet_data.has("shield_regen"): + pet.shield_regen = pet_data["shield_regen"] + if pet_data.has("max_armor"): + pet.max_armor = pet_data["max_armor"] + pet.current_armor = pet.max_armor + + # 攻击属性 + if pet_data.has("base_attack_damage"): + pet.base_attack_damage = pet_data["base_attack_damage"] + if pet_data.has("crit_rate"): + pet.crit_rate = pet_data["crit_rate"] + if pet_data.has("crit_damage"): + pet.crit_damage = pet_data["crit_damage"] + if pet_data.has("armor_penetration"): + pet.armor_penetration = pet_data["armor_penetration"] + + # 技能配置 + if pet_data.has("enable_multi_projectile_skill"): + pet.enable_multi_projectile_skill = pet_data["enable_multi_projectile_skill"] + if pet_data.has("multi_projectile_delay"): + pet.multi_projectile_delay = pet_data["multi_projectile_delay"] + if pet_data.has("enable_berserker_skill"): + pet.enable_berserker_skill = pet_data["enable_berserker_skill"] + if pet_data.has("berserker_bonus"): + pet.berserker_bonus = pet_data["berserker_bonus"] + if pet_data.has("berserker_duration"): + pet.berserker_duration = pet_data["berserker_duration"] + if pet_data.has("enable_self_destruct_skill"): + pet.enable_self_destruct_skill = pet_data["enable_self_destruct_skill"] + if pet_data.has("self_destruct_damage"): + pet.self_destruct_damage = pet_data["self_destruct_damage"] + if pet_data.has("enable_summon_pet_skill"): + pet.enable_summon_pet_skill = pet_data["enable_summon_pet_skill"] + if pet_data.has("summon_count"): + pet.summon_count = pet_data["summon_count"] + if pet_data.has("summon_scale"): + pet.summon_scale = pet_data["summon_scale"] + if pet_data.has("enable_death_respawn_skill"): + pet.enable_death_respawn_skill = pet_data["enable_death_respawn_skill"] + if pet_data.has("respawn_health_percentage"): + pet.respawn_health_percentage = pet_data["respawn_health_percentage"] + + # 移动属性 + if pet_data.has("move_speed"): + pet.move_speed = pet_data["move_speed"] + if pet_data.has("dodge_rate"): + pet.dodge_rate = pet_data["dodge_rate"] + + # 元素属性 + if pet_data.has("element_type"): + if typeof(pet_data["element_type"]) == TYPE_STRING: + pet.element_type = string_to_element_type(pet_data["element_type"]) + else: + pet.element_type = pet_data["element_type"] + if pet_data.has("element_damage_bonus"): + pet.element_damage_bonus = pet_data["element_damage_bonus"] + + # 武器系统 + if pet_data.has("left_weapon") and pet_data["left_weapon"] != "": + pet.equip_weapon(pet_data["left_weapon"], "left") + if pet_data.has("right_weapon") and pet_data["right_weapon"] != "": + pet.equip_weapon(pet_data["right_weapon"], "right") + + # 宠物外观配置 + if pet_data.has("pet_image"): + pet.pet_image_path = pet_data["pet_image"] + apply_pet_image(pet, pet_data["pet_image"]) + + # 打印调试信息 + print("[PetBattlePanel] 应用服务器宠物数据: %s (等级%d)" % [pet.pet_name, pet.pet_level]) + +#字符串转元素类型枚举 +func string_to_element_type(element_string: String) -> NewPetBase.ElementType: + """将字符串转换为元素类型枚举""" + match element_string.to_upper(): + "FIRE": + return NewPetBase.ElementType.FIRE + "WATER": + return NewPetBase.ElementType.WATER + "EARTH": + return NewPetBase.ElementType.EARTH + "METAL": + return NewPetBase.ElementType.METAL + "WOOD": + return NewPetBase.ElementType.WOOD + "THUNDER": + return NewPetBase.ElementType.THUNDER + _: + return NewPetBase.ElementType.NONE + +#将配置应用到宠物上 +func apply_pet_config(pet: NewPetBase, config: Dictionary): + """将配置应用到宠物上""" + if not config.is_empty(): + # 基本属性 + if config.has("pet_name"): + pet.pet_name = config["pet_name"] + if config.has("pet_id"): + pet.pet_id = config["pet_id"] + if config.has("pet_type"): + pet.pet_type = config["pet_type"] + if config.has("pet_level"): + pet.pet_level = config["pet_level"] + + # 生命与防御 + if config.has("max_health"): + pet.max_health = config["max_health"] + pet.current_health = pet.max_health + if config.has("enable_health_regen"): + pet.enable_health_regen = config["enable_health_regen"] + if config.has("health_regen"): + pet.health_regen = config["health_regen"] + if config.has("enable_shield_regen"): + pet.enable_shield_regen = config["enable_shield_regen"] + if config.has("max_shield"): + pet.max_shield = config["max_shield"] + pet.current_shield = pet.max_shield + if config.has("shield_regen"): + pet.shield_regen = config["shield_regen"] + if config.has("max_armor"): + pet.max_armor = config["max_armor"] + pet.current_armor = pet.max_armor + + # 攻击属性 + if config.has("base_attack_damage"): + pet.base_attack_damage = config["base_attack_damage"] + if config.has("crit_rate"): + pet.crit_rate = config["crit_rate"] + if config.has("crit_damage"): + pet.crit_damage = config["crit_damage"] + if config.has("armor_penetration"): + pet.armor_penetration = config["armor_penetration"] + + # 技能配置 + if config.has("enable_multi_projectile_skill"): + pet.enable_multi_projectile_skill = config["enable_multi_projectile_skill"] + if config.has("multi_projectile_delay"): + pet.multi_projectile_delay = config["multi_projectile_delay"] + if config.has("enable_berserker_skill"): + pet.enable_berserker_skill = config["enable_berserker_skill"] + if config.has("berserker_bonus"): + pet.berserker_bonus = config["berserker_bonus"] + if config.has("berserker_duration"): + pet.berserker_duration = config["berserker_duration"] + if config.has("enable_self_destruct_skill"): + pet.enable_self_destruct_skill = config["enable_self_destruct_skill"] + if config.has("self_destruct_damage"): + pet.self_destruct_damage = config["self_destruct_damage"] + if config.has("enable_summon_pet_skill"): + pet.enable_summon_pet_skill = config["enable_summon_pet_skill"] + if config.has("summon_count"): + pet.summon_count = config["summon_count"] + if config.has("summon_scale"): + pet.summon_scale = config["summon_scale"] + if config.has("enable_death_respawn_skill"): + pet.enable_death_respawn_skill = config["enable_death_respawn_skill"] + if config.has("respawn_health_percentage"): + pet.respawn_health_percentage = config["respawn_health_percentage"] + + # 移动属性 + if config.has("move_speed"): + pet.move_speed = config["move_speed"] + if config.has("dodge_rate"): + pet.dodge_rate = config["dodge_rate"] + + # 元素属性 + if config.has("element_type"): + pet.element_type = config["element_type"] + if config.has("element_damage_bonus"): + pet.element_damage_bonus = config["element_damage_bonus"] + + # 武器系统 + if config.has("left_weapon") and config["left_weapon"] != "": + pet.equip_weapon(config["left_weapon"], "left") + if config.has("right_weapon") and config["right_weapon"] != "": + pet.equip_weapon(config["right_weapon"], "right") + + # 宠物外观配置 + if config.has("pet_image"): + pet.pet_image_path = config["pet_image"] # 保存图片路径 + apply_pet_image(pet, config["pet_image"]) + +#应用宠物外观图片 +func apply_pet_image(pet: NewPetBase, image_path: String): + """应用宠物外观图片""" + if image_path == "" or not ResourceLoader.exists(image_path): + return + + # 加载新的宠物场景 + var new_pet_scene = load(image_path) + if not new_pet_scene: + return + + # 实例化新场景以获取图片组件 + var temp_instance = new_pet_scene.instantiate() + # 根节点本身就是PetImage + var new_pet_image = temp_instance + var new_left_tool = temp_instance.get_node_or_null("LeftToolImage") + var new_right_tool = temp_instance.get_node_or_null("RightToolImage") + + if new_pet_image and new_pet_image is AnimatedSprite2D: + # 复制动画帧到现有宠物 + if new_pet_image.sprite_frames: + pet.pet_image.sprite_frames = new_pet_image.sprite_frames + pet.pet_image.animation = new_pet_image.animation + pet.pet_image.scale = new_pet_image.scale + # 确保动画播放 + pet.pet_image.play() + + # 复制工具图片 + if new_left_tool and pet.left_tool_image: + pet.left_tool_image.texture = new_left_tool.texture + pet.left_tool_image.position = new_left_tool.position + pet.left_tool_image.flip_h = new_left_tool.flip_h + pet.left_tool_image.z_index = new_left_tool.z_index + pet.left_tool_image.visible = true + + if new_right_tool and pet.right_tool_image: + pet.right_tool_image.texture = new_right_tool.texture + pet.right_tool_image.position = new_right_tool.position + pet.right_tool_image.flip_h = new_right_tool.flip_h + pet.right_tool_image.show_behind_parent = new_right_tool.show_behind_parent + pet.right_tool_image.visible = true + + # 外观应用成功 + else: + pass # 静默处理错误 + + # 清理临时实例 + temp_instance.queue_free() + + # 重新更新武器图标(因为外观应用可能覆盖了武器图标) + if pet.weapon_system != null: + pet.update_weapon_icons() + +# 每5级特殊效果配置字典 +var level_bonus_config = { + 5: {"crit_rate": 0.05, "armor_penetration": 10.0, "dodge_rate": 0.05}, + 10: {"crit_rate": 0.05, "armor_penetration": 10.0, "dodge_rate": 0.05, "move_speed": 20.0}, + 15: {"crit_rate": 0.05, "armor_penetration": 15.0, "dodge_rate": 0.05, "crit_damage": 0.2}, + 20: {"crit_rate": 0.05, "armor_penetration": 15.0, "dodge_rate": 0.05, "health_regen": 2.0, "shield_regen": 2.0}, + 25: {"crit_rate": 0.05, "armor_penetration": 20.0, "dodge_rate": 0.05, "element_damage_bonus": 30.0}, + 30: {"crit_rate": 0.05, "armor_penetration": 20.0, "dodge_rate": 0.05, "knockback_force": 100.0}, + 35: {"crit_rate": 0.05, "armor_penetration": 25.0, "dodge_rate": 0.05, "attack_range": 20.0}, + 40: {"crit_rate": 0.05, "armor_penetration": 25.0, "dodge_rate": 0.05, "crit_damage": 0.3}, + 45: {"crit_rate": 0.05, "armor_penetration": 30.0, "dodge_rate": 0.05, "move_speed": 30.0}, + 50: {"crit_rate": 0.05, "armor_penetration": 30.0 , "dodge_rate": 0.05, "element_damage_bonus": 50.0, "health_regen": 3.0} +} + +#应用等级缩放 +func apply_level_scaling(pet: NewPetBase): + """应用等级缩放""" + # 每级+2基本属性 + var level_bonus = (pet.pet_level - 1) * 2.0 + + # 基本属性增长 + pet.max_health += level_bonus # 最大生命值 + pet.current_health = pet.max_health + pet.base_attack_damage += level_bonus # 基础攻击伤害 + pet.max_armor += level_bonus # 最大护甲值 + pet.current_armor = pet.max_armor + pet.max_shield += level_bonus # 最大护盾值 + pet.current_shield = pet.max_shield + + # 应用每5级的特殊效果 + for level_threshold in level_bonus_config.keys(): + if pet.pet_level >= level_threshold: + var bonuses = level_bonus_config[level_threshold] + for attribute in bonuses.keys(): + var bonus_value = bonuses[attribute] + match attribute: + "crit_rate": + pet.crit_rate = min(0.8, pet.crit_rate + bonus_value) # 最大80%暴击率 + "armor_penetration": + pet.armor_penetration += bonus_value + "attack_speed": + pet.attack_speed += bonus_value + "dodge_rate": + pet.dodge_rate = min(0.5, pet.dodge_rate + bonus_value) # 最大50%闪避率 + "move_speed": + pet.move_speed += bonus_value + "crit_damage": + pet.crit_damage += bonus_value + "health_regen": + pet.health_regen += bonus_value + "shield_regen": + pet.shield_regen += bonus_value + "element_damage_bonus": + pet.element_damage_bonus += bonus_value + "knockback_force": + pet.knockback_force += bonus_value + "attack_range": + pet.attack_range += bonus_value + +#更新战斗状态 +func update_battle_state(): + """更新战斗状态""" + # 先清理无效的宠物引用 + cleanup_invalid_pet_references() + + # 检查是否有队伍全灭 + var team_a_alive = team_a_pets.filter(func(pet): return is_instance_valid(pet) and pet.is_alive).size() + var team_b_alive = team_b_pets.filter(func(pet): return is_instance_valid(pet) and pet.is_alive).size() + + if team_a_alive == 0 and team_b_alive == 0: + end_battle("平局") + elif team_a_alive == 0: + end_battle("defender") + elif team_b_alive == 0: + end_battle("attacker") + + + +#=================即时清理防止游戏卡死===================== +#清理无效的宠物引用 +func cleanup_invalid_pet_references(): + """清理数组中的无效宠物引用""" + # 清理all_pets数组中的无效引用 + var valid_all_pets: Array[NewPetBase] = [] + for pet in all_pets: + if is_instance_valid(pet): + valid_all_pets.append(pet) + all_pets = valid_all_pets + + # 清理team_a_pets数组中的无效引用 + var valid_team_a_pets: Array[NewPetBase] = [] + for pet in team_a_pets: + if is_instance_valid(pet): + valid_team_a_pets.append(pet) + team_a_pets = valid_team_a_pets + + # 清理team_b_pets数组中的无效引用 + var valid_team_b_pets: Array[NewPetBase] = [] + for pet in team_b_pets: + if is_instance_valid(pet): + valid_team_b_pets.append(pet) + team_b_pets = valid_team_b_pets + +func cleanup_dead_objects(): + """清理死亡对象以优化性能""" + # 更严格的死亡宠物清理逻辑 + var dead_pets = [] + for pet in all_pets: + # 检查宠物是否真正死亡(防止重生技能导致的状态不一致) + if not is_instance_valid(pet): + # 无效的宠物对象,直接标记清理 + dead_pets.append(pet) + elif not pet.is_alive and pet.current_health <= 0: + # 确保宠物真正死亡:生命值为0且is_alive为false + # 额外检查:如果有重生技能但重生次数已用完 + if pet.enable_death_respawn_skill and pet.current_respawn_count < pet.max_respawn_count: + # 还有重生机会,不清理 + continue + else: + # 确认死亡,标记清理 + pet.current_state = NewPetBase.PetState.DEAD + dead_pets.append(pet) + + # 清理确认死亡的宠物 + for pet in dead_pets: + if is_instance_valid(pet): + # 确保从所有数组中移除 + all_pets.erase(pet) + team_a_pets.erase(pet) + team_b_pets.erase(pet) + # 从场景中移除 + if pet.get_parent(): + pet.get_parent().remove_child(pet) + pet.queue_free() + else: + # 无效对象,直接从数组中移除 + all_pets.erase(pet) + team_a_pets.erase(pet) + team_b_pets.erase(pet) + + # 清理无效子弹 + var bullets = get_tree().get_nodes_in_group("bullets") + for bullet in bullets: + if not is_instance_valid(bullet) or (bullet.has_method("is_active") and not bullet.is_active): + bullet.queue_free() + +#结束战斗 +func end_battle(winner: String): + """结束战斗""" + if current_battle_state == BattleState.ENDED: + return + + current_battle_state = BattleState.ENDED + + # 清理所有宠物的AI状态并移除宠物 + for pet in all_pets: + if is_instance_valid(pet): + pet.current_state = NewPetBase.PetState.DEAD # 设置为死亡状态 + pet.current_target = null # 清除目标 + pet.is_alive = false # 设置为死亡 + pet.queue_free() # 删除宠物 + + # 清理所有召唤的仆从小弟 + clear_all_minions() + + # 清空宠物数组 + team_a_pets.clear() + team_b_pets.clear() + all_pets.clear() + + # 清理子弹 + var bullets = get_tree().get_nodes_in_group("bullets") + for bullet in bullets: + bullet.queue_free() + + # 显示战斗结果 + show_battle_result(winner) + + # 生成战斗数据 + var battle_data = { + "attacker_pets": [], + "defender_pets": [], + "battle_duration": battle_time, + "damage_dealt": damage_dealt, + "damage_taken": damage_taken, + "kills": kills, + "battle_log": battle_log + } + + # 发射战斗结束信号 + battle_ended.emit(winner, battle_data) + + add_battle_log("[color=red]战斗结束!获胜方: %s[/color]" % winner) + + for child in get_children(): + if child is Area2D: + remove_child(child) + child.queue_free() + +#清理所有宠物 +func clear_all_pets(): + """清理所有宠物""" + for pet in all_pets: + if is_instance_valid(pet): + pet.queue_free() + + # 清理所有召唤的仆从小弟 + clear_all_minions() + + team_a_pets.clear() + team_b_pets.clear() + all_pets.clear() + +#清理所有召唤的仆从小弟 +func clear_all_minions(): + """清理所有召唤的仆从小弟""" + # 获取所有pets组中的节点 + var all_pets_in_group = get_tree().get_nodes_in_group("pets") + var minions_cleared = 0 + + for pet in all_pets_in_group: + if is_instance_valid(pet) and pet is NewPetBase: + # 立即设置为死亡状态 + pet.is_alive = false + pet.current_state = NewPetBase.PetState.DEAD + pet.current_target = null + # 从场景中移除 + if pet.get_parent(): + pet.get_parent().remove_child(pet) + pet.queue_free() + minions_cleared += 1 + + if minions_cleared > 0: + add_battle_log("[color=purple]清理了 %d 个召唤仆从[/color]" % minions_cleared) + +#立即清理所有宠物 +func clear_all_pets_immediately(): + """立即清理所有宠物(用于时间到时的平局处理)""" + for pet in all_pets: + if is_instance_valid(pet): + # 立即设置为死亡状态 + pet.is_alive = false + pet.current_state = NewPetBase.PetState.DEAD + pet.current_target = null + # 立即从场景中移除 + pet.get_parent().remove_child(pet) + pet.queue_free() + + # 清理所有召唤的仆从小弟 + clear_all_minions() + + # 清理所有子弹 + var bullets = get_tree().get_nodes_in_group("bullets") + for bullet in bullets: + if is_instance_valid(bullet): + bullet.queue_free() + + # 清空宠物数组 + team_a_pets.clear() + team_b_pets.clear() + all_pets.clear() + + add_battle_log("[color=red]时间到!所有宠物已被清理,战斗结束![/color]") +#=================即时清理防止游戏卡死===================== + + +#宠物死亡事件 +func _on_pet_died(pet: NewPetBase): + """宠物死亡事件""" + # 简化死亡处理,减少不必要的计算 + if battle_log.size() < 30: # 限制死亡日志数量 + add_battle_log("[color=red]%s 死亡[/color]" % pet.pet_name) + +#宠物攻击事件 +func _on_pet_attacked(attacker: NewPetBase, target: NewPetBase, damage: float): + """宠物攻击事件""" + # 简化统计更新 + damage_dealt[attacker.pet_id] = damage_dealt.get(attacker.pet_id, 0.0) + damage + damage_taken[target.pet_id] = damage_taken.get(target.pet_id, 0.0) + damage + + # 大幅减少攻击日志,只记录关键事件 + if damage >= 100: # 只记录高伤害攻击 + add_battle_log("[color=orange]%s->%s %.0f[/color]" % [attacker.pet_name, target.pet_name, damage]) + +#宠物技能使用事件 +func _on_pet_skill_used(pet: NewPetBase, skill_name: String): + """宠物技能使用事件""" + # 减少技能日志,只记录重要技能 + if skill_name in ["狂暴模式", "自爆", "召唤小弟", "死亡重生"]: + add_battle_log("[color=cyan]%s:%s[/color]" % [pet.pet_name, skill_name]) + + +#================偷菜对战设置=========================== +# 设置偷菜对战 +func setup_steal_battle(attacker_pets: Array, defender_pets: Array, attacker_name: String, defender_name: String): + """设置偷菜对战""" + print("[PetBattlePanel] 设置偷菜对战: 攻击者=%s, 防守者=%s" % [attacker_name, defender_name]) + print("[PetBattlePanel] 攻击方宠物数量: %d, 防守方宠物数量: %d" % [attacker_pets.size(), defender_pets.size()]) + + # 检查双方是否都有宠物 + if attacker_pets.is_empty() or defender_pets.is_empty(): + print("[PetBattlePanel] 错误: 双方必须至少有一个宠物才能参战") + return false + + # 重置战斗状态和UI + current_battle_state = BattleState.PREPARING + battle_time = 0.0 + battle_log.clear() + damage_dealt.clear() + damage_taken.clear() + kills.clear() + + # 隐藏战斗结束面板,显示战斗细节面板 + battle_end_panel.visible = false + battle_details_panel.visible = true + + # 重置战斗日志显示 + battle_details_text.text = "[color=green]战斗准备中...[/color]\n" + + # 限制出战宠物数量最多4个 + var limited_attacker_pets = attacker_pets.slice(0, min(4, attacker_pets.size())) + var limited_defender_pets = defender_pets.slice(0, min(4, defender_pets.size())) + + print("[PetBattlePanel] 限制后攻击方宠物数量: %d, 防守方宠物数量: %d" % [limited_attacker_pets.size(), limited_defender_pets.size()]) + + # 显示对战面板 + show() + + # 清理现有宠物 + clear_all_pets() + + # 设置队伍信息 + team_a_name = attacker_name + "(攻击方)" + team_b_name = defender_name + "(防守方)" + + # 更新UI显示 + #team_a_label.text = team_a_name + #team_b_label.text = team_b_name + + # 获取队伍位置点 + var team_a_positions = get_team_positions(team_a_node) + var team_b_positions = get_team_positions(team_b_node) + + # 生成攻击方宠物(teamA) + for i in range(limited_attacker_pets.size()): + var pet_data = limited_attacker_pets[i] + var position = team_a_positions[i] if i < team_a_positions.size() else team_a_positions[0] + var pet = spawn_pet(pet_data, "attacker", position) + if pet: + team_a_pets.append(pet) + all_pets.append(pet) + + # 生成防守方宠物(teamB) + for i in range(limited_defender_pets.size()): + var pet_data = limited_defender_pets[i] + var position = team_b_positions[i] if i < team_b_positions.size() else team_b_positions[0] + var pet = spawn_pet(pet_data, "defender", position) + if pet: + team_b_pets.append(pet) + all_pets.append(pet) + + print("[PetBattlePanel] 对战设置完成,攻击方: %d只,防守方: %d只" % [team_a_pets.size(), team_b_pets.size()]) + + # 添加战斗日志 + add_battle_log("[color=yellow]偷菜对战开始![/color]") + add_battle_log("[color=cyan]%s VS %s[/color]" % [team_a_name, team_b_name]) + + # 开始战斗 + start_battle(limited_attacker_pets, limited_defender_pets) + + return true +#================偷菜对战设置=========================== + + +#================场外辅助=========================== + +#美化确认弹窗 +func setup_confirm_dialog(): + """设置和美化确认弹窗""" + confirm_dialog.title = "辅助功能确认" + confirm_dialog.ok_button_text = "确认使用" + confirm_dialog.cancel_button_text = "取消" + confirm_dialog.unresizable = false + confirm_dialog.size = Vector2i(450, 200) + + # 创建并应用主题样式 + var theme = Theme.new() + var style_box = StyleBoxFlat.new() + style_box.bg_color = Color(0.2, 0.3, 0.4, 0.95) + style_box.border_width_left = 3 + style_box.border_width_right = 3 + style_box.border_width_top = 3 + style_box.border_width_bottom = 3 + style_box.border_color = Color(0.4, 0.6, 0.8, 1.0) + style_box.corner_radius_top_left = 10 + style_box.corner_radius_top_right = 10 + style_box.corner_radius_bottom_left = 10 + style_box.corner_radius_bottom_right = 10 + + theme.set_stylebox("panel", "AcceptDialog", style_box) + confirm_dialog.theme = theme + +#更新辅助功能冷却计时器 +func update_assist_cooldowns(delta: float): + """更新辅助功能冷却计时器""" + # 更新冷却计时器 + if heal_cooldown_timer > 0: + heal_cooldown_timer -= delta + if heal_cooldown_timer <= 0: + team_a_heal_button.disabled = false + team_a_heal_button.text = "团队治疗" + else: + team_a_heal_button.text = "治疗冷却 %.1fs" % heal_cooldown_timer + + if rage_cooldown_timer > 0: + rage_cooldown_timer -= delta + if rage_cooldown_timer <= 0: + team_a_rage_button.disabled = false + team_a_rage_button.text = "团队狂暴" + else: + team_a_rage_button.text = "狂暴冷却 %.1fs" % rage_cooldown_timer + + if shield_cooldown_timer > 0: + shield_cooldown_timer -= delta + if shield_cooldown_timer <= 0: + team_a_shield_button.disabled = false + team_a_shield_button.text = "团队护盾" + else: + team_a_shield_button.text = "护盾冷却 %.1fs" % shield_cooldown_timer + +#显示辅助功能确认弹窗 +func show_assist_confirm(operation_type: String, description: String, effect: String): + """显示辅助功能确认弹窗""" + current_assist_operation = operation_type + + # 设置弹窗内容(纯文本格式) + var dialog_text = "%s\n\n" % description + dialog_text += "效果:%s\n\n" % effect + dialog_text += "注意:使用后该技能将进入5秒冷却时间\n\n" + dialog_text += "确定要使用这个辅助功能吗?" + + confirm_dialog.dialog_text = dialog_text + confirm_dialog.popup_centered() + +#确认使用辅助功能 +func _on_assist_confirmed(): + """确认使用辅助功能""" + match current_assist_operation: + "heal": + execute_team_heal() + "rage": + execute_team_rage() + "shield": + execute_team_shield() + + current_assist_operation = "" + +#取消使用辅助功能 +func _on_assist_canceled(): + """取消使用辅助功能""" + current_assist_operation = "" + +#执行团队治疗 +func execute_team_heal(): + """执行团队治疗功能""" + var healed_count = 0 + # 只对teamA(attacker队伍)的宠物生效 + for pet in all_pets: + if pet.is_alive and pet.pet_team == "attacker": + var heal_amount = pet.max_health * 0.3 # 恢复30%最大生命值 + pet.current_health = min(pet.max_health, pet.current_health + heal_amount) + pet.update_health_bar() + healed_count += 1 + + # 添加治疗特效(绿色光芒)- 使用弱引用避免访问已销毁的对象 + if pet.pet_image: + var pet_ref = weakref(pet) + var tween = create_tween() + tween.tween_method(func(color): + var pet_obj = pet_ref.get_ref() + if pet_obj != null and is_instance_valid(pet_obj) and pet_obj.pet_image: + pet_obj.pet_image.modulate = color, + Color.WHITE, Color.GREEN, 0.3) + tween.tween_method(func(color): + var pet_obj = pet_ref.get_ref() + if pet_obj != null and is_instance_valid(pet_obj) and pet_obj.pet_image: + pet_obj.pet_image.modulate = color, + Color.GREEN, Color.WHITE, 0.3) + + # 启动冷却 + heal_cooldown_timer = assist_cooldown_time + team_a_heal_button.disabled = true + + add_battle_log("[color=green]🌿 使用团队治疗!为 %d 只teamA宠物恢复了30%%生命值[/color]" % healed_count) + +#执行团队狂暴 +func execute_team_rage(): + """执行团队狂暴功能""" + var raged_count = 0 + # 只对teamA(attacker队伍)的宠物生效 + for pet in all_pets: + if pet.is_alive and pet.pet_team == "attacker": + # 激活狂暴模式5秒 + pet.is_berserker = true + raged_count += 1 + + # 添加狂暴特效(红色光芒) + if pet.pet_image: + pet.pet_image.modulate = Color.RED + + # 5秒后自动取消狂暴(使用弱引用避免访问已销毁的对象) + var pet_ref = weakref(pet) + get_tree().create_timer(5.0).timeout.connect(func(): + var pet_obj = pet_ref.get_ref() + if pet_obj != null and is_instance_valid(pet_obj) and pet_obj.is_alive: + pet_obj.is_berserker = false + if pet_obj.pet_image: + pet_obj.pet_image.modulate = Color.WHITE + ) + + # 启动冷却 + rage_cooldown_timer = assist_cooldown_time + team_a_rage_button.disabled = true + + add_battle_log("[color=red]🔥 使用团队狂暴!%d 只teamA宠物进入狂暴状态5秒[/color]" % raged_count) + +#执行团队护盾 +func execute_team_shield(): + """执行团队护盾功能""" + var shielded_count = 0 + # 只对teamA(attacker队伍)的宠物生效 + for pet in all_pets: + if pet.is_alive and pet.pet_team == "attacker": + # 增加100点护盾(允许超过最大值) + pet.current_shield += 10000 + # 临时提高最大护盾值以显示正确的进度条 + if pet.current_shield > pet.max_shield: + pet.max_shield = pet.current_shield + pet.update_shield_bar() + shielded_count += 1 + + # 添加护盾特效(蓝色光芒)- 使用弱引用避免访问已销毁的对象 + if pet.pet_image: + var pet_ref = weakref(pet) + var tween = create_tween() + tween.tween_method(func(color): + var pet_obj = pet_ref.get_ref() + if pet_obj != null and is_instance_valid(pet_obj) and pet_obj.pet_image: + pet_obj.pet_image.modulate = color, + Color.WHITE, Color.BLUE, 0.3) + tween.tween_method(func(color): + var pet_obj = pet_ref.get_ref() + if pet_obj != null and is_instance_valid(pet_obj) and pet_obj.pet_image: + pet_obj.pet_image.modulate = color, + Color.BLUE, Color.WHITE, 0.3) + + # 启动冷却 + shield_cooldown_timer = assist_cooldown_time + team_a_shield_button.disabled = true + + add_battle_log("[color=blue]🛡️ 使用团队护盾!为 %d 只teamA宠物增加了100点护甲[/color]" % shielded_count) + +#团队治疗按钮(直接恢复30%血量) +func _on_team_a_heal_pressed() -> void: + if heal_cooldown_timer > 0: + return + + show_assist_confirm("heal", "团队治疗", "为所有存活的己方宠物恢复30%最大生命值") + +#团队狂暴按钮(直接进入五秒狂暴模式,与狂暴技能不冲突) +func _on_team_a_rage_pressed() -> void: + if rage_cooldown_timer > 0: + return + + show_assist_confirm("rage", "团队狂暴", "让所有存活的己方宠物进入狂暴状态5秒,攻击力翻倍") + +#团队护盾按钮(直接加100护盾) +func _on_team_a_shield_pressed() -> void: + if shield_cooldown_timer > 0: + return + + show_assist_confirm("shield", "团队护盾", "为所有存活的己方宠物增加100点护甲值") + +#================场外辅助=========================== diff --git a/Scene/NewPet/PetBattlePanel.gd.uid b/Scene/NewPet/PetBattlePanel.gd.uid new file mode 100644 index 0000000..28a724d --- /dev/null +++ b/Scene/NewPet/PetBattlePanel.gd.uid @@ -0,0 +1 @@ +uid://bt06n5cxip4kr diff --git a/Scene/NewPet/PetBattlePanel.tscn b/Scene/NewPet/PetBattlePanel.tscn new file mode 100644 index 0000000..9af111c --- /dev/null +++ b/Scene/NewPet/PetBattlePanel.tscn @@ -0,0 +1,318 @@ +[gd_scene load_steps=3 format=3 uid="uid://diwbnwhnq026"] + +[ext_resource type="Script" uid="uid://bt06n5cxip4kr" path="res://Scene/NewPet/PetBattlePanel.gd" id="1_0uw4q"] +[ext_resource type="Texture2D" uid="uid://dh0dsw3jr0gra" path="res://assets/宠物对战背景图片/背景2.webp" id="2_c80tv"] + +[node name="PetBattlePanel" type="Panel"] +offset_right = 1400.0 +offset_bottom = 720.0 +script = ExtResource("1_0uw4q") + +[node name="MapBackGround" type="TextureRect" parent="."] +layout_mode = 0 +offset_right = 1557.0 +offset_bottom = 867.0 +scale = Vector2(0.9, 0.9) +texture = ExtResource("2_c80tv") + +[node name="TeamB" type="Node2D" parent="."] +position = Vector2(1239, 421) + +[node name="Pos1" type="Marker2D" parent="TeamB"] +position = Vector2(17, -166) + +[node name="Pos2" type="Marker2D" parent="TeamB"] +position = Vector2(42, 160) + +[node name="Pos3" type="Marker2D" parent="TeamB"] +position = Vector2(42, -38) + +[node name="Pos4" type="Marker2D" parent="TeamB"] +position = Vector2(21, -315) + +[node name="Pos5" type="Marker2D" parent="TeamB"] +position = Vector2(42, -102) + +[node name="Pos6" type="Marker2D" parent="TeamB"] +position = Vector2(42, 96) + +[node name="Pos7" type="Marker2D" parent="TeamB"] +position = Vector2(42, 32) + +[node name="Pos8" type="Marker2D" parent="TeamB"] +position = Vector2(21, -251) + +[node name="Pos9" type="Marker2D" parent="TeamB"] +position = Vector2(42, -230) + +[node name="Pos10" type="Marker2D" parent="TeamB"] +position = Vector2(42, 224) + +[node name="TeamA" type="Node2D" parent="."] +position = Vector2(138, 134) + +[node name="Pos1" type="Marker2D" parent="TeamA"] +position = Vector2(-37, 408) + +[node name="Pos2" type="Marker2D" parent="TeamA"] +position = Vector2(-41, 93) + +[node name="Pos3" type="Marker2D" parent="TeamA"] +position = Vector2(-38, 313) + +[node name="Pos4" type="Marker2D" parent="TeamA"] +position = Vector2(-38, 223) + +[node name="Pos5" type="Marker2D" parent="TeamA"] +position = Vector2(-38, 133) + +[node name="Pos6" type="Marker2D" parent="TeamA"] +position = Vector2(-38, 43) + +[node name="Pos7" type="Marker2D" parent="TeamA"] +position = Vector2(-38, 358) + +[node name="Pos8" type="Marker2D" parent="TeamA"] +position = Vector2(-38, 268) + +[node name="Pos9" type="Marker2D" parent="TeamA"] +position = Vector2(-38, 178) + +[node name="Pos10" type="Marker2D" parent="TeamA"] +position = Vector2(-38, 453) + +[node name="PlayerSkillPanel" type="Panel" parent="."] +layout_mode = 0 +offset_left = 1143.0 +offset_right = 1400.0 +offset_bottom = 720.0 + +[node name="Title" type="Label" parent="PlayerSkillPanel"] +self_modulate = Color(0, 1, 0, 1) +layout_mode = 0 +offset_right = 257.0 +offset_bottom = 40.0 +theme_override_font_sizes/font_size = 25 +text = "辅助功能" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="TeamASkills" type="VBoxContainer" parent="PlayerSkillPanel"] +layout_mode = 0 +offset_top = 50.0 +offset_right = 257.0 +offset_bottom = 300.0 + +[node name="TeamAHeal" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "团队治疗" + +[node name="TeamARage" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "团队狂暴" + +[node name="TeamAShield" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +text = "团队护盾" + +[node name="test" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="test2" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="test3" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="test4" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="test5" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="test6" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="test7" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="test8" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="test9" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="test10" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="test11" type="Button" parent="PlayerSkillPanel/TeamASkills"] +layout_mode = 2 +theme_override_font_sizes/font_size = 18 +disabled = true +text = "测试" + +[node name="BattleControls" type="VBoxContainer" parent="PlayerSkillPanel"] +layout_mode = 0 +offset_top = 580.0 +offset_right = 257.0 +offset_bottom = 720.0 + +[node name="StartBattleButton" type="Button" parent="PlayerSkillPanel/BattleControls"] +visible = false +layout_mode = 2 +theme_override_colors/font_color = Color(0, 1, 0, 1) +theme_override_font_sizes/font_size = 20 +text = "开始战斗" + +[node name="StopBattleButton" type="Button" parent="PlayerSkillPanel/BattleControls"] +layout_mode = 2 +theme_override_colors/font_color = Color(1, 0, 0, 1) +theme_override_font_sizes/font_size = 20 +text = "逃跑" + +[node name="BattleEndPanel" type="Panel" parent="."] +visible = false +top_level = true +layout_mode = 0 +offset_left = 294.0 +offset_right = 1071.0 +offset_bottom = 720.0 + +[node name="Title" type="Label" parent="BattleEndPanel"] +layout_mode = 0 +offset_right = 777.0 +offset_bottom = 104.0 +theme_override_colors/font_color = Color(0.991435, 0.798103, 0.357309, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 5 +theme_override_constants/shadow_offset_y = 5 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 20 +theme_override_font_sizes/font_size = 60 +text = "战斗结束" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Contents" type="RichTextLabel" parent="BattleEndPanel"] +layout_mode = 0 +offset_top = 104.0 +offset_right = 777.0 +offset_bottom = 567.0 +theme_override_font_sizes/normal_font_size = 30 +bbcode_enabled = true +text = "[宠物名字]获得300经验, +增加200亲密度" +horizontal_alignment = 1 + +[node name="ReturnFarmButton" type="Button" parent="BattleEndPanel"] +layout_mode = 0 +offset_left = 294.0 +offset_top = 567.0 +offset_right = 502.0 +offset_bottom = 644.0 +theme_override_font_sizes/font_size = 50 +text = "返回农场" + +[node name="Title" type="Label" parent="."] +layout_mode = 0 +offset_right = 1400.0 +offset_bottom = 55.0 +theme_override_colors/font_color = Color(0.623819, 1, 0.593898, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 5 +theme_override_constants/shadow_offset_y = 5 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 10 +theme_override_font_sizes/font_size = 50 +text = "宠物对战" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Time" type="Label" parent="."] +layout_mode = 0 +offset_left = 574.0 +offset_top = 60.0 +offset_right = 813.0 +offset_bottom = 129.0 +theme_override_colors/font_color = Color(0.623529, 1, 1, 1) +theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/shadow_offset_x = 5 +theme_override_constants/shadow_offset_y = 5 +theme_override_constants/outline_size = 20 +theme_override_constants/shadow_outline_size = 10 +theme_override_font_sizes/font_size = 35 +text = "[05:00]" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="PetBattleDetailsPanel" type="Panel" parent="."] +layout_mode = 0 +offset_right = 257.0 +offset_bottom = 720.0 + +[node name="Title" type="Label" parent="PetBattleDetailsPanel"] +layout_mode = 0 +offset_right = 257.0 +offset_bottom = 23.0 +theme_override_font_sizes/font_size = 30 +text = "战斗细节" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="BattleDetails" type="RichTextLabel" parent="PetBattleDetailsPanel"] +layout_mode = 0 +offset_top = 42.0 +offset_right = 257.0 +offset_bottom = 720.0 +bbcode_enabled = true + +[node name="ConfirmDialog" type="ConfirmationDialog" parent="."] +title = "弹窗标题" +initial_position = 1 +size = Vector2i(400, 300) +unresizable = true +ok_button_text = "确认" +dialog_text = "弹窗内容" +dialog_autowrap = true +cancel_button_text = "取消" + +[connection signal="pressed" from="PlayerSkillPanel/TeamASkills/TeamAHeal" to="." method="_on_team_a_heal_pressed"] +[connection signal="pressed" from="PlayerSkillPanel/TeamASkills/TeamARage" to="." method="_on_team_a_rage_pressed"] +[connection signal="pressed" from="PlayerSkillPanel/TeamASkills/TeamAShield" to="." method="_on_team_a_shield_pressed"] +[connection signal="pressed" from="PlayerSkillPanel/BattleControls/StopBattleButton" to="." method="_on_stop_battle_button_pressed"] diff --git a/Scene/NewPet/PetConfig.gd b/Scene/NewPet/PetConfig.gd new file mode 100644 index 0000000..9df6ec2 --- /dev/null +++ b/Scene/NewPet/PetConfig.gd @@ -0,0 +1,396 @@ +extends Node +class_name PetConfig +# 每一种宠物的配置数据 方便导出JSON数据,放到MongoDB数据库上 + + + +# 攻击类型枚举(简化为仅近战) +enum AttackType { + MELEE # 近战攻击 +} + +enum ElementType { + NONE, METAL, WOOD, WATER, FIRE, EARTH, THUNDER +} + +enum PetState { + IDLE, # 待机 + MOVING, # 移动 + ATTACKING, # 攻击中 + SKILL_CASTING, # 释放技能 + DEAD # 死亡 +} + +#==========================以下是导出数据可以被修改的========================================= +# 基本属性 +var pet_name: String = "萌芽小绿" # 宠物名称 +var pet_id: String = "0001" # 宠物唯一编号 +var pet_type: String = "小绿" # 宠物种类 +var pet_level: int = 1 # 宠物等级 +var pet_experience: int = 0 # 宠物经验值 + +#性格 出生日期 爱好 个人介绍 +var pet_temperament: String = "温顺" # 性格 +var pet_birthday: String = "2023-01-01" # 出生日期 +var pet_hobby: String = "喜欢吃pet" # 爱好 +var pet_introduction: String = "我是一个小绿" # 个人介绍 + +# 生命与防御 +var max_health: float = 200.0 # 最大生命值 +var enable_health_regen: bool = true # 是否开启生命恢复 +var health_regen: float = 1.0 # 每秒生命恢复大小 +var enable_shield_regen: bool = true # 是否开启护盾恢复 +var max_shield: float = 100.0 # 最大护盾值 +var shield_regen: float = 1.0 # 每秒护盾恢复大小 +var max_armor: float = 100.0 # 最大护甲值 + +# 攻击属性 +var base_attack_damage: float = 25.0 # 基础攻击力 +var crit_rate: float = 0.1 # 暴击几率(0~1) +var crit_damage: float = 1.5 # 暴击伤害倍率(1.5 = 150%伤害) +var armor_penetration: float = 0.0 # 护甲穿透值(直接减少对方护甲值) + +#======================以后有新技能在这里添加============================== +# 技能-多发射击 +var enable_multi_projectile_skill: bool = false +var multi_projectile_delay: float = 2.0 # 多发射击延迟时间(秒) + +# 技能-狂暴模式 +var enable_berserker_skill: bool = false +var berserker_bonus: float = 1.5 # 狂暴伤害加成 +var berserker_duration: float = 5.0 # 狂暴持续时间(秒) + +#技能-自爆 +var enable_self_destruct_skill: bool = false +var self_destruct_damage: float = 50.0 # 自爆伤害值 + +#技能-召唤小弟 +var enable_summon_pet_skill: bool = false +var summon_count: int = 1 # 召唤小弟数量 +var summon_scale: float = 0.1 # 召唤小弟属性缩放比例(10%) + +#技能-死亡重生 +var enable_death_respawn_skill: bool = false +var respawn_health_percentage: float = 0.3 # 重生时恢复的血量百分比(30%) +#======================以后有新技能在这里添加============================== + +# 移动属性 +var move_speed: float = 150.0 # 移动速度(像素/秒) +var dodge_rate: float = 0.05 # 闪避概率(0~1) + +# 元素属性 +var element_type: ElementType = ElementType.NONE # 元素类型(例如火、水、雷等) +var element_damage_bonus: float = 50.0 # 元素伤害加成(额外元素伤害) + +# 武器系统 +var left_weapon: String = "" # 左手武器名称 +var right_weapon: String = "" # 右手武器名称 + +# 宠物配置字典 - 用于导出到MongoDB数据库 +var pet_configs: Dictionary = { + "烈焰鸟": { + "pet_name": "树萌芽の烈焰鸟", + "can_purchase": true, + "cost": 1000, + "pet_image": "res://Scene/NewPet/PetType/flying_bird.tscn", + "pet_id": "0001", + "pet_type": "烈焰鸟", + "pet_level": 1, + "pet_experience": 500, + "pet_temperament": "勇猛", + "pet_birthday": "2023-03-15", + "pet_hobby": "喜欢战斗和烈火", + "pet_introduction": "我爱吃虫子", + "max_health": 300, + "enable_health_regen": true, + "health_regen": 2, + "enable_shield_regen": true, + "max_shield": 150, + "shield_regen": 1.5, + "max_armor": 120, + "base_attack_damage": 40, + "crit_rate": 0.15, + "crit_damage": 2, + "armor_penetration": 10, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 2, + "enable_berserker_skill": true, + "berserker_bonus": 1.8, + "berserker_duration": 6, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": false, + "enable_death_respawn_skill": true, + "respawn_health_percentage": 0.4, + "move_speed": 180, + "dodge_rate": 0.08, + "element_type": "FIRE", + "element_damage_bonus": 75, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "大蓝虫": { + "pet_name": "树萌芽の大蓝虫", + "can_purchase": true, + "cost": 1000, + "pet_image": "res://Scene/NewPet/PetType/big_beetle.tscn", + "pet_id": "0002", + "pet_type": "大蓝虫", + "pet_level": 8, + "pet_experience": 320, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和小甲壳虫玩", + "pet_introduction": "我是大蓝虫,不是大懒虫!", + "max_health": 180, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200, + "shield_regen": 2.5, + "max_armor": 80, + "base_attack_damage": 35, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "小蓝虫": { + "pet_name": "树萌芽の小蓝虫", + "can_purchase": true, + "cost": 1000, + "pet_image": "res://Scene/NewPet/PetType/small_beetle.tscn", + "pet_id": "0002", + "pet_type": "小蓝虫", + "pet_level": 1, + "pet_experience": 0, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和大蓝虫玩", + "pet_introduction": "我是小蓝虫,不是小懒虫!", + "max_health": 90, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200, + "shield_regen": 2.5, + "max_armor": 80, + "base_attack_damage": 35, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "小蓝": { + "pet_name": "树萌芽の小蓝", + "can_purchase": true, + "cost": 1000, + "pet_image": "res://Scene/NewPet/PetType/small_blue.tscn", + "pet_id": "0002", + "pet_type": "小蓝", + "pet_level": 1, + "pet_experience": 0, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和小黄一起玩", + "pet_introduction": "我是小黄!", + "max_health": 120, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200, + "shield_regen": 2.5, + "max_armor": 80, + "base_attack_damage": 35, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + } +} + +# 初始化函数 +func _ready(): + """节点准备就绪时自动加载JSON配置""" + load_configs_from_json() + +# 手动初始化配置的函数 +func initialize_configs(): + """手动初始化宠物配置,优先从JSON加载""" + if not load_configs_from_json(): + print("JSON加载失败,使用默认配置") + # 如果JSON加载失败,保持使用代码中的默认配置 + + +# 获取宠物配置的函数 +func get_pet_config(pet_key: String) -> Dictionary: + """根据宠物键值获取配置""" + if pet_configs.has(pet_key): + return pet_configs[pet_key] + else: + print("未找到宠物配置: ", pet_key, ",使用默认配置") + return get_default_config() + +# 获取所有宠物配置键值的函数 +func get_all_pet_keys() -> Array: + """获取所有可用的宠物配置键值""" + return pet_configs.keys() + +# 检查宠物配置是否存在的函数 +func has_pet_config(pet_key: String) -> bool: + """检查指定的宠物配置是否存在""" + return pet_configs.has(pet_key) + +# 获取默认配置的函数 +func get_default_config() -> Dictionary: + """获取默认宠物配置""" + return { + "pet_name": pet_name, + "pet_id": pet_id, + "pet_type": pet_type, + "pet_level": pet_level, + "pet_experience": pet_experience, + "pet_temperament": pet_temperament, + "pet_birthday": pet_birthday, + "pet_hobby": pet_hobby, + "pet_introduction": pet_introduction, + "max_health": max_health, + "enable_health_regen": enable_health_regen, + "health_regen": health_regen, + "enable_shield_regen": enable_shield_regen, + "max_shield": max_shield, + "shield_regen": shield_regen, + "max_armor": max_armor, + "base_attack_damage": base_attack_damage, + "crit_rate": crit_rate, + "crit_damage": crit_damage, + "armor_penetration": armor_penetration, + "enable_multi_projectile_skill": enable_multi_projectile_skill, + "multi_projectile_delay": multi_projectile_delay, + "enable_berserker_skill": enable_berserker_skill, + "berserker_bonus": berserker_bonus, + "berserker_duration": berserker_duration, + "enable_self_destruct_skill": enable_self_destruct_skill, + "self_destruct_damage": self_destruct_damage, + "enable_summon_pet_skill": enable_summon_pet_skill, + "summon_count": summon_count, + "summon_scale": summon_scale, + "enable_death_respawn_skill": enable_death_respawn_skill, + "respawn_health_percentage": respawn_health_percentage, + "move_speed": move_speed, + "dodge_rate": dodge_rate, + "element_type": element_type, + "element_damage_bonus": element_damage_bonus, + "left_weapon": left_weapon, + "right_weapon": right_weapon + } + +# 字符串转换为ElementType枚举的函数 +func string_to_element_type(element_str: String) -> ElementType: + """将字符串转换为ElementType枚举""" + match element_str.to_upper(): + "NONE":#没有元素类型 + return ElementType.NONE + "METAL":#金元素 + return ElementType.METAL + "WOOD":#木元素 + return ElementType.WOOD + "WATER":#水元素 + return ElementType.WATER + "FIRE": #火元素 + return ElementType.FIRE + "EARTH":#土元素 + return ElementType.EARTH + "THUNDER":#雷元素 + return ElementType.THUNDER + _: + return ElementType.NONE + +# 从JSON文件加载宠物配置的函数 +func load_configs_from_json(file_path: String = "res://Scene/NewPet/Pet_data.json") -> bool: + """从JSON文件加载宠物配置""" + if not FileAccess.file_exists(file_path): + print("宠物配置文件不存在: ", file_path) + return false + + var file = FileAccess.open(file_path, FileAccess.READ) + if file == null: + print("无法打开宠物配置文件: ", file_path) + return false + + var json_string = file.get_as_text() + file.close() + + var json = JSON.new() + var parse_result = json.parse(json_string) + if parse_result != OK: + print("JSON解析失败: ", json.error_string) + return false + + var loaded_configs = json.data + if typeof(loaded_configs) != TYPE_DICTIONARY: + print("JSON格式错误,期望字典类型") + return false + + # 清空现有配置并加载新配置 + pet_configs.clear() + + # 遍历加载的配置 + for pet_key in loaded_configs.keys(): + var config = loaded_configs[pet_key] + if typeof(config) != TYPE_DICTIONARY: + print("跳过无效的宠物配置: ", pet_key) + continue + + # 处理element_type字符串转换为枚举 + if config.has("element_type") and typeof(config["element_type"]) == TYPE_STRING: + config["element_type"] = string_to_element_type(config["element_type"]) + + # 添加到配置字典 + pet_configs[pet_key] = config + + print("成功从JSON加载了 ", pet_configs.size(), " 个宠物配置") + return true + +# 导出配置到JSON的函数 +func export_configs_to_json() -> String: + """将宠物配置导出为JSON字符串,用于保存到MongoDB""" + return JSON.stringify(pet_configs) diff --git a/Scene/NewPet/PetConfig.gd.uid b/Scene/NewPet/PetConfig.gd.uid new file mode 100644 index 0000000..9d205cf --- /dev/null +++ b/Scene/NewPet/PetConfig.gd.uid @@ -0,0 +1 @@ +uid://l31ap5jcuyfl diff --git a/Scene/NewPet/PetType/big_beetle.tscn b/Scene/NewPet/PetType/big_beetle.tscn new file mode 100644 index 0000000..c9270d9 --- /dev/null +++ b/Scene/NewPet/PetType/big_beetle.tscn @@ -0,0 +1,68 @@ +[gd_scene load_steps=8 format=3 uid="uid://ba85asiwug57i"] + +[ext_resource type="Texture2D" uid="uid://lx0l12qrituk" path="res://assets/宠物图片/一堆小怪.png" id="1_4f76q"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_vbyii"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_op7i3"] +atlas = ExtResource("1_4f76q") +region = Rect2(72, 48, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_c36rm"] +atlas = ExtResource("1_4f76q") +region = Rect2(120, 48, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_mjdfm"] +atlas = ExtResource("1_4f76q") +region = Rect2(72, 48, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_q454c"] +atlas = ExtResource("1_4f76q") +region = Rect2(96, 48, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_b73qu"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_op7i3") +}], +"loop": true, +"name": &"idle", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_c36rm") +}], +"loop": true, +"name": &"sleep", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_mjdfm") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_q454c") +}], +"loop": true, +"name": &"walk", +"speed": 5.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_b73qu") +animation = &"walk" +autoplay = "walk" + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_vbyii") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_vbyii") +flip_h = true diff --git a/Scene/NewPet/PetType/flying_bird.tscn b/Scene/NewPet/PetType/flying_bird.tscn new file mode 100644 index 0000000..e90fc1b --- /dev/null +++ b/Scene/NewPet/PetType/flying_bird.tscn @@ -0,0 +1,64 @@ +[gd_scene load_steps=8 format=3 uid="uid://bpkq40vvq3cxy"] + +[ext_resource type="Texture2D" uid="uid://lx0l12qrituk" path="res://assets/宠物图片/一堆小怪.png" id="1_lxn61"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_wrr70"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_lgi35"] +atlas = ExtResource("1_lxn61") +region = Rect2(192, 48, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_wn6km"] +atlas = ExtResource("1_lxn61") +region = Rect2(144, 48, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_qmpjj"] +atlas = ExtResource("1_lxn61") +region = Rect2(168, 48, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_1mpkc"] +atlas = ExtResource("1_lxn61") +region = Rect2(192, 48, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_b73qu"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_lgi35") +}], +"loop": true, +"name": &"idle", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_wn6km") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_qmpjj") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_1mpkc") +}], +"loop": true, +"name": &"walk", +"speed": 10.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_b73qu") +animation = &"walk" +autoplay = "walk" +frame_progress = 0.111287 + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_wrr70") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_wrr70") +flip_h = true diff --git a/Scene/NewPet/PetType/green_slime.tscn b/Scene/NewPet/PetType/green_slime.tscn new file mode 100644 index 0000000..ea465f5 --- /dev/null +++ b/Scene/NewPet/PetType/green_slime.tscn @@ -0,0 +1,103 @@ +[gd_scene load_steps=13 format=3 uid="uid://dvkix032ikul3"] + +[ext_resource type="Texture2D" uid="uid://b75oytao5cgjo" path="res://assets/宠物图片/绿色史莱姆.png" id="1_m1ura"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_58kah"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_ou315"] +atlas = ExtResource("1_m1ura") +region = Rect2(0, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_saxlb"] +atlas = ExtResource("1_m1ura") +region = Rect2(72, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_bxslx"] +atlas = ExtResource("1_m1ura") +region = Rect2(48, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_dvhl1"] +atlas = ExtResource("1_m1ura") +region = Rect2(24, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_0t1ns"] +atlas = ExtResource("1_m1ura") +region = Rect2(0, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_sbjn0"] +atlas = ExtResource("1_m1ura") +region = Rect2(72, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_qvnbx"] +atlas = ExtResource("1_m1ura") +region = Rect2(48, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_n0kjo"] +atlas = ExtResource("1_m1ura") +region = Rect2(24, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_obu0n"] +atlas = ExtResource("1_m1ura") +region = Rect2(0, 24, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_yhcbw"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_ou315") +}], +"loop": true, +"name": &"idle", +"speed": 8.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_saxlb") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_bxslx") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_dvhl1") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_0t1ns") +}], +"loop": false, +"name": &"wake", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_sbjn0") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_qvnbx") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_n0kjo") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_obu0n") +}], +"loop": true, +"name": &"walk", +"speed": 8.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_yhcbw") +animation = &"idle" +autoplay = "walk" + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_58kah") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_58kah") +flip_h = true diff --git a/Scene/NewPet/PetType/little_knight.tscn b/Scene/NewPet/PetType/little_knight.tscn new file mode 100644 index 0000000..290628d --- /dev/null +++ b/Scene/NewPet/PetType/little_knight.tscn @@ -0,0 +1,176 @@ +[gd_scene load_steps=24 format=3 uid="uid://dwdoine3llw30"] + +[ext_resource type="Texture2D" uid="uid://bal78ts2eq4yu" path="res://assets/宠物图片/护卫.png" id="1_4imwo"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_ywvjf"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_rjt8u"] +atlas = ExtResource("1_4imwo") +region = Rect2(224, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_81r1q"] +atlas = ExtResource("1_4imwo") +region = Rect2(192, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_nliwy"] +atlas = ExtResource("1_4imwo") +region = Rect2(160, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_kbr5d"] +atlas = ExtResource("1_4imwo") +region = Rect2(128, 0, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_yhrjc"] +atlas = ExtResource("1_4imwo") +region = Rect2(224, 64, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_2dbgw"] +atlas = ExtResource("1_4imwo") +region = Rect2(192, 64, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_h0n46"] +atlas = ExtResource("1_4imwo") +region = Rect2(160, 64, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_1tk7r"] +atlas = ExtResource("1_4imwo") +region = Rect2(128, 64, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ogy1e"] +atlas = ExtResource("1_4imwo") +region = Rect2(96, 64, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_mygjj"] +atlas = ExtResource("1_4imwo") +region = Rect2(64, 64, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_rkj0j"] +atlas = ExtResource("1_4imwo") +region = Rect2(32, 64, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_j7anc"] +atlas = ExtResource("1_4imwo") +region = Rect2(0, 64, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_cxgnv"] +atlas = ExtResource("1_4imwo") +region = Rect2(224, 96, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_2itrd"] +atlas = ExtResource("1_4imwo") +region = Rect2(192, 96, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_c7ofb"] +atlas = ExtResource("1_4imwo") +region = Rect2(160, 96, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_aa4e4"] +atlas = ExtResource("1_4imwo") +region = Rect2(128, 96, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jx5oo"] +atlas = ExtResource("1_4imwo") +region = Rect2(96, 96, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_umo24"] +atlas = ExtResource("1_4imwo") +region = Rect2(64, 96, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_853s8"] +atlas = ExtResource("1_4imwo") +region = Rect2(32, 96, 32, 32) + +[sub_resource type="AtlasTexture" id="AtlasTexture_6x5xm"] +atlas = ExtResource("1_4imwo") +region = Rect2(0, 96, 32, 32) + +[sub_resource type="SpriteFrames" id="SpriteFrames_yhcbw"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_rjt8u") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_81r1q") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_nliwy") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_kbr5d") +}], +"loop": true, +"name": &"idle", +"speed": 8.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_yhrjc") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_2dbgw") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_h0n46") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_1tk7r") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ogy1e") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_mygjj") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_rkj0j") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_j7anc") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_cxgnv") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_2itrd") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_c7ofb") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_aa4e4") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_jx5oo") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_umo24") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_853s8") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_6x5xm") +}], +"loop": true, +"name": &"walk", +"speed": 8.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_yhcbw") +animation = &"walk" +autoplay = "walk" +frame_progress = 0.272604 + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_ywvjf") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_ywvjf") +flip_h = true diff --git a/Scene/NewPet/PetType/red_slime.tscn b/Scene/NewPet/PetType/red_slime.tscn new file mode 100644 index 0000000..7ecaa4b --- /dev/null +++ b/Scene/NewPet/PetType/red_slime.tscn @@ -0,0 +1,104 @@ +[gd_scene load_steps=13 format=3 uid="uid://c8siga6au2vqh"] + +[ext_resource type="Texture2D" uid="uid://cvpsjlje7q3to" path="res://assets/宠物图片/红色史莱姆.png" id="1_2d2gf"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_ni2i3"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_s7ip1"] +atlas = ExtResource("1_2d2gf") +region = Rect2(0, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ff7pm"] +atlas = ExtResource("1_2d2gf") +region = Rect2(72, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_53j2r"] +atlas = ExtResource("1_2d2gf") +region = Rect2(48, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_0ij01"] +atlas = ExtResource("1_2d2gf") +region = Rect2(24, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_20513"] +atlas = ExtResource("1_2d2gf") +region = Rect2(0, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_3f8fr"] +atlas = ExtResource("1_2d2gf") +region = Rect2(72, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_kbexh"] +atlas = ExtResource("1_2d2gf") +region = Rect2(48, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_njkpw"] +atlas = ExtResource("1_2d2gf") +region = Rect2(24, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_xbspe"] +atlas = ExtResource("1_2d2gf") +region = Rect2(0, 24, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_yhcbw"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_s7ip1") +}], +"loop": true, +"name": &"idle", +"speed": 8.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_ff7pm") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_53j2r") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_0ij01") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_20513") +}], +"loop": false, +"name": &"wake", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_3f8fr") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_kbexh") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_njkpw") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_xbspe") +}], +"loop": true, +"name": &"walk", +"speed": 8.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_yhcbw") +animation = &"walk" +autoplay = "walk" +frame_progress = 0.528863 + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_ni2i3") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_ni2i3") +flip_h = true diff --git a/Scene/NewPet/PetType/small_beetle.tscn b/Scene/NewPet/PetType/small_beetle.tscn new file mode 100644 index 0000000..486d8ad --- /dev/null +++ b/Scene/NewPet/PetType/small_beetle.tscn @@ -0,0 +1,64 @@ +[gd_scene load_steps=7 format=3 uid="uid://bk5di5uq6bo04"] + +[ext_resource type="Texture2D" uid="uid://lx0l12qrituk" path="res://assets/宠物图片/一堆小怪.png" id="1_rph6q"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_bks4n"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_nswws"] +atlas = ExtResource("1_rph6q") +region = Rect2(0, 48, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_mjdfm"] +atlas = ExtResource("1_rph6q") +region = Rect2(48, 48, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_1eo38"] +atlas = ExtResource("1_rph6q") +region = Rect2(24, 48, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_b73qu"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_nswws") +}], +"loop": true, +"name": &"idle", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_mjdfm") +}], +"loop": true, +"name": &"sleep", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_nswws") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_1eo38") +}], +"loop": true, +"name": &"walk", +"speed": 5.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_b73qu") +animation = &"walk" +autoplay = "walk" + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_bks4n") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_bks4n") +flip_h = true diff --git a/Scene/NewPet/PetType/small_blue.tscn b/Scene/NewPet/PetType/small_blue.tscn new file mode 100644 index 0000000..dec62f0 --- /dev/null +++ b/Scene/NewPet/PetType/small_blue.tscn @@ -0,0 +1,52 @@ +[gd_scene load_steps=6 format=3 uid="uid://d1sj6wl3mfpo3"] + +[ext_resource type="Texture2D" uid="uid://lx0l12qrituk" path="res://assets/宠物图片/一堆小怪.png" id="1_6fty5"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_4lqxb"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_stamd"] +atlas = ExtResource("1_6fty5") +region = Rect2(48, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_v0b4v"] +atlas = ExtResource("1_6fty5") +region = Rect2(72, 0, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_b2ss3"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_stamd") +}], +"loop": true, +"name": &"idle", +"speed": 8.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_stamd") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_v0b4v") +}], +"loop": true, +"name": &"walk", +"speed": 8.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_b2ss3") +animation = &"walk" +autoplay = "walk" + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_4lqxb") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_4lqxb") +flip_h = true diff --git a/Scene/NewPet/PetType/small_drill_bit.tscn b/Scene/NewPet/PetType/small_drill_bit.tscn new file mode 100644 index 0000000..a037abc --- /dev/null +++ b/Scene/NewPet/PetType/small_drill_bit.tscn @@ -0,0 +1,64 @@ +[gd_scene load_steps=7 format=3 uid="uid://dkvo63yforem7"] + +[ext_resource type="Texture2D" uid="uid://lx0l12qrituk" path="res://assets/宠物图片/一堆小怪.png" id="1_x37sh"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_w6fwq"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_j2fq3"] +atlas = ExtResource("1_x37sh") +region = Rect2(144, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_6q1oc"] +atlas = ExtResource("1_x37sh") +region = Rect2(192, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_xvdwk"] +atlas = ExtResource("1_x37sh") +region = Rect2(168, 24, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_6q1oc"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_j2fq3") +}], +"loop": true, +"name": &"idle", +"speed": 8.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_6q1oc") +}], +"loop": true, +"name": &"sleep", +"speed": 8.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_j2fq3") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_xvdwk") +}], +"loop": true, +"name": &"walk", +"speed": 8.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_6q1oc") +animation = &"walk" +autoplay = "walk" + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_w6fwq") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_w6fwq") +flip_h = true diff --git a/Scene/NewPet/PetType/small_green.tscn b/Scene/NewPet/PetType/small_green.tscn new file mode 100644 index 0000000..a9315c3 --- /dev/null +++ b/Scene/NewPet/PetType/small_green.tscn @@ -0,0 +1,56 @@ +[gd_scene load_steps=7 format=3 uid="uid://bpsoc04xlvhqa"] + +[ext_resource type="Texture2D" uid="uid://lx0l12qrituk" path="res://assets/宠物图片/一堆小怪.png" id="1_i7yg5"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_wo7be"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_5gxwu"] +atlas = ExtResource("1_i7yg5") +region = Rect2(0, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_pxsqn"] +atlas = ExtResource("1_i7yg5") +region = Rect2(0, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_xxlll"] +atlas = ExtResource("1_i7yg5") +region = Rect2(24, 0, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_k25pl"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_5gxwu") +}], +"loop": true, +"name": &"idle", +"speed": 8.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_pxsqn") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_xxlll") +}], +"loop": true, +"name": &"walk", +"speed": 8.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_k25pl") +animation = &"idle" +autoplay = "walk" + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_wo7be") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_wo7be") +flip_h = true diff --git a/Scene/NewPet/PetType/small_orange.tscn b/Scene/NewPet/PetType/small_orange.tscn new file mode 100644 index 0000000..25e64c9 --- /dev/null +++ b/Scene/NewPet/PetType/small_orange.tscn @@ -0,0 +1,52 @@ +[gd_scene load_steps=6 format=3 uid="uid://bk7wkksxa7150"] + +[ext_resource type="Texture2D" uid="uid://lx0l12qrituk" path="res://assets/宠物图片/一堆小怪.png" id="1_4x5tv"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_iom1h"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_tdtxh"] +atlas = ExtResource("1_4x5tv") +region = Rect2(0, 24, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_5rxf3"] +atlas = ExtResource("1_4x5tv") +region = Rect2(24, 24, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_ujsmd"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_tdtxh") +}], +"loop": true, +"name": &"idle", +"speed": 8.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_tdtxh") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_5rxf3") +}], +"loop": true, +"name": &"walk", +"speed": 8.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_ujsmd") +animation = &"walk" +autoplay = "walk" + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_iom1h") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_iom1h") +flip_h = true diff --git a/Scene/NewPet/PetType/small_pink.tscn b/Scene/NewPet/PetType/small_pink.tscn new file mode 100644 index 0000000..f9237c7 --- /dev/null +++ b/Scene/NewPet/PetType/small_pink.tscn @@ -0,0 +1,56 @@ +[gd_scene load_steps=7 format=3 uid="uid://cxj61dijvapdt"] + +[ext_resource type="Texture2D" uid="uid://lx0l12qrituk" path="res://assets/宠物图片/一堆小怪.png" id="1_wkxhn"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_xic1v"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_cxnqb"] +atlas = ExtResource("1_wkxhn") +region = Rect2(96, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_auciw"] +atlas = ExtResource("1_wkxhn") +region = Rect2(96, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_nrp4g"] +atlas = ExtResource("1_wkxhn") +region = Rect2(120, 0, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_b2ss3"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_cxnqb") +}], +"loop": true, +"name": &"idle", +"speed": 8.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_auciw") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_nrp4g") +}], +"loop": true, +"name": &"walk", +"speed": 8.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_b2ss3") +animation = &"idle" +autoplay = "walk" + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_xic1v") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_xic1v") +flip_h = true diff --git a/Scene/NewPet/PetType/small_yellow.tscn b/Scene/NewPet/PetType/small_yellow.tscn new file mode 100644 index 0000000..02d0c73 --- /dev/null +++ b/Scene/NewPet/PetType/small_yellow.tscn @@ -0,0 +1,52 @@ +[gd_scene load_steps=6 format=3 uid="uid://is5klrhiktg4"] + +[ext_resource type="Texture2D" uid="uid://lx0l12qrituk" path="res://assets/宠物图片/一堆小怪.png" id="1_qrx6w"] +[ext_resource type="Texture2D" uid="uid://dciakkwnchcga" path="res://assets/我的世界图片/武器工具/木剑.png" id="2_dkex0"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_trhvc"] +atlas = ExtResource("1_qrx6w") +region = Rect2(144, 0, 24, 24) + +[sub_resource type="AtlasTexture" id="AtlasTexture_k5jn7"] +atlas = ExtResource("1_qrx6w") +region = Rect2(168, 0, 24, 24) + +[sub_resource type="SpriteFrames" id="SpriteFrames_yhcbw"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_trhvc") +}], +"loop": true, +"name": &"idle", +"speed": 5.0 +}, { +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_trhvc") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_k5jn7") +}], +"loop": true, +"name": &"walk", +"speed": 8.0 +}] + +[node name="PetImage" type="AnimatedSprite2D"] +scale = Vector2(4, 4) +sprite_frames = SubResource("SpriteFrames_yhcbw") +animation = &"walk" +autoplay = "walk" + +[node name="LeftToolImage" type="Sprite2D" parent="."] +z_index = 5 +position = Vector2(-10.5, 3) +texture = ExtResource("2_dkex0") +flip_h = true + +[node name="RightToolImage" type="Sprite2D" parent="."] +show_behind_parent = true +position = Vector2(-7.5, -6.25) +texture = ExtResource("2_dkex0") +flip_h = true diff --git a/Scene/NewPet/Pet_bag.json b/Scene/NewPet/Pet_bag.json new file mode 100644 index 0000000..b6abc43 --- /dev/null +++ b/Scene/NewPet/Pet_bag.json @@ -0,0 +1,81 @@ +{ +"宠物仓库":{ + "烈焰鸟": { + "pet_name": "树萌芽の烈焰鸟", + "pet_image":"res://Scene/NewPet/PetType/flying_bird.tscn", + "pet_id": "wea1212w12", + "pet_type": "烈焰鸟", + "pet_level": 1, + "pet_experience": 500, + "pet_temperament": "勇猛", + "pet_birthday": "2025-07-25", + "pet_hobby": "喜欢战斗和烈火", + "pet_introduction": "我爱吃虫子", + "max_health": 300.0, + "enable_health_regen": true, + "health_regen": 2.0, + "enable_shield_regen": true, + "max_shield": 150.0, + "shield_regen": 1.5, + "max_armor": 120.0, + "base_attack_damage": 40.0, + "crit_rate": 0.15, + "crit_damage": 2.0, + "armor_penetration": 10.0, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 2.0, + "enable_berserker_skill": true, + "berserker_bonus": 1.8, + "berserker_duration": 6.0, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": false, + "enable_death_respawn_skill": true, + "respawn_health_percentage": 0.4, + "move_speed": 180.0, + "dodge_rate": 0.08, + "element_type": "FIRE", + "element_damage_bonus": 75.0, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "大蓝虫": { + "pet_name": "树萌芽の大蓝虫", + "pet_image":"res://Scene/NewPet/PetType/big_beetle.tscn", + "pet_id": "dlc123123", + "pet_type": "大甲壳虫", + "pet_level": 8, + "pet_experience": 320, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和小甲壳虫玩", + "pet_introduction": "我是大蓝虫,不是大懒虫!", + "max_health": 180.0, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200.0, + "shield_regen": 2.5, + "max_armor": 80.0, + "base_attack_damage": 35.0, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15.0, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120.0, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100.0, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + } + }, +"巡逻宠物":["wea1212w12"], +"出战宠物":["dlc123123"] +} \ No newline at end of file diff --git a/Scene/NewPet/Pet_data.json b/Scene/NewPet/Pet_data.json new file mode 100644 index 0000000..e9dbd91 --- /dev/null +++ b/Scene/NewPet/Pet_data.json @@ -0,0 +1,165 @@ +{ + "_id": { + "$oid": "687cf59b8e77ba00a7414bab" + }, + "updated_at": { + "$date": "2025-07-20T22:13:38.521Z" + }, + "烈焰鸟": { + "pet_name": "树萌芽の烈焰鸟", + "can_purchase": true, + "cost": 1000, + "pet_image": "res://Scene/NewPet/PetType/flying_bird.tscn", + "pet_id": "0001", + "pet_type": "烈焰鸟", + "pet_level": 1, + "pet_experience": 500, + "pet_temperament": "勇猛", + "pet_birthday": "2023-03-15", + "pet_hobby": "喜欢战斗和烈火", + "pet_introduction": "我爱吃虫子", + "max_health": 300, + "enable_health_regen": true, + "health_regen": 2, + "enable_shield_regen": true, + "max_shield": 150, + "shield_regen": 1.5, + "max_armor": 120, + "base_attack_damage": 40, + "crit_rate": 0.15, + "crit_damage": 2, + "armor_penetration": 10, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 2, + "enable_berserker_skill": true, + "berserker_bonus": 1.8, + "berserker_duration": 6, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": false, + "enable_death_respawn_skill": true, + "respawn_health_percentage": 0.4, + "move_speed": 180, + "dodge_rate": 0.08, + "element_type": "FIRE", + "element_damage_bonus": 75, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "大蓝虫": { + "pet_name": "树萌芽の大蓝虫", + "can_purchase": true, + "cost": 1000, + "pet_image": "res://Scene/NewPet/PetType/big_beetle.tscn", + "pet_id": "0002", + "pet_type": "大蓝虫", + "pet_level": 8, + "pet_experience": 320, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和小甲壳虫玩", + "pet_introduction": "我是大蓝虫,不是大懒虫!", + "max_health": 180, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200, + "shield_regen": 2.5, + "max_armor": 80, + "base_attack_damage": 35, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "小蓝虫": { + "pet_name": "树萌芽の小蓝虫", + "can_purchase": true, + "cost": 1000, + "pet_image": "res://Scene/NewPet/PetType/small_beetle.tscn", + "pet_id": "0002", + "pet_type": "小蓝虫", + "pet_level": 1, + "pet_experience": 0, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和大蓝虫玩", + "pet_introduction": "我是小蓝虫,不是小懒虫!", + "max_health": 90, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200, + "shield_regen": 2.5, + "max_armor": 80, + "base_attack_damage": 35, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + }, + "小蓝": { + "pet_name": "树萌芽の小蓝", + "can_purchase": true, + "cost": 1000, + "pet_image": "res://Scene/NewPet/PetType/small_blue.tscn", + "pet_id": "0002", + "pet_type": "小蓝", + "pet_level": 1, + "pet_experience": 0, + "pet_temperament": "冷静", + "pet_birthday": "2023-06-20", + "pet_hobby": "喜欢和小黄一起玩", + "pet_introduction": "我是小黄!", + "max_health": 120, + "enable_health_regen": true, + "health_regen": 1.2, + "enable_shield_regen": true, + "max_shield": 200, + "shield_regen": 2.5, + "max_armor": 80, + "base_attack_damage": 35, + "crit_rate": 0.12, + "crit_damage": 1.8, + "armor_penetration": 15, + "enable_multi_projectile_skill": true, + "multi_projectile_delay": 1.5, + "enable_berserker_skill": false, + "enable_self_destruct_skill": false, + "enable_summon_pet_skill": true, + "summon_count": 2, + "summon_scale": 0.15, + "enable_death_respawn_skill": false, + "move_speed": 120, + "dodge_rate": 0.12, + "element_type": "WATER", + "element_damage_bonus": 100, + "left_weapon": "钻石剑", + "right_weapon": "钻石剑" + } +} \ No newline at end of file diff --git a/Scene/NewPet/WeaponBase.gd b/Scene/NewPet/WeaponBase.gd new file mode 100644 index 0000000..f1f5677 --- /dev/null +++ b/Scene/NewPet/WeaponBase.gd @@ -0,0 +1,128 @@ +extends Node +class_name WeaponBase + +#武器系统 +var weapon_data = { + "钻石剑": { + "icon": 'res://assets/我的世界图片/武器工具/钻石剑.png', + "function": "apply_diamond_sword_effect" + }, + "铁剑": { + "icon": 'res://assets/我的世界图片/武器工具/铁剑.png', + "function": "apply_iron_sword_effect" + }, + "钻石斧": { + "icon": 'res://assets/我的世界图片/武器工具/钻石斧.png', + "function": "apply_diamond_axe_effect" + }, + "铁镐": { + "icon": 'res://assets/我的世界图片/武器工具/铁镐.png', + "function": "apply_iron_pickaxe_effect" + } +} + +# 武器效果函数 - 每种武器单独一个函数 + +#================钻石剑效果======================== +# 钻石剑效果 +func apply_diamond_sword_effect(pet): + pet.base_attack_damage += 15.0 + pet.crit_rate += 0.1 + pet.attack_speed += 0.2 + # 钻石剑效果已应用 + +# 移除钻石剑效果 +func remove_diamond_sword_effect(pet): + pet.base_attack_damage -= 15.0 + pet.crit_rate -= 0.1 + pet.attack_speed -= 0.2 + # 钻石剑效果已移除 + +#================钻石剑效果======================== + +#================铁剑效果======================== +# 铁剑效果 +func apply_iron_sword_effect(pet): + pet.base_attack_damage += 10.0 + pet.crit_rate += 0.05 + pet.attack_speed += 0.1 + # 铁剑效果已应用 + +# 移除铁剑效果 +func remove_iron_sword_effect(pet): + pet.base_attack_damage -= 10.0 + pet.crit_rate -= 0.05 + pet.attack_speed -= 0.1 + # 铁剑效果已移除 +#================铁剑效果======================== + + +#================钻石斧效果======================== +# 钻石斧效果 +func apply_diamond_axe_effect(pet): + pet.base_attack_damage += 20.0 + pet.armor_penetration += 0.2 + pet.knockback_force += 100.0 + # 钻石斧效果已应用 + +# 移除钻石斧效果 +func remove_diamond_axe_effect(pet): + pet.base_attack_damage -= 20.0 + pet.armor_penetration -= 0.2 + pet.knockback_force -= 100.0 + # 钻石斧效果已移除 +#================钻石斧效果======================== + + +#================铁镐效果======================== +# 铁镐效果 +func apply_iron_pickaxe_effect(pet): + pet.base_attack_damage += 8.0 + pet.armor_penetration += 0.3 + pet.attack_range += 20.0 + # 铁镐效果已应用 + +# 移除铁镐效果 +func remove_iron_pickaxe_effect(pet): + pet.base_attack_damage -= 8.0 + pet.armor_penetration -= 0.3 + pet.attack_range -= 20.0 + # 铁镐效果已移除 +#================铁镐效果======================== + + +#======================武器系统通用函数========================== +# 应用武器效果的主函数 +func apply_weapon_effect(pet, weapon_name: String): + if not weapon_data.has(weapon_name): + return + + var weapon = weapon_data[weapon_name] + var function_name = weapon.get("function", "") + + if function_name != "": + call(function_name, pet) + +# 移除武器效果的函数 +func remove_weapon_effect(pet, weapon_name: String): + if not weapon_data.has(weapon_name): + return + + var weapon = weapon_data[weapon_name] + var function_name = weapon.get("function", "") + + if function_name != "": + # 将apply替换为remove来调用移除函数 + var remove_function_name = function_name.replace("apply_", "remove_") + call(remove_function_name, pet) + +# 获取武器图标路径 +func get_weapon_icon(weapon_name: String) -> String: + if weapon_data.has(weapon_name): + return weapon_data[weapon_name].get("icon", "") + return "" + +# 获取所有武器名称列表 +func get_all_weapon_names() -> Array: + return weapon_data.keys() +#======================武器系统通用函数========================== diff --git a/Scene/NewPet/WeaponBase.gd.uid b/Scene/NewPet/WeaponBase.gd.uid new file mode 100644 index 0000000..5de56c8 --- /dev/null +++ b/Scene/NewPet/WeaponBase.gd.uid @@ -0,0 +1 @@ +uid://bpa6hp1mm6sj1 diff --git a/Script/BigPanel/ItemBagPanel.gd b/Script/BigPanel/ItemBagPanel.gd index 5a81cb8..aa9f79f 100644 --- a/Script/BigPanel/ItemBagPanel.gd +++ b/Script/BigPanel/ItemBagPanel.gd @@ -293,7 +293,7 @@ func _show_item_info(item_name: String, item_count: int): info_text += "描述: " + description if not _is_item_usable(item_name): - info_text += "\n注意: 此道具功能暂未实现" + pass else: info_text = item_name + " (数量: " + str(item_count) + ")\n描述: 暂无信息" @@ -363,7 +363,7 @@ func _send_farm_item_use_request(item_name: String): # 发送请求 tcp_network_manager_panel.send_message(message) - Toast.show("正在使用道具...", Color.BLUE, 2.0, 1.0) + #Toast.show("正在使用道具...", Color.BLUE, 2.0, 1.0) # 显示宠物使用道具确认对话框 func _show_pet_item_confirmation_dialog(item_name: String, item_count: int): @@ -431,13 +431,12 @@ func _send_pet_item_use_request(item_name: String, pet_id: String): # 退出宠物使用道具模式 _exit_pet_item_mode() - Toast.show("正在使用道具...", Color.BLUE, 2.0, 1.0) + #Toast.show("正在使用道具...", Color.BLUE, 2.0, 1.0) # 退出宠物使用道具模式 func _exit_pet_item_mode(): is_pet_item_mode = false current_pet_data = {} - # 刷新UI update_item_bag_ui() @@ -496,13 +495,12 @@ func _on_quit_button_pressed() -> void: func _on_refresh_button_pressed() -> void: # 刷新道具背包UI update_item_bag_ui() - Toast.show("道具背包已刷新", Color.GREEN, 2.0, 1.0) + #Toast.show("道具背包已刷新", Color.GREEN, 2.0, 1.0) #面板显示与隐藏切换处理 func _on_visibility_changed(): if visible: GlobalVariables.isZoomDisabled = true - # 面板显示时自动刷新数据 update_item_bag_ui() pass else: diff --git a/Script/BigPanel/LoginPanel.gd b/Script/BigPanel/LoginPanel.gd index 492c627..858a5fa 100644 --- a/Script/BigPanel/LoginPanel.gd +++ b/Script/BigPanel/LoginPanel.gd @@ -433,6 +433,7 @@ func _handle_login_success(user_data: Dictionary): main_game.item_bag = user_data.get("道具背包", []) main_game.pet_bag = user_data.get("宠物背包", []) main_game.patrol_pets = user_data.get("巡逻宠物", []) + main_game.battle_pets = user_data.get("出战宠物", []) # 启动游戏并隐藏登录面板 main_game.start_game = true diff --git a/Script/BigPanel/PetBagPanel.gd b/Script/BigPanel/PetBagPanel.gd index e734f1b..1b624af 100644 --- a/Script/BigPanel/PetBagPanel.gd +++ b/Script/BigPanel/PetBagPanel.gd @@ -61,9 +61,9 @@ func update_pet_bag_ui(): # 为背包中的每个宠物创建按钮 for pet_data in main_game.pet_bag: - var pet_name = pet_data.get("基本信息", {}).get("宠物类型", "未知宠物") - var pet_level = pet_data.get("等级经验", {}).get("宠物等级", 1) - var pet_owner_name = pet_data.get("基本信息", {}).get("宠物名称", pet_name) + var pet_name = pet_data.get("pet_type", "未知宠物") + var pet_level = pet_data.get("pet_level", 1) + var pet_owner_name = pet_data.get("pet_name", pet_name) # 创建宠物按钮 var button = _create_pet_button(pet_name, pet_level, pet_owner_name) @@ -118,31 +118,45 @@ func _update_button_pet_image(button: Button, pet_name: String): # 检查按钮是否有CropImage节点 var pet_image = button.get_node_or_null("CropImage") if not pet_image: + print("宠物背包按钮没有找到CropImage节点:", button.name) return - # 从宠物配置获取场景路径 + # 从服务器的宠物配置获取场景路径 var texture = null - var pet_config = _load_pet_config() + var pet_config = main_game.pet_config # 使用服务器返回的宠物配置 if pet_config.has(pet_name): var pet_info = pet_config[pet_name] - var scene_path = pet_info.get("场景路径", "") + var scene_path = pet_info.get("pet_image", "") # 使用服务器数据的pet_image字段 + print("宠物背包 ", pet_name, " 的图片路径:", scene_path) if scene_path != "" and ResourceLoader.exists(scene_path): + print("宠物背包开始加载宠物场景:", scene_path) # 加载宠物场景并获取PetImage的纹理 var pet_scene = load(scene_path) if pet_scene: var pet_instance = pet_scene.instantiate() - var pet_image_node = pet_instance.get_node_or_null("PetImage") - if pet_image_node and pet_image_node.sprite_frames: + # 直接使用实例化的场景根节点,因为根节点就是PetImage + if pet_instance and pet_instance.sprite_frames: # 获取默认动画的第一帧 - var animation_names = pet_image_node.sprite_frames.get_animation_names() + var animation_names = pet_instance.sprite_frames.get_animation_names() if animation_names.size() > 0: var default_animation = animation_names[0] - var frame_count = pet_image_node.sprite_frames.get_frame_count(default_animation) + var frame_count = pet_instance.sprite_frames.get_frame_count(default_animation) if frame_count > 0: - texture = pet_image_node.sprite_frames.get_frame_texture(default_animation, 0) + texture = pet_instance.sprite_frames.get_frame_texture(default_animation, 0) + print("宠物背包成功获取宠物纹理:", pet_name) + else: + print("宠物背包场景没有动画:", pet_name) + else: + print("宠物背包场景没有PetImage节点或sprite_frames:", pet_name) pet_instance.queue_free() + else: + print("宠物背包无法加载宠物场景:", scene_path) + else: + print("宠物背包图片路径无效或文件不存在:", scene_path) + else: + print("宠物背包配置中没有找到:", pet_name) # 设置图片 if texture: @@ -151,8 +165,10 @@ func _update_button_pet_image(button: Button, pet_name: String): pet_image.scale = Vector2(10, 10) # 确保图片居中显示 pet_image.centered = true + print("宠物背包成功设置宠物图片:", pet_name) else: pet_image.visible = false + print("宠物背包无法获取宠物图片:", pet_name) # 加载宠物配置数据 func _load_pet_config() -> Dictionary: diff --git a/Script/BigPanel/PetStorePanel.gd b/Script/BigPanel/PetStorePanel.gd index c9b2c58..e4ce44d 100644 --- a/Script/BigPanel/PetStorePanel.gd +++ b/Script/BigPanel/PetStorePanel.gd @@ -24,6 +24,8 @@ extends Panel # 宠物配置数据 var pet_config: Dictionary = {} +# 请求状态标志,防止重复请求 +var is_requesting_config: bool = false # 准备函数 func _ready(): @@ -57,20 +59,23 @@ func update_pet_store_ui(): child.queue_free() print("更新宠物商店UI,宠物种类:", pet_config.size()) + print("宠物配置数据:", pet_config) # 为每个宠物配置创建按钮 for pet_name in pet_config.keys(): var pet_info = pet_config[pet_name] - var purchase_info = pet_info.get("购买信息", {}) - var can_buy = purchase_info.get("能否购买", false) + print("处理宠物:", pet_name, ",数据:", pet_info) + + # 适配扁平化数据格式 + var can_buy = pet_info.get("can_purchase", false) # 只显示可购买的宠物 if not can_buy: + print("宠物 ", pet_name, " 不可购买,跳过") continue - var pet_cost = purchase_info.get("购买价格", 0) - var basic_info = pet_info.get("基本信息", {}) - var pet_desc = basic_info.get("简介", "可爱的宠物伙伴") + var pet_cost = pet_info.get("cost", 0) + var pet_desc = pet_info.get("description", "可爱的宠物伙伴") # 检查玩家是否已购买该宠物 var is_owned = _check_pet_owned(pet_name) @@ -88,6 +93,7 @@ func update_pet_store_ui(): button.pressed.connect(func(): _on_store_pet_selected(pet_name, pet_cost, pet_desc)) store_grid.add_child(button) + print("已添加宠物按钮:", pet_name) # 检查玩家是否已拥有某种宠物 func _check_pet_owned(pet_name: String) -> bool: @@ -95,8 +101,7 @@ func _check_pet_owned(pet_name: String) -> bool: return false for pet_data in main_game.pet_bag: - var basic_info = pet_data.get("基本信息", {}) - var pet_type = basic_info.get("宠物类型", "") + var pet_type = pet_data.get("pet_type", "") if pet_type == pet_name: return true return false @@ -144,28 +149,44 @@ func _update_button_pet_image(button: Button, pet_name: String): # 检查按钮是否有CropImage节点 var pet_image = button.get_node_or_null("CropImage") if not pet_image: + print("按钮没有CropImage节点,跳过图片设置") return # 从宠物配置获取场景路径 var texture = null if pet_config.has(pet_name): var pet_info = pet_config[pet_name] - var scene_path = pet_info.get("场景路径", "") + var scene_path = pet_info.get("pet_image", "") + print("宠物 ", pet_name, " 的图片路径:", scene_path) if scene_path != "" and ResourceLoader.exists(scene_path): + print("开始加载宠物场景:", scene_path) # 加载宠物场景并获取PetImage的纹理 var pet_scene = load(scene_path) if pet_scene: var pet_instance = pet_scene.instantiate() - var pet_image_node = pet_instance.get_node_or_null("PetImage") + # 场景的根节点就是PetImage,直接使用 + var pet_image_node = pet_instance if pet_image_node and pet_image_node.sprite_frames: # 获取默认动画的第一帧 - var default_animation = pet_image_node.sprite_frames.get_animation_names()[0] - var frame_count = pet_image_node.sprite_frames.get_frame_count(default_animation) - if frame_count > 0: - texture = pet_image_node.sprite_frames.get_frame_texture(default_animation, 0) + var animation_names = pet_image_node.sprite_frames.get_animation_names() + if animation_names.size() > 0: + var default_animation = animation_names[0] + var frame_count = pet_image_node.sprite_frames.get_frame_count(default_animation) + if frame_count > 0: + texture = pet_image_node.sprite_frames.get_frame_texture(default_animation, 0) + print("成功获取宠物纹理:", pet_name) + else: + print("宠物场景没有动画:", pet_name) + else: + print("宠物场景没有PetImage节点或sprite_frames:", pet_name) pet_instance.queue_free() - + else: + print("无法加载宠物场景:", scene_path) + else: + print("宠物图片路径无效或文件不存在:", scene_path) + else: + print("宠物配置中没有找到:", pet_name) # 设置图片 if texture: @@ -174,31 +195,52 @@ func _update_button_pet_image(button: Button, pet_name: String): pet_image.scale = Vector2(10, 10) # 确保图片居中显示 pet_image.centered = true - + print("成功设置宠物图片:", pet_name) else: + # 如果无法获取图片,隐藏图片节点但保留按钮 pet_image.visible = false + print("无法获取宠物图片,隐藏图片节点:", pet_name) -# 从主游戏脚本获取宠物配置数据 +# 从服务器获取MongoDB中的宠物配置数据 func _load_pet_config_from_main(): - # 从宠物数据文件加载配置 - var file = FileAccess.open("res://Data/pet_data.json", FileAccess.READ) - if file == null: - print("宠物商店:无法打开宠物配置文件") - pet_config = {} + # 如果正在请求中,避免重复发送 + if is_requesting_config: + print("宠物商店:正在请求配置数据中,跳过重复请求") return - var json = JSON.new() - var json_string = file.get_as_text() - file.close() - - var parse_result = json.parse(json_string) - if parse_result != OK: - print("宠物商店:解析宠物配置文件失败") + # 发送请求到服务器获取宠物配置 + if tcp_network_manager_panel and tcp_network_manager_panel.has_method("sendGetPetConfig"): + is_requesting_config = true + if tcp_network_manager_panel.sendGetPetConfig(): + print("宠物商店:已发送获取宠物配置请求") + # 等待服务器响应,配置数据将通过网络回调更新 + else: + print("宠物商店:发送获取宠物配置请求失败") + pet_config = {} + is_requesting_config = false + else: + print("宠物商店:网络管理器不可用,无法获取宠物配置") pet_config = {} - return + is_requesting_config = false + +# 处理服务器返回的宠物配置数据 +func _on_pet_config_received(response_data: Dictionary): + """处理从服务器接收到的宠物配置数据""" + # 重置请求状态 + is_requesting_config = false - pet_config = json.data - print("宠物商店:成功加载宠物配置数据,宠物种类:", pet_config.size()) + var success = response_data.get("success", false) + if success: + pet_config = response_data.get("pet_config", {}) + print("宠物商店:成功接收宠物配置数据,宠物种类:", pet_config.size()) + # 只更新UI,不重新发送请求 + update_pet_store_ui() + else: + var error_message = response_data.get("message", "获取宠物配置失败") + print("宠物商店:获取宠物配置失败:", error_message) + pet_config = {} + # 显示错误提示 + Toast.show(error_message, Color.RED, 3.0, 1.0) # 商店宠物点击处理 - 购买宠物 func _on_store_pet_selected(pet_name: String, pet_cost: int, pet_desc: String): @@ -269,9 +311,12 @@ func _send_buy_pet_request(pet_name: String, pet_cost: int): #=========================面板通用处理========================= # 手动刷新宠物商店面板 func _on_refresh_button_pressed() -> void: + # 清空现有配置和请求状态,强制重新获取 + pet_config = {} + is_requesting_config = false # 重新初始化宠物商店 init_pet_store() - Toast.show("宠物商店已刷新", Color.GREEN, 2.0, 1.0) + #Toast.show("宠物商店已刷新", Color.GREEN, 2.0, 1.0) # 关闭宠物商店面板 func _on_quit_button_pressed() -> void: @@ -283,8 +328,12 @@ func _on_quit_button_pressed() -> void: func _on_visibility_changed(): if visible: GlobalVariables.isZoomDisabled = true - # 面板显示时自动刷新数据 - update_pet_store_ui() + # 面板显示时只在没有配置数据时才请求 + if pet_config.is_empty(): + init_pet_store() + else: + # 如果已有配置数据,直接更新UI + update_pet_store_ui() pass else: GlobalVariables.isZoomDisabled = false diff --git a/Script/Pet/PetBase.gd b/Script/Pet/PetBase.gd index 0ccc703..4a01e69 100644 --- a/Script/Pet/PetBase.gd +++ b/Script/Pet/PetBase.gd @@ -53,15 +53,10 @@ extends CharacterBody2D #- 亲密度(额外加属性) #- 品质(白/绿/蓝/橙/红/紫) -#基本攻击方式: #近战 #近战攻击伤害 #近战攻击速度 -#远程 -#远程攻击伤害 -#远程攻击速度 - #附录 #- 护甲公式示例:实际伤害 = 基础伤害 × (1 - 护甲值/(护甲值 + 100)),搭配"护甲穿透"可直接减少目标护甲值 #- 元素克制:火属性攻击对冰属性敌人造成150%伤害,同时被水属性克制(仅造成80%伤害) @@ -111,7 +106,7 @@ var current_armor: float = 100.0 # 当前护甲值 # 攻击属性 var attack_type: AttackType = AttackType.RANGED # 攻击类型 var attack_damage: float = 20.0 # 基础攻击伤害 -var attack_range: float = 300.0 # 攻击距离 +var attack_range: float = 400.0 # 攻击距离 var crit_rate: float = 0.1 # 暴击率(0.0-1.0) var crit_damage: float = 1.5 # 暴击伤害倍数 var life_steal: float = 0.1 # 生命汲取(0.0-1.0) @@ -152,7 +147,7 @@ enum RangedAttackMode { PIERCING # 穿透攻击 } -# 内部状态变量(不需要导出) + var attack_speed: float = 1.0 # 当前攻击速度(根据攻击类型动态设置) var gatling_firing: bool = false # 是否正在加特林射击 var gatling_current_bullet: int = 0 # 当前加特林子弹计数 @@ -219,13 +214,57 @@ var is_attacking: bool = false # 是否正在攻击 var is_berserker: bool = false # 是否处于狂暴状态 var is_stunned: bool = false # 是否被眩晕 var is_invulnerable: bool = false # 是否无敌 -var is_being_knocked_back: bool = false # 是否正在被击退 var current_target: CharacterBody2D = null # 当前目标 var last_attacker: CharacterBody2D = null # 最后攻击者(用于击杀奖励) var last_attack_time: float = 0.0 # 上次攻击时间 var last_regen_time: float = 0.0 # 上次恢复时间 var last_target_check_time: float = 0.0 # 上次目标检查时间 -var knockback_velocity: Vector2 = Vector2.ZERO # 击退速度 + +# 受伤动画相关 +var hurt_tween: Tween = null # 受伤动画缓动 +var original_modulate: Color = Color.WHITE # 原始颜色 +var last_hurt_time: float = 0.0 # 上次受伤时间(防止受伤动画过于频繁) +var hurt_animation_cooldown: float = 0.3 # 受伤动画冷却时间 + +# 攻击频率控制 +var min_attack_interval: float = 0.5 # 最小攻击间隔(防止攻击过于频繁) + +# 伤害反弹保护 +var damage_reflect_depth: int = 0 # 伤害反弹递归深度 +var max_reflect_depth: int = 3 # 最大反弹深度(防止无限递归) + +# 性能保护 +var performance_mode: bool = false # 性能模式(减少特效和计算) +var frame_skip_counter: int = 0 # 帧跳跃计数器 + +# 升级系统 - 基础属性列表(每次升级随机选择加点) +var base_upgrade_attributes: Array[String] = [ + "max_health", # 最大生命值 + "attack_damage", # 攻击伤害 + "move_speed", # 移动速度 + "max_shield", # 最大护盾值 + "max_armor", # 最大护甲值 + "crit_rate", # 暴击率 + "health_regen", # 生命恢复 + "attack_range" # 攻击距离 +] + +# 每次升级随机选择的属性数量 +var attributes_per_level: int = 3 + +# 每5级额外属性奖励表 +var level_milestone_bonuses: Dictionary = { + 5: {"max_health": 20, "attack_damage": 5, "crit_rate": 0.02}, + 10: {"max_shield": 30, "armor_penetration": 5, "life_steal": 0.05}, + 15: {"max_armor": 25, "knockback_resist": 0.1, "dodge_rate": 0.03}, + 20: {"health_regen": 2, "move_speed": 15, "attack_range": 30}, + 25: {"max_health": 40, "attack_damage": 10, "enable_berserker_mode": true}, + 30: {"max_shield": 50, "shield_regen": 1, "enable_damage_reflect": true}, + 35: {"crit_damage": 0.3, "berserker_bonus": 0.2, "damage_reflect": 0.05}, + 40: {"max_armor": 40, "control_resist": 0.15, "enable_aid_system": true}, + 45: {"projectile_speed": 50, "pierce_count": 1, "enable_death_immunity": true}, + 50: {"max_health": 100, "attack_damage": 25, "enable_resurrection": true} +} # 巡逻状态 var is_patrolling: bool = false # 是否正在巡逻 @@ -642,6 +681,10 @@ func _ready(): # 初始化生日 initialize_birthday() + # 保存原始颜色 + if pet_image: + original_modulate = pet_image.modulate + # 延迟初始化UI显示,确保所有节点都已准备好 call_deferred("update_ui") @@ -702,35 +745,50 @@ func clamp_to_battle_area(): global_position.x = clamp(global_position.x, battle_area_min.x, battle_area_max.x) global_position.y = clamp(global_position.y, battle_area_min.y, battle_area_max.y) -#宠物物理更新 +#宠物物理更新(带性能保护) func _physics_process(delta): if not is_alive or is_dying: return + # 性能保护:每3帧执行一次非关键逻辑 + frame_skip_counter += 1 + var should_skip_frame = performance_mode and (frame_skip_counter % 3 != 0) + + # 检测性能问题(如果帧时间过长,自动启用性能模式) + if delta > 0.025: # 帧时间超过25ms(低于40FPS) + if not performance_mode: + performance_mode = true + print("⚡ " + pet_name + " 启用性能模式(帧时间: " + str("%.3f" % delta) + "s)") + # 巡逻宠物特殊处理 if is_patrolling: handle_patrol(delta) return # 处理生命和护盾恢复 - handle_regeneration(delta) + if not should_skip_frame: + handle_regeneration(delta) - # 更新年龄和亲密度 - update_age_and_intimacy(delta) + # 更新年龄和亲密度(低优先级,可跳帧) + if not should_skip_frame: + update_age_and_intimacy(delta) # 检查狂暴状态 - check_berserker_mode() + if not should_skip_frame: + check_berserker_mode() - # 检查援助系统 - check_aid_system() + # 检查援助系统(低优先级,可跳帧) + if not should_skip_frame: + check_aid_system() # 如果被眩晕则不能行动 if is_stunned: return - # 定期检查目标状态(每0.5秒检查一次) + # 定期检查目标状态(性能模式下降低检查频率) var current_time = Time.get_ticks_msec() / 1000.0 - if current_time - last_target_check_time >= 0.5: + var check_interval = 0.5 if not performance_mode else 1.0 + if current_time - last_target_check_time >= check_interval: check_target_validity() last_target_check_time = current_time @@ -791,15 +849,7 @@ func update_ai_state(delta): #宠物移动 func handle_movement(delta): - # 处理击退效果 - if is_being_knocked_back: - velocity = knockback_velocity - # 击退衰减 - knockback_velocity = knockback_velocity.lerp(Vector2.ZERO, 5.0 * delta) - if knockback_velocity.length() < 10.0: - is_being_knocked_back = false - knockback_velocity = Vector2.ZERO - elif current_state == PetState.MOVING_TO_TARGET and current_target: + if current_state == PetState.MOVING_TO_TARGET and current_target: var distance_to_target = global_position.distance_to(current_target.global_position) var direction = (current_target.global_position - global_position).normalized() @@ -832,7 +882,7 @@ func handle_movement(delta): else: velocity = Vector2.ZERO -#宠物攻击 +#宠物攻击(带频率保护) func handle_attack(delta): if current_state == PetState.ATTACKING and current_target: var current_time = Time.get_ticks_msec() / 1000.0 # 转换为秒 @@ -841,8 +891,9 @@ func handle_attack(delta): if ranged_mode == RangedAttackMode.GATLING: handle_gatling_attack(current_time, delta) else: - # 普通攻击频率控制 - if current_time - last_attack_time >= 1.0 / attack_speed: + # 普通攻击频率控制(确保最小攻击间隔) + var attack_interval = max(1.0 / attack_speed, min_attack_interval) + if current_time - last_attack_time >= attack_interval: perform_attack(current_target) last_attack_time = current_time @@ -959,9 +1010,9 @@ func perform_melee_attack(target: CharacterBody2D): var heal_amount = damage * life_steal heal(heal_amount) - # 击退效果 - if knockback_force > 0: - apply_knockback_to_target(target) + # 击退效果已禁用 + # if knockback_force > 0: + # apply_knockback_to_target(target) # 根据攻击模式发射子弹 func fire_projectile_by_mode(target: CharacterBody2D): @@ -1090,15 +1141,29 @@ func create_and_fire_projectile(start_pos: Vector2, target_pos: Vector2, damage: RangedAttackMode.PIERCING: projectile.get_node("ProjectileSprite").modulate = Color.PURPLE -#宠物受到伤害 +#宠物受到伤害(带死循环保护) func take_damage(damage: float, armor_pen: float = 0.0, attacker_element: ElementType = ElementType.NONE, attacker: CharacterBody2D = null): if not is_alive or is_invulnerable: return + # 防止过于频繁的伤害处理(性能保护) + var current_time = Time.get_ticks_msec() / 1000.0 + if current_time - last_attack_time < 0.05: # 50ms最小伤害间隔 + return + + # 增加伤害反弹递归深度 + damage_reflect_depth += 1 + + # 递归深度保护(防止无限反弹) + if damage_reflect_depth > max_reflect_depth: + damage_reflect_depth = max(0, damage_reflect_depth - 1) + return + # 闪避检测 if randf() < dodge_rate: if attacker and is_instance_valid(attacker): add_battle_detail_to_panel("✨ " + pet_name + " 闪避了 " + attacker.pet_name + " 的攻击!", Color.CYAN) + damage_reflect_depth = max(0, damage_reflect_depth - 1) return var actual_damage = damage @@ -1123,8 +1188,11 @@ func take_damage(damage: float, armor_pen: float = 0.0, attacker_element: Elemen if actual_damage > 0: current_health -= actual_damage - # 添加受伤细节 - if attacker and is_instance_valid(attacker): + # 播放受伤动画(带冷却保护) + play_hurt_animation() + + # 添加受伤细节(性能模式下减少文本输出) + if not performance_mode and attacker and is_instance_valid(attacker): var damage_text = "💔 " + pet_name + " 受到 " + str(int(actual_damage)) + " 点伤害" if element_extra_damage > 0: damage_text += " (元素克制 +" + str(int(element_extra_damage)) + ")" @@ -1135,15 +1203,19 @@ func take_damage(damage: float, armor_pen: float = 0.0, attacker_element: Elemen last_attacker = attacker # 反击机制:立即将攻击者设为目标(只有启用战斗时才反击) + # 添加反击冷却,防止过于频繁的目标切换 if combat_enabled and attacker and is_instance_valid(attacker) and attacker.is_alive: if attacker.get_team() != pet_team: # 确保不攻击队友 - current_target = attacker - current_state = PetState.MOVING_TO_TARGET + # 只有当前没有目标或当前目标已死亡时才切换目标 + if not current_target or not is_instance_valid(current_target) or not current_target.is_alive: + current_target = attacker + current_state = PetState.MOVING_TO_TARGET - # 伤害反弹 - if enable_damage_reflect and damage_reflect > 0.0 and attacker and is_instance_valid(attacker): - var reflect_damage = damage * damage_reflect - attacker.take_damage(reflect_damage, 0.0, element_type, self) # 反弹伤害也会触发反击 + # 伤害反弹(带递归深度保护) + if enable_damage_reflect and damage_reflect > 0.0 and attacker and is_instance_valid(attacker) and damage_reflect_depth <= max_reflect_depth: + var reflect_damage = damage * damage_reflect * 0.5 # 反弹伤害减半,防止无限递归 + # 延迟反弹,避免同帧内的递归调用 + call_deferred("apply_reflect_damage", attacker, reflect_damage) # 检查死亡 if current_health <= 0: @@ -1159,9 +1231,17 @@ func take_damage(damage: float, armor_pen: float = 0.0, attacker_element: Elemen if not is_dying: # 防止重复调用die() call_deferred("die") + # 减少伤害反弹递归深度 + damage_reflect_depth = max(0, damage_reflect_depth - 1) + # 更新UI call_deferred("update_ui") +# 延迟应用反弹伤害(防止递归调用) +func apply_reflect_damage(target: CharacterBody2D, reflect_damage: float): + if target and is_instance_valid(target) and target.is_alive: + target.take_damage(reflect_damage, 0.0, element_type, self) + #宠物死亡 func die(): if is_dying: # 如果已经在死亡过程中,直接返回 @@ -1416,6 +1496,10 @@ func apply_quality_bonuses(): func get_team() -> String: return pet_team +# 获取攻击类型(调试用) +func get_attack_type() -> AttackType: + return attack_type + # 处理生命和护盾恢复 func handle_regeneration(delta: float): var current_time = Time.get_ticks_msec() / 1000.0 @@ -1516,7 +1600,7 @@ func gain_experience(amount: float): while pet_experience >= max_experience and pet_level < 50: level_up() -# 升级 +# 升级(新的随机属性系统) func level_up(): pet_experience -= max_experience pet_level += 1 @@ -1524,25 +1608,197 @@ func level_up(): # 计算新的升级经验需求(指数增长) max_experience = 100.0 * pow(1.2, pet_level - 1) - # 升级属性加成 - var level_bonus = 1.1 # 每级10%属性加成 + # 随机选择属性进行升级 + var upgraded_attributes = apply_random_attribute_upgrade() - max_health *= level_bonus - current_health = max_health # 升级回满血 - attack_damage *= level_bonus - max_shield *= level_bonus - current_shield = max_shield # 升级回满护盾 - max_armor *= level_bonus - current_armor = max_armor # 升级回满护甲 + # 检查是否有里程碑奖励(每5级) + var milestone_rewards = apply_milestone_bonus() + + # 升级回血和护盾护甲 + current_health = max_health + current_shield = max_shield + current_armor = max_armor # 升级特效 show_level_up_effect() # 添加升级细节 - add_battle_detail_to_panel("🎉 " + pet_name + " 升级到 " + str(pet_level) + " 级!", Color.GOLD) + var upgrade_text = "🎉 " + pet_name + " 升级到 " + str(pet_level) + " 级!" + upgrade_text += "\n📈 随机提升:" + ", ".join(upgraded_attributes) + if milestone_rewards.size() > 0: + upgrade_text += "\n🏆 里程碑奖励:" + ", ".join(milestone_rewards) + + add_battle_detail_to_panel(upgrade_text, Color.GOLD) call_deferred("update_ui") +# 应用随机属性升级 +func apply_random_attribute_upgrade() -> Array[String]: + var upgraded_attributes: Array[String] = [] + var available_attributes = base_upgrade_attributes.duplicate() + + # 随机选择几个属性进行升级 + for i in range(min(attributes_per_level, available_attributes.size())): + var random_index = randi() % available_attributes.size() + var selected_attribute = available_attributes[random_index] + available_attributes.remove_at(random_index) + + # 应用属性升级 + var upgrade_applied = apply_single_attribute_upgrade(selected_attribute) + if upgrade_applied: + upgraded_attributes.append(upgrade_applied) + + return upgraded_attributes + +# 应用单个属性升级 +func apply_single_attribute_upgrade(attribute_name: String) -> String: + match attribute_name: + "max_health": + var bonus = randf_range(8.0, 15.0) # 随机8-15点生命值 + max_health += bonus + return "生命值 +" + str(int(bonus)) + "attack_damage": + var bonus = randf_range(2.0, 5.0) # 随机2-5点攻击力 + attack_damage += bonus + return "攻击力 +" + str(int(bonus)) + "move_speed": + var bonus = randf_range(3.0, 8.0) # 随机3-8点移动速度 + move_speed += bonus + return "移动速度 +" + str(int(bonus)) + "max_shield": + var bonus = randf_range(5.0, 12.0) # 随机5-12点护盾值 + max_shield += bonus + return "护盾值 +" + str(int(bonus)) + "max_armor": + var bonus = randf_range(4.0, 10.0) # 随机4-10点护甲值 + max_armor += bonus + return "护甲值 +" + str(int(bonus)) + "crit_rate": + var bonus = randf_range(0.01, 0.03) # 随机1-3%暴击率 + crit_rate = min(1.0, crit_rate + bonus) # 暴击率上限100% + return "暴击率 +" + str(int(bonus * 100)) + "%" + "health_regen": + var bonus = randf_range(0.3, 0.8) # 随机0.3-0.8点生命恢复 + health_regen += bonus + return "生命恢复 +" + str("%.1f" % bonus) + "attack_range": + var bonus = randf_range(8.0, 20.0) # 随机8-20点攻击距离 + attack_range += bonus + return "攻击距离 +" + str(int(bonus)) + _: + return "" + +# 应用里程碑奖励 +func apply_milestone_bonus() -> Array[String]: + var milestone_rewards: Array[String] = [] + + if not level_milestone_bonuses.has(pet_level): + return milestone_rewards + + var bonuses = level_milestone_bonuses[pet_level] + + for bonus_key in bonuses.keys(): + var bonus_value = bonuses[bonus_key] + var reward_text = apply_milestone_bonus_single(bonus_key, bonus_value) + if reward_text != "": + milestone_rewards.append(reward_text) + + return milestone_rewards + +# 应用单个里程碑奖励 +func apply_milestone_bonus_single(bonus_key: String, bonus_value) -> String: + match bonus_key: + "max_health": + max_health += bonus_value + return "生命值 +" + str(bonus_value) + "attack_damage": + attack_damage += bonus_value + return "攻击力 +" + str(bonus_value) + "max_shield": + max_shield += bonus_value + return "护盾值 +" + str(bonus_value) + "max_armor": + max_armor += bonus_value + return "护甲值 +" + str(bonus_value) + "crit_rate": + crit_rate = min(1.0, crit_rate + bonus_value) + return "暴击率 +" + str(int(bonus_value * 100)) + "%" + "armor_penetration": + armor_penetration += bonus_value + return "护甲穿透 +" + str(bonus_value) + "life_steal": + life_steal = min(1.0, life_steal + bonus_value) + return "生命汲取 +" + str(int(bonus_value * 100)) + "%" + "knockback_resist": + knockback_resist = min(1.0, knockback_resist + bonus_value) + return "击退抗性 +" + str(int(bonus_value * 100)) + "%" + "dodge_rate": + dodge_rate = min(1.0, dodge_rate + bonus_value) + return "闪避率 +" + str(int(bonus_value * 100)) + "%" + "health_regen": + health_regen += bonus_value + return "生命恢复 +" + str(bonus_value) + "move_speed": + move_speed += bonus_value + return "移动速度 +" + str(bonus_value) + "attack_range": + attack_range += bonus_value + return "攻击距离 +" + str(bonus_value) + "shield_regen": + shield_regen += bonus_value + return "护盾恢复 +" + str(bonus_value) + "crit_damage": + crit_damage += bonus_value + return "暴击伤害 +" + str(int(bonus_value * 100)) + "%" + "berserker_bonus": + berserker_bonus += bonus_value + return "狂暴加成 +" + str(int(bonus_value * 100)) + "%" + "damage_reflect": + damage_reflect = min(1.0, damage_reflect + bonus_value) + return "伤害反弹 +" + str(int(bonus_value * 100)) + "%" + "control_resist": + control_resist = min(1.0, control_resist + bonus_value) + return "控制抗性 +" + str(int(bonus_value * 100)) + "%" + "projectile_speed": + projectile_speed += bonus_value + return "子弹速度 +" + str(bonus_value) + "pierce_count": + pierce_count += bonus_value + return "穿透数量 +" + str(bonus_value) + "enable_berserker_mode": + if bonus_value: + enable_berserker_mode = true + return "解锁狂暴模式" + else: + return "" + "enable_damage_reflect": + if bonus_value: + enable_damage_reflect = true + return "解锁伤害反弹" + else: + return "" + "enable_aid_system": + if bonus_value: + enable_aid_system = true + return "解锁援助召唤" + else: + return "" + "enable_death_immunity": + if bonus_value: + enable_death_immunity = true + death_immunity = true + return "解锁死亡免疫" + else: + return "" + "enable_resurrection": + if bonus_value: + enable_resurrection = true + return "解锁死亡重生" + else: + return "" + _: + return "" + # 显示升级特效 func show_level_up_effect(): if not pet_image: @@ -1677,34 +1933,24 @@ func heal(amount: float): current_health = min(max_health, current_health + amount) call_deferred("update_ui") -# 对目标应用击退效果 +# 击退效果已禁用 func apply_knockback_to_target(target: CharacterBody2D): - if not target or not is_instance_valid(target): - return - - # 计算击退方向 - var direction = (target.global_position - global_position).normalized() - - # 计算击退力度(考虑目标的击退抗性) - var effective_knockback = knockback_force * (1.0 - target.knockback_resist) - - if effective_knockback > 0: - target.apply_knockback(direction, effective_knockback) - add_battle_detail_to_panel(pet_name + " 击退了 " + target.pet_name) + # 击退功能暂时禁用 + pass -# 被击退时调用 +# 击退效果已禁用 func apply_knockback(direction: Vector2, force: float): - if not is_alive: - return + # 击退功能暂时禁用 + pass + +# 将位置限制在战斗区域内 +func clamp_position_to_battle_area(pos: Vector2) -> Vector2: + var battle_area_min = Vector2(50, 50) + var battle_area_max = Vector2(1350, 670) - # 设置击退状态 - is_being_knocked_back = true - knockback_velocity = direction * force - - # 击退时短暂失去目标(可选) - if current_target and randf() < 0.3: # 30%概率失去目标 - current_target = null - current_state = PetState.IDLE + pos.x = clamp(pos.x, battle_area_min.x, battle_area_max.x) + pos.y = clamp(pos.y, battle_area_min.y, battle_area_max.y) + return pos # 元素克制计算 func get_element_multiplier(attacker_element: ElementType, defender_element: ElementType) -> float: @@ -1827,3 +2073,82 @@ func clamp_to_patrol_area(): # 限制位置 position.x = clamp(position.x, min_x, max_x) position.y = clamp(position.y, min_y, max_y) + +# 播放受伤动画(带冷却保护) +func play_hurt_animation(): + if not pet_image: + return + + # 检查受伤动画冷却时间 + var current_time = Time.get_ticks_msec() / 1000.0 + if current_time - last_hurt_time < hurt_animation_cooldown: + return # 冷却中,不播放动画 + + last_hurt_time = current_time + + # 如果已经有受伤动画在播放,停止之前的 + if hurt_tween: + hurt_tween.kill() + hurt_tween = null + + # 性能模式下简化动画 + if performance_mode: + # 简单的颜色变化,无需Tween + pet_image.modulate = Color.RED + # 使用计时器恢复颜色(更轻量) + await get_tree().create_timer(0.1).timeout + if pet_image: # 确保宠物还存在 + pet_image.modulate = original_modulate + return + + # 创建受伤动画(闪红效果) + hurt_tween = create_tween() + + # 立即变红 + pet_image.modulate = Color.RED + + # 0.2秒后恢复原色 + hurt_tween.tween_property(pet_image, "modulate", original_modulate, 0.2) + + # 动画结束后清理 + hurt_tween.tween_callback(func(): + hurt_tween = null + ) + +# 切换性能模式 +func toggle_performance_mode(): + performance_mode = !performance_mode + var mode_text = "性能模式" if performance_mode else "正常模式" + add_battle_detail_to_panel("⚡ " + pet_name + " 切换到 " + mode_text, Color.YELLOW) + print("⚡ " + pet_name + " 切换到 " + mode_text) + +# 输出宠物性能状态 +func debug_performance_status(): + print("=== " + pet_name + " 性能状态调试 ===") + print("性能模式: " + str(performance_mode)) + print("伤害反弹深度: " + str(damage_reflect_depth)) + print("帧跳跃计数: " + str(frame_skip_counter)) + print("上次受伤时间: " + str(last_hurt_time)) + print("上次攻击时间: " + str(last_attack_time)) + print("当前状态: " + str(current_state)) + print("是否存活: " + str(is_alive)) + print("是否正在死亡: " + str(is_dying)) + print("============================") + +# 重置性能状态(紧急恢复) +func reset_performance_state(): + performance_mode = false + damage_reflect_depth = 0 + frame_skip_counter = 0 + + # 清理可能卡住的动画 + if hurt_tween: + hurt_tween.kill() + hurt_tween = null + + # 恢复正常颜色 + if pet_image: + pet_image.modulate = original_modulate + + print("🔄 " + pet_name + " 性能状态已重置") + add_battle_detail_to_panel("🔄 " + pet_name + " 性能状态已重置", Color.GREEN) diff --git a/Script/Pet/PetFightPanel.gd b/Script/Pet/PetFightPanel.gd index f634a96..4cca55e 100644 --- a/Script/Pet/PetFightPanel.gd +++ b/Script/Pet/PetFightPanel.gd @@ -57,7 +57,7 @@ var current_attacker_name: String = "" # 当前进攻者用户名 func _ready(): - # 加载宠物配置 + visibility_changed.connect(_on_visibility_changed) load_pet_configs() # 连接返回农场按钮 @@ -69,7 +69,6 @@ func _ready(): battle_end_panel.visible = false if pet_battle_details_panel: pet_battle_details_panel.visible = false - # 加载宠物配置 func load_pet_configs(): @@ -115,10 +114,17 @@ func clear_battle_details(): if battle_details: battle_details.text = "" +# 战斗结束检查计时器 +var battle_check_timer: float = 0.0 +var battle_check_interval: float = 0.5 # 每0.5秒检查一次,减少性能开销 + func _process(delta): - # 只有启用自动对战时才检查战斗结束 + # 只有启用自动对战时才检查战斗结束,并使用计时器减少检查频率 if auto_battle_enabled and battle_started and not battle_ended: - check_battle_end() + battle_check_timer += delta + if battle_check_timer >= battle_check_interval: + battle_check_timer = 0.0 + check_battle_end() # 获取队伍节点 - 供宠物调用 @@ -224,12 +230,6 @@ func end_battle(winner: String): # 显示战斗结算面板 func show_battle_end_panel(winner: String): var result_text = "" - var team1_survivors = 0 - var team2_survivors = 0 - var team1_total_damage = 0.0 - var team2_total_damage = 0.0 - var team1_pets_info: Array[String] = [] - var team2_pets_info: Array[String] = [] # 统计存活宠物和详细信息 - 从宠物组中获取 var all_pets = get_tree().get_nodes_in_group("pets") @@ -237,22 +237,6 @@ func show_battle_end_panel(winner: String): if not is_instance_valid(pet): continue - var status = "💀死亡" - if pet.is_alive: - status = "❤️存活(" + str(int(pet.current_health)) + ")" - if pet.pet_team == "team1": - team1_survivors += 1 - elif pet.pet_team == "team2": - team2_survivors += 1 - - # 统计战力 - if pet.pet_team == "team1": - team1_total_damage += pet.attack_damage - team1_pets_info.append(pet.pet_name + " " + status) - elif pet.pet_team == "team2": - team2_total_damage += pet.attack_damage - team2_pets_info.append(pet.pet_name + " " + status) - # 构建结算文本 result_text += "=== 战斗结算 ===\n\n" @@ -416,39 +400,46 @@ func clear_all_pets(): # 清空对战细节 clear_battle_details() - # 先移除宠物组标签 - var all_pets = get_tree().get_nodes_in_group("pets") - for pet in all_pets: - if is_instance_valid(pet): - # 检查是否是当前面板下的宠物 - if pet.get_parent() == team1_node or pet.get_parent() == team2_node or pet.get_parent() == neutral_node: - pet.remove_from_group("pets") - pet.remove_from_group("team1") - pet.remove_from_group("team2") - pet.remove_from_group("neutral") + # 批量处理宠物清理,提高性能 + var nodes_to_clear = [team1_node, team2_node, neutral_node] - # 清理现有宠物 - for child in team1_node.get_children(): - if is_instance_valid(child): - child.queue_free() - - for child in team2_node.get_children(): - if is_instance_valid(child): - child.queue_free() - - for child in neutral_node.get_children(): - if is_instance_valid(child): - child.queue_free() + for node in nodes_to_clear: + if not is_instance_valid(node): + continue + + # 先移除组标签,再清理节点 + for child in node.get_children(): + if is_instance_valid(child): + # 停止宠物的所有行为,防止在清理过程中继续执行逻辑 + if child.has_method("set_combat_enabled"): + child.set_combat_enabled(false) + + # 移除所有组标签 + child.remove_from_group("pets") + child.remove_from_group("team1") + child.remove_from_group("team2") + child.remove_from_group("neutral") + child.remove_from_group("aid_minions") + + # 立即销毁,避免延迟 + node.remove_child(child) + child.queue_free() # 清空队伍数组 team1_pets.clear() team2_pets.clear() - # 清理所有子弹 - var all_projectiles = get_tree().get_nodes_in_group("projectiles") - for projectile in all_projectiles: - if is_instance_valid(projectile): - projectile.queue_free() + # 清理所有子弹和援助宠物 + var groups_to_clear = ["projectiles", "aid_minions"] + for group_name in groups_to_clear: + var group_nodes = get_tree().get_nodes_in_group(group_name) + for node in group_nodes: + if is_instance_valid(node): + node.remove_from_group(group_name) + node.queue_free() + + # 等待一帧确保清理完成 + await get_tree().process_frame # 处理偷菜对战结果 func handle_steal_battle_result(winner: String): @@ -556,7 +547,9 @@ func update_battle_pet_data(pet_id: String, attacker_name: String, exp_gained: f "new_max_experience": max_exp, "new_intimacy": current_intimacy, "level_ups": level_ups, - "level_bonus_multiplier": level_bonus_multiplier + "level_bonus_multiplier": level_bonus_multiplier, + "is_steal_battle": is_steal_battle, + "battle_winner": winner_team } # 发送数据到服务器 diff --git a/Script/Pet/PetFightPanel_BattleRoyale.gd.uid b/Script/Pet/PetFightPanel_BattleRoyale.gd.uid deleted file mode 100644 index 08ad1c9..0000000 --- a/Script/Pet/PetFightPanel_BattleRoyale.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b4p4xk0pdf7yg diff --git a/Script/SmallPanel/GlobalServerBroadcastPanel.gd b/Script/SmallPanel/GlobalServerBroadcastPanel.gd index a7aa1e9..18c445e 100644 --- a/Script/SmallPanel/GlobalServerBroadcastPanel.gd +++ b/Script/SmallPanel/GlobalServerBroadcastPanel.gd @@ -95,8 +95,9 @@ func _add_message_to_history(data: Dictionary): # 如果有玩家昵称,优先显示昵称 var display_name = player_name if player_name != "" else username - # 格式化时间 - var datetime = Time.get_datetime_dict_from_unix_time(timestamp) + # 格式化时间 - 确保timestamp是整数类型 + var timestamp_int = int(timestamp) if typeof(timestamp) == TYPE_STRING else timestamp + var datetime = Time.get_datetime_dict_from_unix_time(timestamp_int) var time_str = "%04d年%02d月%02d日 %02d:%02d:%02d" % [datetime.year, datetime.month, datetime.day, datetime.hour, datetime.minute, datetime.second] # 创建消息记录 diff --git a/Script/SmallPanel/LandPanel.gd b/Script/SmallPanel/LandPanel.gd index 102b860..5b70157 100644 --- a/Script/SmallPanel/LandPanel.gd +++ b/Script/SmallPanel/LandPanel.gd @@ -427,7 +427,7 @@ func _on_harvest_button_pressed(): -#=================面板通用函数========================== +#===================面板通用函数========================== #退出 func _on_quit_button_pressed(): self.hide() @@ -447,7 +447,7 @@ func _on_visibility_changed(): GlobalVariables.isZoomDisabled = false pass -#=================面板通用函数========================== +#===================面板通用函数========================== # 更新面板信息显示 func _update_panel_information(): diff --git a/Script/SmallPanel/PetInformPanel.gd b/Script/SmallPanel/PetInformPanel.gd index e7c8167..baf68fb 100644 --- a/Script/SmallPanel/PetInformPanel.gd +++ b/Script/SmallPanel/PetInformPanel.gd @@ -60,9 +60,8 @@ func show_pet_info(pet_name: String, pet_data: Dictionary): # 设置宠物图片 _set_pet_image(pet_name) - # 设置宠物名称 - var basic_info = pet_data.get("基本信息", {}) - var pet_owner_name = basic_info.get("宠物名称", pet_name) + # 设置宠物名称(新格式:直接从pet_name字段获取) + var pet_owner_name = pet_data.get("pet_name", pet_name) pet_name_edit.text = pet_owner_name # 设置宠物详细信息 @@ -85,26 +84,40 @@ func _set_pet_image(pet_name: String): # 获取宠物纹理 func _get_pet_texture(pet_name: String) -> Texture2D: - var pet_config = _load_pet_config() + # 从服务器的宠物配置获取场景路径 + var pet_config = main_game.pet_config # 使用服务器返回的宠物配置 if pet_config.has(pet_name): var pet_info = pet_config[pet_name] - var scene_path = pet_info.get("场景路径", "") + var scene_path = pet_info.get("pet_image", "") # 使用服务器数据的pet_image字段 + print("宠物信息面板 ", pet_name, " 的图片路径:", scene_path) if scene_path != "" and ResourceLoader.exists(scene_path): + print("宠物信息面板开始加载宠物场景:", scene_path) var pet_scene = load(scene_path) if pet_scene: var pet_instance = pet_scene.instantiate() - var pet_image_node = pet_instance.get_node_or_null("PetImage") - if pet_image_node and pet_image_node.sprite_frames: - var animation_names = pet_image_node.sprite_frames.get_animation_names() + # 直接使用实例化的场景根节点,因为根节点就是PetImage + if pet_instance and pet_instance.sprite_frames: + var animation_names = pet_instance.sprite_frames.get_animation_names() if animation_names.size() > 0: var default_animation = animation_names[0] - var frame_count = pet_image_node.sprite_frames.get_frame_count(default_animation) + var frame_count = pet_instance.sprite_frames.get_frame_count(default_animation) if frame_count > 0: - var texture = pet_image_node.sprite_frames.get_frame_texture(default_animation, 0) + var texture = pet_instance.sprite_frames.get_frame_texture(default_animation, 0) + print("宠物信息面板成功获取宠物纹理:", pet_name) pet_instance.queue_free() return texture + else: + print("宠物信息面板场景没有动画:", pet_name) + else: + print("宠物信息面板场景没有PetImage节点或sprite_frames:", pet_name) pet_instance.queue_free() + else: + print("宠物信息面板无法加载宠物场景:", scene_path) + else: + print("宠物信息面板图片路径无效或文件不存在:", scene_path) + else: + print("宠物信息面板配置中没有找到:", pet_name) return null # 加载宠物配置数据 @@ -123,19 +136,10 @@ func _load_pet_config() -> Dictionary: return json.data -# 设置宠物详细信息(使用bbcode美化) +# 设置宠物详细信息(使用bbcode美化)- 新格式 func _set_pet_detailed_info(pet_name: String, pet_data: Dictionary): - var basic_info = pet_data.get("基本信息", {}) - var level_exp = pet_data.get("等级经验", {}) - var purchase_info = pet_data.get("购买信息", {}) - var health_defense = pet_data.get("生命与防御", {}) - var attack_info = pet_data.get("基础攻击属性", {}) - var movement = pet_data.get("移动与闪避", {}) - var element = pet_data.get("元素属性", {}) - var quality = pet_data.get("品质系统", {}) - # 计算宠物年龄 - var pet_birthday = basic_info.get("生日", "") + var pet_birthday = pet_data.get("pet_birthday", "") var pet_age = 0 if pet_birthday != "": pet_age = _calculate_pet_age(pet_birthday) @@ -145,76 +149,64 @@ func _set_pet_detailed_info(pet_name: String, pet_data: Dictionary): # 基本信息 info_text += "[color=pink][b]🐾 基本信息[/b][/color]\n" - info_text += "宠物类型:[color=yellow]" + str(basic_info.get("宠物类型", "未知")) + "[/color]\n" - info_text += "宠物编号:[color=gray]" + str(basic_info.get("宠物ID", "无")) + "[/color]\n" - info_text += "性格特点:[color=cyan]" + str(basic_info.get("性格", "活泼")) + "[/color]\n" + info_text += "宠物类型:[color=yellow]" + str(pet_data.get("pet_type", "未知")) + "[/color]\n" + info_text += "宠物编号:[color=gray]" + str(pet_data.get("pet_id", "无")) + "[/color]\n" + info_text += "性格特点:[color=cyan]" + str(pet_data.get("pet_temperament", "活泼")) + "[/color]\n" info_text += "出生日期:[color=green]" + str(pet_birthday) + "[/color]\n" info_text += "年龄天数:[color=orange]" + str(pet_age) + " 天[/color]\n" + info_text += "爱好:[color=magenta]" + str(pet_data.get("pet_hobby", "无")) + "[/color]\n" + info_text += "介绍:[color=lime]" + str(pet_data.get("pet_introduction", "无")) + "[/color]\n\n" # 等级经验 info_text += "[color=gold][b]⭐ 等级经验[/b][/color]\n" - info_text += "当前等级:[color=yellow]" + str(level_exp.get("宠物等级", 1)) + " 级[/color]\n" - info_text += "经验值:[color=cyan]" + str(level_exp.get("当前经验", 0)) + "/" + str(level_exp.get("最大经验", 100)) + "[/color]\n" - info_text += "亲密度:[color=pink]" + str(level_exp.get("亲密度", 0)) + "/" + str(level_exp.get("最大亲密度", 1000)) + "[/color]\n\n" + info_text += "当前等级:[color=yellow]" + str(pet_data.get("pet_level", 1)) + " 级[/color]\n" + info_text += "经验值:[color=cyan]" + str(pet_data.get("pet_experience", 0)) + "/" + str(pet_data.get("pet_max_experience", 1000)) + "[/color]\n" + info_text += "亲密度:[color=pink]" + str(pet_data.get("pet_intimacy", 0)) + "/" + str(pet_data.get("pet_max_intimacy", 1000)) + "[/color]\n\n" # 生命与防御 info_text += "[color=red][b]❤️ 生命与防御[/b][/color]\n" - info_text += "生命值:[color=red]" + str(health_defense.get("当前生命值", 0)) + "/" + str(health_defense.get("最大生命值", 0)) + "[/color]\n" - info_text += "护甲值:[color=blue]" + str(health_defense.get("当前护甲值", 0)) + "/" + str(health_defense.get("最大护甲值", 0)) + "[/color]\n" - info_text += "护盾值:[color=cyan]" + str(health_defense.get("当前护盾值", 0)) + "/" + str(health_defense.get("最大护盾值", 0)) + "[/color]\n" - info_text += "生命恢复:[color=lime]" + str(health_defense.get("生命恢复速度", 0)) + "/秒[/color]\n\n" + info_text += "生命值:[color=red]" + str(pet_data.get("pet_current_health", pet_data.get("max_health", 100))) + "/" + str(pet_data.get("max_health", 100)) + "[/color]\n" + info_text += "护甲值:[color=blue]" + str(pet_data.get("pet_current_armor", pet_data.get("max_armor", 0))) + "/" + str(pet_data.get("max_armor", 0)) + "[/color]\n" + info_text += "护盾值:[color=cyan]" + str(pet_data.get("pet_current_shield", pet_data.get("max_shield", 0))) + "/" + str(pet_data.get("max_shield", 0)) + "[/color]\n" + info_text += "生命恢复:[color=lime]" + str(pet_data.get("health_regen", 0)) + "/秒[/color]\n" + info_text += "护盾恢复:[color=cyan]" + str(pet_data.get("shield_regen", 0)) + "/秒[/color]\n\n" # 攻击属性 info_text += "[color=orange][b]⚔️ 攻击属性[/b][/color]\n" - info_text += "攻击类型:[color=yellow]" + _get_attack_type_name(str(attack_info.get("攻击类型", "MELEE"))) + "[/color]\n" - info_text += "攻击伤害:[color=red]" + str(attack_info.get("基础攻击伤害", 0)) + " 点[/color]\n" - info_text += "攻击距离:[color=green]" + str(attack_info.get("攻击距离", 0)) + " 像素[/color]\n" - info_text += "暴击几率:[color=purple]" + str(attack_info.get("暴击率", 0) * 100) + "%[/color]\n" - info_text += "暴击倍数:[color=purple]" + str(attack_info.get("暴击伤害倍数", 1.0)) + " 倍[/color]\n" - info_text += "生命汲取:[color=magenta]" + str(attack_info.get("生命汲取", 0) * 100) + "%[/color]\n\n" + info_text += "攻击伤害:[color=red]" + str(pet_data.get("base_attack_damage", 0)) + " 点[/color]\n" + info_text += "暴击几率:[color=purple]" + str(pet_data.get("crit_rate", 0) * 100) + "%[/color]\n" + info_text += "暴击倍数:[color=purple]" + str(pet_data.get("crit_damage", 1.0)) + " 倍[/color]\n" + info_text += "护甲穿透:[color=orange]" + str(pet_data.get("armor_penetration", 0)) + " 点[/color]\n" + info_text += "左手武器:[color=yellow]" + str(pet_data.get("left_weapon", "无")) + "[/color]\n" + info_text += "右手武器:[color=yellow]" + str(pet_data.get("right_weapon", "无")) + "[/color]\n\n" # 移动与闪避 info_text += "[color=green][b]🏃 移动与闪避[/b][/color]\n" - info_text += "移动速度:[color=cyan]" + str(movement.get("移动速度", 0)) + " 像素/秒[/color]\n" - info_text += "闪避几率:[color=yellow]" + str(movement.get("闪避率", 0) * 100) + "%[/color]\n" - info_text += "击退力度:[color=red]" + str(movement.get("击退力度", 0)) + " 点[/color]\n" - info_text += "击退抗性:[color=blue]" + str(movement.get("击退抗性", 0) * 100) + "%[/color]\n\n" + info_text += "移动速度:[color=cyan]" + str(pet_data.get("move_speed", 0)) + " 像素/秒[/color]\n" + info_text += "闪避几率:[color=yellow]" + str(pet_data.get("dodge_rate", 0) * 100) + "%[/color]\n\n" # 元素属性 info_text += "[color=purple][b]🔥 元素属性[/b][/color]\n" - info_text += "元素类型:[color=yellow]" + _get_element_name(str(element.get("元素类型", "NONE"))) + "[/color]\n" - info_text += "元素伤害:[color=orange]" + str(element.get("元素克制额外伤害", 0)) + " 点[/color]\n\n" + info_text += "元素类型:[color=yellow]" + _get_element_name(str(pet_data.get("element_type", "NONE"))) + "[/color]\n" + info_text += "元素伤害:[color=orange]" + str(pet_data.get("element_damage_bonus", 0)) + " 点[/color]\n\n" - # 品质系统 - var quality_text = str(quality.get("宠物品质", "COMMON")) - var quality_color = "white" - var quality_name = "" - if quality_text == "COMMON": - quality_color = "gray" - quality_name = "普通" - elif quality_text == "RARE": - quality_color = "blue" - quality_name = "稀有" - elif quality_text == "EPIC": - quality_color = "purple" - quality_name = "史诗" - elif quality_text == "LEGENDARY": - quality_color = "orange" - quality_name = "传说" - else: - quality_name = quality_text - - info_text += "[color=gold][b]✨ 品质系统[/b][/color]\n" - info_text += "宠物品质:[color=" + quality_color + "]" + quality_name + "[/color]\n\n" - - # 购买信息 - info_text += "[color=gold][b]💰 购买信息[/b][/color]\n" - info_text += "购买价格:[color=yellow]" + str(purchase_info.get("购买价格", 0)) + " 金币[/color]\n" + # 技能系统 + info_text += "[color=gold][b]✨ 技能系统[/b][/color]\n" + if pet_data.get("enable_multi_projectile_skill", false): + info_text += "多重弹射:[color=green]已激活[/color] (延迟: " + str(pet_data.get("multi_projectile_delay", 0)) + "秒)\n" + if pet_data.get("enable_berserker_skill", false): + info_text += "狂暴技能:[color=red]已激活[/color] (倍数: " + str(pet_data.get("berserker_bonus", 1.0)) + ", 持续: " + str(pet_data.get("berserker_duration", 0)) + "秒)\n" + if pet_data.get("enable_self_destruct_skill", false): + info_text += "自爆技能:[color=orange]已激活[/color]\n" + if pet_data.get("enable_summon_pet_skill", false): + info_text += "召唤技能:[color=cyan]已激活[/color] (数量: " + str(pet_data.get("summon_count", 0)) + ", 缩放: " + str(pet_data.get("summon_scale", 1.0)) + ")\n" + if pet_data.get("enable_death_respawn_skill", false): + info_text += "死亡重生:[color=purple]已激活[/color] (生命: " + str(pet_data.get("respawn_health_percentage", 0) * 100) + "%)\n" + info_text += "\n" # 设置文本 pet_inform.text = info_text - # 获取攻击类型名称 func _get_attack_type_name(attack_type: String) -> String: match attack_type: @@ -340,9 +332,8 @@ func on_edit_inform_button_pressed(): Toast.show("宠物名字太长,最多20个字符", Color.RED, 2.0, 1.0) return - # 获取当前宠物名字 - var basic_info = current_pet_data.get("基本信息", {}) - var current_name = basic_info.get("宠物名称", "") + # 获取当前宠物名字(新格式) + var current_name = current_pet_data.get("pet_name", "") # 检查名字是否有变化 if new_pet_name == current_name: @@ -383,9 +374,8 @@ func _on_confirm_rename_pet(new_name: String, dialog: AcceptDialog): # 取消重命名宠物 func _on_cancel_rename_pet(dialog: AcceptDialog): - # 恢复原名字 - var basic_info = current_pet_data.get("基本信息", {}) - var original_name = basic_info.get("宠物名称", "") + # 恢复原名字(新格式) + var original_name = current_pet_data.get("pet_name", "") pet_name_edit.text = original_name dialog.queue_free() @@ -395,9 +385,8 @@ func _send_rename_pet_request(new_name: String): Toast.show("网络功能不可用", Color.RED, 2.0, 1.0) return - # 获取宠物ID - var basic_info = current_pet_data.get("基本信息", {}) - var pet_id = basic_info.get("宠物ID", "") + # 获取宠物ID(新格式) + var pet_id = current_pet_data.get("pet_id", "") if pet_id == "": Toast.show("宠物ID无效", Color.RED, 2.0, 1.0) @@ -411,9 +400,9 @@ func _send_rename_pet_request(new_name: String): # 处理重命名成功的响应(从宠物背包或其他地方调用) func on_rename_pet_success(pet_id: String, new_name: String): - # 更新当前宠物数据 - if current_pet_data.get("基本信息", {}).get("宠物ID", "") == pet_id: - current_pet_data["基本信息"]["宠物名称"] = new_name + # 更新当前宠物数据(新格式) + if current_pet_data.get("pet_id", "") == pet_id: + current_pet_data["pet_name"] = new_name pet_name_edit.text = new_name Toast.show("宠物名字修改成功!", Color.GREEN, 2.0, 1.0) @@ -469,11 +458,6 @@ func on_use_item_button_pressed(): # 巡逻按钮点击事件 func _on_patrol_button_pressed(): - #直接在客户端 - patro_button.text = "取消巡逻" - patro_button.modulate = Color.ORANGE - - if current_pet_data.is_empty(): Toast.show("没有选择宠物", Color.RED, 2.0, 1.0) return @@ -484,65 +468,61 @@ func _on_patrol_button_pressed(): return # 获取宠物ID - var basic_info = current_pet_data.get("基本信息", {}) - var pet_id = basic_info.get("宠物ID", "") - + var pet_id = current_pet_data.get("pet_id", "") if pet_id == "": Toast.show("宠物ID无效", Color.RED, 2.0, 1.0) return - # 检查当前宠物是否已在巡逻 - var is_currently_patrolling = _is_pet_patrolling(pet_id) + # 检查是否已经在巡逻 + var is_patrolling = _is_pet_patrolling(pet_id) - if is_currently_patrolling: - # 取消巡逻 - _remove_from_patrol(pet_id) + if is_patrolling: + # 取消巡逻 - 发送到服务器 + _send_patrol_request(pet_id, false) + var pet_name = current_pet_data.get("pet_name", "宠物") + Toast.show("正在取消 " + pet_name + " 的巡逻...", Color.YELLOW, 2.0, 1.0) else: - # 添加到巡逻 - _add_to_patrol(pet_id) + # 检查巡逻宠物数量限制 + if main_game.patrol_pet_instances.size() >= 4: + Toast.show("最多只能设置4个巡逻宠物", Color.RED, 2.0, 1.0) + return + + # 开始巡逻 - 发送到服务器 + _send_patrol_request(pet_id, true) + var pet_name = current_pet_data.get("pet_name", "宠物") + #Toast.show("正在设置 " + pet_name + " 为巡逻宠物...", Color.GREEN, 2.0, 1.0) -# 检查宠物是否正在巡逻(基于服务器数据) +# 发送巡逻请求到服务器 +func _send_patrol_request(pet_id: String, is_patrolling: bool): + var message = { + "type": "set_patrol_pet", + "pet_id": pet_id, + "is_patrolling": is_patrolling + } + tcp_network_manager_panel.client.send_data(message) + +# 检查宠物是否在巡逻 func _is_pet_patrolling(pet_id: String) -> bool: - # 检查服务器的巡逻宠物数据 - if main_game.patrol_pets == null or main_game.patrol_pets.size() == 0: - return false - - # 遍历巡逻宠物列表,查找匹配的ID - for patrol_pet in main_game.patrol_pets: - var patrol_pet_id = patrol_pet.get("基本信息", {}).get("宠物ID", "") - if patrol_pet_id == pet_id: - return true - + # 检查本地 patrol_pet_instances 数组 + for pet_instance in main_game.patrol_pet_instances: + if pet_instance and is_instance_valid(pet_instance): + if pet_instance.pet_id == pet_id: + return true return false -# 添加到巡逻(新的基于ID的逻辑) -func _add_to_patrol(pet_id: String): - # 检查巡逻宠物数量限制(目前服务器设置最多3个) - if main_game.patrol_pets != null and main_game.patrol_pets.size() >= 3: - Toast.show("最多只能设置3个巡逻宠物", Color.ORANGE, 3.0, 1.0) - return +# 移除巡逻宠物 +func _remove_patrol_pet(pet_id: String): + # 查找并移除对应的巡逻宠物实例 + for pet_instance in main_game.patrol_pet_instances: + if pet_instance and is_instance_valid(pet_instance): + # 检查是否是对应的巡逻宠物 + if pet_instance.pet_id == pet_id: + pet_instance.queue_free() + main_game.patrol_pet_instances.erase(pet_instance) + print("移除巡逻宠物实例: " + pet_instance.pet_name) + return - # 目前简化为只允许一个巡逻宠物 - if main_game.patrol_pets != null and main_game.patrol_pets.size() >= 1: - Toast.show("已有宠物在巡逻,请先取消当前巡逻", Color.ORANGE, 3.0, 1.0) - return - - # 如果不是访问模式,则发送到服务器保存 - if not main_game.is_visiting_mode: - # 发送到服务器保存 - tcp_network_manager_panel.sendSetPatrolPet(pet_id, true) - var pet_name = current_pet_data.get("基本信息", {}).get("宠物名称", "未知") - else: - Toast.show("访问模式下无法设置巡逻宠物", Color.ORANGE, 2.0, 1.0) - -# 从巡逻中移除(新的基于ID的逻辑) -func _remove_from_patrol(pet_id: String): - # 如果不是访问模式,则发送到服务器保存 - if not main_game.is_visiting_mode: - # 发送到服务器移除 - tcp_network_manager_panel.sendSetPatrolPet(pet_id, false) - else: - Toast.show("访问模式下无法取消巡逻宠物", Color.ORANGE, 2.0, 1.0) + print("未找到对应的巡逻宠物实例: " + pet_id) # 更新巡逻按钮文本 func _update_patrol_button_text(is_patrolling: bool): @@ -558,8 +538,7 @@ func _refresh_patrol_button(): if current_pet_data.is_empty(): return - var basic_info = current_pet_data.get("基本信息", {}) - var pet_id = basic_info.get("宠物ID", "") + var pet_id = current_pet_data.get("pet_id", "") if pet_id == "": return @@ -578,9 +557,8 @@ func _on_battle_button_pressed(): Toast.show("访问模式下无法设置出战宠物", Color.ORANGE, 2.0, 1.0) return - # 获取宠物ID - var basic_info = current_pet_data.get("基本信息", {}) - var pet_id = basic_info.get("宠物ID", "") + # 获取宠物ID(新格式) + var pet_id = current_pet_data.get("pet_id", "") if pet_id == "": Toast.show("宠物ID无效", Color.RED, 2.0, 1.0) @@ -602,9 +580,9 @@ func _is_pet_battling(pet_id: String) -> bool: if main_game.battle_pets == null or main_game.battle_pets.size() == 0: return false - # 遍历出战宠物列表,查找匹配的ID + # 遍历出战宠物列表,查找匹配的ID(新格式) for battle_pet in main_game.battle_pets: - var battle_pet_id = battle_pet.get("基本信息", {}).get("宠物ID", "") + var battle_pet_id = battle_pet.get("pet_id", "") if battle_pet_id == pet_id: return true @@ -612,9 +590,9 @@ func _is_pet_battling(pet_id: String) -> bool: # 添加到出战(新的基于ID的逻辑) func _add_to_battle(pet_id: String): - # 检查出战宠物数量限制(目前服务器设置最多1个) - if main_game.battle_pets != null and main_game.battle_pets.size() >= 1: - Toast.show("最多只能设置1个出战宠物", Color.ORANGE, 3.0, 1.0) + # 检查出战宠物数量限制(目前服务器设置最多4个) + if main_game.battle_pets != null and main_game.battle_pets.size() >= 4: + Toast.show("最多只能设置4个出战宠物", Color.ORANGE, 3.0, 1.0) return # 检查是否在巡逻中(出战宠物不能是巡逻宠物) @@ -626,7 +604,7 @@ func _add_to_battle(pet_id: String): if not main_game.is_visiting_mode: # 发送到服务器保存 tcp_network_manager_panel.sendSetBattlePet(pet_id, true) - var pet_name = current_pet_data.get("基本信息", {}).get("宠物名称", "未知") + var pet_name = current_pet_data.get("pet_name", "未知") Toast.show("正在设置 " + pet_name + " 为出战宠物...", Color.YELLOW, 2.0, 1.0) else: Toast.show("访问模式下无法设置出战宠物", Color.ORANGE, 2.0, 1.0) @@ -655,8 +633,7 @@ func _refresh_battle_button(): if current_pet_data.is_empty(): return - var basic_info = current_pet_data.get("基本信息", {}) - var pet_id = basic_info.get("宠物ID", "") + var pet_id = current_pet_data.get("pet_id", "") if pet_id == "": return diff --git a/Script/SmallPanel/WisdomTreePanel.gd b/Script/SmallPanel/WisdomTreePanel.gd index 8787c82..81ff411 100644 --- a/Script/SmallPanel/WisdomTreePanel.gd +++ b/Script/SmallPanel/WisdomTreePanel.gd @@ -247,15 +247,20 @@ func handle_wisdom_tree_operation_response(success: bool, message: String, opera # 根据操作类型显示不同的提示 match operation_type: "water": - Toast.show("浇水成功!" + message, Color.CYAN) + #Toast.show("浇水成功!" + message, Color.CYAN) + pass "fertilize": - Toast.show("施肥成功!" + message, Color.PURPLE) + #Toast.show("施肥成功!" + message, Color.PURPLE) + pass "kill_grass": - Toast.show("除草成功!" + message, Color.GREEN) + #Toast.show("除草成功!" + message, Color.GREEN) + pass "kill_bug": - Toast.show("杀虫成功!" + message, Color.GREEN) + #Toast.show("杀虫成功!" + message, Color.GREEN) + pass "play_music": - Toast.show("放音乐成功!" + message, Color.MAGENTA) + #Toast.show("放音乐成功!" + message, Color.MAGENTA) + pass # 放音乐时可能获得随机消息,需要特殊处理 if updated_data.has("random_message"): var random_message = updated_data["random_message"] @@ -293,7 +298,7 @@ func handle_wisdom_tree_message_response(success: bool, message: String, updated main_game.money = updated_data["钱币"] main_game._update_ui() - Toast.show("消息发送成功!", Color.GREEN) + #Toast.show("消息发送成功!", Color.GREEN) else: Toast.show(message, Color.RED) diff --git a/Server/ConsoleCommandsAPI.py b/Server/ConsoleCommandsAPI.py index 0e3c2a1..982f615 100644 --- a/Server/ConsoleCommandsAPI.py +++ b/Server/ConsoleCommandsAPI.py @@ -25,24 +25,24 @@ class ConsoleCommandsAPI: """ self.server = server self.commands = { - "addmoney": self.cmd_add_money, - "addxp": self.cmd_add_experience, - "addlevel": self.cmd_add_level, - "addseed": self.cmd_add_seed, - "lsplayer": self.cmd_list_players, - "playerinfo": self.cmd_player_info, - "resetland": self.cmd_reset_land, - "weather": self.cmd_weather, - "help": self.cmd_help, - "stop": self.cmd_stop, - "save": self.cmd_save_all, - "reload": self.cmd_reload_config, + "addmoney": self.cmd_add_money, # 给玩家添加金币 + "addxp": self.cmd_add_experience, # 给玩家添加经验值 + "addlevel": self.cmd_add_level, # 给玩家添加等级 + "addseed": self.cmd_add_seed, # 给玩家添加种子 + "lsplayer": self.cmd_list_players, # 列出所有玩家 + "playerinfo": self.cmd_player_info, # 查看玩家信息 + "resetland": self.cmd_reset_land, # 重置玩家土地 + "weather": self.cmd_weather, # 设置天气 + "help": self.cmd_help, # 显示帮助信息 + "stop": self.cmd_stop, # 停止服务器 + "save": self.cmd_save_all, # 保存所有玩家数据 + "reload": self.cmd_reload_config, # 重新加载配置文件 # MongoDB管理命令 - "dbtest": self.cmd_db_test, - "dbconfig": self.cmd_db_config, - "dbchat": self.cmd_db_chat, - "dbclean": self.cmd_db_clean, - "dbbackup": self.cmd_db_backup + "dbtest": self.cmd_db_test, # 测试MongoDB连接 + "dbconfig": self.cmd_db_config, # 配置MongoDB连接 + "dbchat": self.cmd_db_chat, # 管理聊天数据 + "dbclean": self.cmd_db_clean, # 清理数据库 + "dbbackup": self.cmd_db_backup # 备份数据库 } # 初始化MongoDB API @@ -227,36 +227,36 @@ class ConsoleCommandsAPI: def cmd_list_players(self, args: List[str]): """列出所有玩家命令: /lsplayer""" - saves_dir = "game_saves" - if not os.path.exists(saves_dir): - print("❌ 游戏存档目录不存在") - return - - player_files = [f for f in os.listdir(saves_dir) if f.endswith('.json')] - if not player_files: - print("📭 暂无已注册玩家") - return - - print(f"📋 已注册玩家列表 (共 {len(player_files)} 人):") - print("-" * 80) - print(f"{'QQ号':<12} {'昵称':<15} {'等级':<6} {'金币':<10} {'最后登录':<20}") - print("-" * 80) - - for i, filename in enumerate(sorted(player_files), 1): - qq_number = filename.replace('.json', '') - try: - player_data = self.server._load_player_data_from_file(qq_number) - if player_data: - nickname = player_data.get("玩家昵称", "未设置") - level = player_data.get("等级", 1) - money = player_data.get("钱币", 0) - last_login = player_data.get("最后登录时间", "从未登录") + try: + # 使用MongoDB获取玩家数据 + if hasattr(self.server, 'mongo_api') and self.server.mongo_api: + players_data = self.server.mongo_api.get_all_players_basic_info() + + if not players_data: + print("📭 暂无已注册玩家") + return + + print(f"📋 已注册玩家列表 (共 {len(players_data)} 人):") + print("-" * 80) + print(f"{'QQ号':<12} {'昵称':<15} {'等级':<6} {'金币':<10} {'最后登录':<20}") + print("-" * 80) + + for player in players_data: + qq_number = player.get("玩家账号", "未知") + nickname = player.get("玩家昵称", "未设置") + level = player.get("等级", 1) + money = player.get("钱币", 0) + last_login = player.get("最后登录时间", "从未登录") print(f"{qq_number:<12} {nickname:<15} {level:<6} {money:<10} {last_login:<20}") - except Exception as e: - print(f"{qq_number:<12} {'数据错误':<15} {'--':<6} {'--':<10} {'无法读取':<20}") - - print("-" * 80) + + print("-" * 80) + else: + print("❌ 未配置MongoDB连接") + + except Exception as e: + print(f"❌ 列出玩家时出错: {str(e)}") + def cmd_player_info(self, args: List[str]): """查看玩家信息命令: /playerinfo QQ号""" diff --git a/Server/QQEmailSendAPI.py b/Server/QQEmailSendAPI.py index 7ea63cf..426288b 100644 --- a/Server/QQEmailSendAPI.py +++ b/Server/QQEmailSendAPI.py @@ -211,7 +211,13 @@ class EmailVerification: # 优先尝试使用MongoDB try: from SMYMongoDBAPI import SMYMongoDBAPI - mongo_api = SMYMongoDBAPI("test") + import os + # 根据环境动态选择MongoDB配置 + if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true': + environment = "production" + else: + environment = "test" + mongo_api = SMYMongoDBAPI(environment) if mongo_api.is_connected(): success = mongo_api.save_verification_code(qq_number, verification_code, expiry_time, code_type) if success: @@ -279,7 +285,13 @@ class EmailVerification: # 优先尝试使用MongoDB try: from SMYMongoDBAPI import SMYMongoDBAPI - mongo_api = SMYMongoDBAPI("test") + import os + # 根据环境动态选择MongoDB配置 + if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true': + environment = "production" + else: + environment = "test" + mongo_api = SMYMongoDBAPI(environment) if mongo_api.is_connected(): success, message = mongo_api.verify_verification_code(qq_number, input_code, code_type) print(f"[验证码系统-MongoDB] QQ {qq_number} 验证结果: {success}, 消息: {message}") @@ -364,7 +376,13 @@ class EmailVerification: # 优先尝试使用MongoDB try: from SMYMongoDBAPI import SMYMongoDBAPI - mongo_api = SMYMongoDBAPI("test") + import os + # 根据环境动态选择MongoDB配置 + if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true': + environment = "production" + else: + environment = "test" + mongo_api = SMYMongoDBAPI(environment) if mongo_api.is_connected(): expired_count = mongo_api.clean_expired_verification_codes() print(f"[验证码系统-MongoDB] 清理完成,删除了 {expired_count} 个过期验证码") @@ -433,7 +451,13 @@ class EmailVerification: # 优先尝试使用MongoDB try: from SMYMongoDBAPI import SMYMongoDBAPI - mongo_api = SMYMongoDBAPI("test") + import os + # 根据环境动态选择MongoDB配置 + if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true': + environment = "production" + else: + environment = "test" + mongo_api = SMYMongoDBAPI(environment) if mongo_api.is_connected(): verification_codes = mongo_api.get_verification_codes() if verification_codes and qq_number in verification_codes: diff --git a/Server/SMYMongoDBAPI.py b/Server/SMYMongoDBAPI.py index 79991f2..372fe1b 100644 --- a/Server/SMYMongoDBAPI.py +++ b/Server/SMYMongoDBAPI.py @@ -31,12 +31,16 @@ class SMYMongoDBAPI: "test": { "host": "localhost", "port": 27017, - "database": "mengyafarm" + "database": "mengyafarm", + "username": None, + "password": None }, "production": { "host": "192.168.31.233", "port": 27017, - "database": "mengyafarm" + "database": "mengyafarm", + "username": "shumengya", + "password": "tyh@19900420" } } @@ -55,8 +59,17 @@ class SMYMongoDBAPI: bool: 连接是否成功 """ try: + from urllib.parse import quote_plus current_config = self.config[self.environment] - connection_string = f"mongodb://{current_config['host']}:{current_config['port']}/" + + # 构建连接字符串 + if current_config.get('username') and current_config.get('password'): + # 对用户名和密码进行URL编码以处理特殊字符 + username = quote_plus(current_config['username']) + password = quote_plus(current_config['password']) + connection_string = f"mongodb://{username}:{password}@{current_config['host']}:{current_config['port']}/{current_config['database']}?authSource=admin" + else: + connection_string = f"mongodb://{current_config['host']}:{current_config['port']}/" self.client = pymongo.MongoClient( connection_string, @@ -386,302 +399,604 @@ class SMYMongoDBAPI: #=====================初始玩家数据模板系统====================== - #=====================验证码系统====================== - def get_verification_codes(self) -> Optional[Dict[str, Any]]: - """获取验证码数据""" - return self._get_config_by_id(self.CONFIG_IDS["verification_codes"], "验证码数据") + #=====================玩家数据管理====================== + + # ========================= 验证码系统 ========================= - def update_verification_codes(self, codes_data: Dict[str, Any]) -> bool: - """更新验证码数据""" - return self._update_config_by_id(self.CONFIG_IDS["verification_codes"], codes_data, "验证码数据") - - #=====================聊天消息系统====================== - def save_chat_message(self, username: str, player_name: str, content: str, timestamp: float = None) -> bool: - """保存聊天消息到MongoDB""" - try: - if timestamp is None: - timestamp = datetime.now().timestamp() - - # 获取日期字符串作为文档标识 - date_obj = datetime.fromtimestamp(timestamp) - date_str = date_obj.strftime("%Y-%m-%d") - - # 创建消息记录 - message_record = { - "username": username, - "player_name": player_name, - "content": content, - "timestamp": timestamp, - "time_str": date_obj.strftime("%Y年%m月%d日 %H:%M:%S") - } - - collection = self.get_collection("chat") - - # 查找当天的文档 - query = {"date": date_str} - existing_doc = collection.find_one(query) - - if existing_doc: - # 如果文档存在,添加消息到messages数组 - result = collection.update_one( - query, - { - "$push": {"messages": message_record}, - "$set": {"updated_at": datetime.now()} - } - ) - success = result.acknowledged and result.modified_count > 0 - else: - # 如果文档不存在,创建新文档 - new_doc = { - "date": date_str, - "messages": [message_record], - "created_at": datetime.now(), - "updated_at": datetime.now() - } - result = collection.insert_one(new_doc) - success = result.acknowledged - - if success: - self.logger.info(f"成功保存聊天消息: {username}({player_name}): {content[:20]}...") - else: - self.logger.error(f"保存聊天消息失败: {username}({player_name}): {content[:20]}...") - - return success - - except Exception as e: - self.logger.error(f"保存聊天消息异常: {e}") - return False - - def get_chat_history(self, days: int = 3, limit: int = 500) -> List[Dict[str, Any]]: - """获取聊天历史消息""" - try: - collection = self.get_collection("chat") - - # 计算日期范围 - end_date = datetime.now() - start_date = end_date - timedelta(days=days-1) - - # 生成日期列表 - date_list = [] - current_date = start_date - while current_date <= end_date: - date_list.append(current_date.strftime("%Y-%m-%d")) - current_date += timedelta(days=1) - - # 查询这些日期的文档 - query = {"date": {"$in": date_list}} - docs = collection.find(query).sort("date", 1) - - # 收集所有消息 - all_messages = [] - for doc in docs: - messages = doc.get("messages", []) - all_messages.extend(messages) - - # 按时间戳排序 - all_messages.sort(key=lambda x: x.get("timestamp", 0)) - - # 限制数量 - if limit > 0 and len(all_messages) > limit: - all_messages = all_messages[-limit:] - - self.logger.info(f"获取聊天历史消息成功: {len(all_messages)} 条消息(最近{days}天)") - return all_messages - - except Exception as e: - self.logger.error(f"获取聊天历史消息失败: {e}") - return [] - - def get_latest_chat_message(self) -> Optional[Dict[str, Any]]: - """获取最新的一条聊天消息""" - try: - collection = self.get_collection("chat") - - # 获取最近的文档 - latest_doc = collection.find().sort("date", -1).limit(1) - - for doc in latest_doc: - messages = doc.get("messages", []) - if messages: - # 返回最后一条消息 - latest_message = messages[-1] - self.logger.info(f"获取最新聊天消息成功: {latest_message.get('username', 'N/A')}: {latest_message.get('content', '')[:20]}...") - return latest_message - - self.logger.info("没有找到聊天消息") - return None - - except Exception as e: - self.logger.error(f"获取最新聊天消息失败: {e}") - return None - - def clean_old_chat_messages(self, keep_days: int = 30) -> int: - """清理旧的聊天消息""" - try: - collection = self.get_collection("chat") - - # 计算保留的最早日期 - cutoff_date = datetime.now() - timedelta(days=keep_days) - cutoff_date_str = cutoff_date.strftime("%Y-%m-%d") - - # 删除早于cutoff_date的文档 - query = {"date": {"$lt": cutoff_date_str}} - result = collection.delete_many(query) - - deleted_count = result.deleted_count - self.logger.info(f"清理旧聊天消息完成: 删除了 {deleted_count} 个文档({keep_days}天前的消息)") - return deleted_count - - except Exception as e: - self.logger.error(f"清理旧聊天消息失败: {e}") - return 0 - #=====================聊天消息系统====================== - - def save_verification_code(self, qq_number: str, verification_code: str, expiry_time: int = 300, code_type: str = "register") -> bool: - """保存单个验证码到MongoDB""" - import time + def save_verification_code(self, qq_number: str, verification_code: str, + expiry_time: int = 300, code_type: str = "register") -> bool: + """ + 保存验证码到MongoDB + Args: + qq_number: QQ号 + verification_code: 验证码 + expiry_time: 过期时间(秒),默认5分钟 + code_type: 验证码类型,"register" 或 "reset_password" + + Returns: + bool: 保存成功返回True,否则返回False + """ try: - # 获取当前验证码数据 - codes_data = self.get_verification_codes() or {} + import time + from datetime import datetime, timedelta - # 添加新的验证码 - expire_at = time.time() + expiry_time - current_time = time.time() + collection = self.get_collection("verification_codes") - codes_data[qq_number] = { + # 计算过期时间 + expire_at = datetime.now() + timedelta(seconds=expiry_time) + + # 验证码文档 + verification_doc = { + "qq_number": qq_number, "code": verification_code, - "expire_at": expire_at, "code_type": code_type, - "created_at": current_time, + "created_at": datetime.now(), + "expire_at": expire_at, "used": False } - # 更新到MongoDB - success = self.update_verification_codes(codes_data) - if success: - self.logger.info(f"为QQ {qq_number} 保存{code_type}验证码: {verification_code}, 过期时间: {expire_at}") - return success + # 使用upsert更新或插入(覆盖同一QQ号的旧验证码) + query = {"qq_number": qq_number, "code_type": code_type} + result = collection.replace_one(query, verification_doc, upsert=True) + if result.acknowledged: + self.logger.info(f"成功保存验证码: QQ {qq_number}, 类型 {code_type}") + return True + else: + self.logger.error(f"保存验证码失败: QQ {qq_number}") + return False + except Exception as e: - self.logger.error(f"保存验证码失败: {e}") + self.logger.error(f"保存验证码异常 [QQ {qq_number}]: {e}") return False - def verify_verification_code(self, qq_number: str, input_code: str, code_type: str = "register") -> tuple[bool, str]: - """验证验证码""" - import time + def verify_verification_code(self, qq_number: str, input_code: str, + code_type: str = "register") -> tuple[bool, str]: + """ + 验证用户输入的验证码 - try: - # 获取验证码数据 - codes_data = self.get_verification_codes() - if not codes_data: - self.logger.warning(f"QQ {qq_number} 验证失败: 验证码数据不存在") - return False, "验证码不存在或已过期" + Args: + qq_number: QQ号 + input_code: 用户输入的验证码 + code_type: 验证码类型,"register" 或 "reset_password" - # 检查该QQ号是否有验证码 - if qq_number not in codes_data: - self.logger.warning(f"QQ {qq_number} 验证失败: 没有找到验证码记录") + Returns: + tuple: (验证成功, 消息) + """ + try: + from datetime import datetime + + collection = self.get_collection("verification_codes") + + # 查找验证码 + query = {"qq_number": qq_number, "code_type": code_type} + code_doc = collection.find_one(query) + + if not code_doc: return False, "验证码不存在,请重新获取" - # 获取存储的验证码信息 - code_info = codes_data[qq_number] - stored_code = code_info.get("code", "") - expire_at = code_info.get("expire_at", 0) - stored_code_type = code_info.get("code_type", "register") - is_used = code_info.get("used", False) - created_at = code_info.get("created_at", 0) + # 检查是否已使用 + if code_doc.get("used", False): + return False, "验证码已使用,请重新获取" - self.logger.info(f"QQ {qq_number} 验证码详情: 存储码={stored_code}, 输入码={input_code}, 类型={stored_code_type}, 已使用={is_used}, 创建时间={created_at}") - - # 检查验证码类型是否匹配 - if stored_code_type != code_type: - self.logger.warning(f"QQ {qq_number} 验证失败: 验证码类型不匹配,存储类型={stored_code_type}, 请求类型={code_type}") - return False, f"验证码类型不匹配,请重新获取{code_type}验证码" - - # 检查验证码是否已被使用 - if is_used: - self.logger.warning(f"QQ {qq_number} 验证失败: 验证码已被使用") - return False, "验证码已被使用,请重新获取" - - # 检查验证码是否过期 - current_time = time.time() - if current_time > expire_at: - # 移除过期的验证码 - del codes_data[qq_number] - self.update_verification_codes(codes_data) - self.logger.warning(f"QQ {qq_number} 验证失败: 验证码已过期") + # 检查是否过期 + if datetime.now() > code_doc.get("expire_at", datetime.now()): return False, "验证码已过期,请重新获取" - # 验证码比较(不区分大小写) - if input_code.upper() == stored_code.upper(): - # 验证成功,标记为已使用 - codes_data[qq_number]["used"] = True - codes_data[qq_number]["used_at"] = current_time - - success = self.update_verification_codes(codes_data) - if success: - self.logger.info(f"QQ {qq_number} 验证成功: 验证码已标记为已使用") - else: - self.logger.warning(f"标记验证码已使用时失败,但验证成功") - return True, "验证码正确" + # 验证码码 + if input_code.upper() != code_doc.get("code", "").upper(): + return False, "验证码错误,请重新输入" + + # 标记为已使用 + update_result = collection.update_one( + query, + {"$set": {"used": True, "used_at": datetime.now()}} + ) + + if update_result.acknowledged: + self.logger.info(f"验证码验证成功: QQ {qq_number}, 类型 {code_type}") + return True, "验证码验证成功" else: - self.logger.warning(f"QQ {qq_number} 验证失败: 验证码不匹配") - return False, "验证码错误" + self.logger.error(f"标记验证码已使用失败: QQ {qq_number}") + return False, "验证码验证失败" except Exception as e: - self.logger.error(f"验证验证码异常: {e}") + self.logger.error(f"验证验证码异常 [QQ {qq_number}]: {e}") return False, "验证码验证失败" def clean_expired_verification_codes(self) -> int: - """清理过期的验证码和已使用的验证码""" - import time + """ + 清理过期的验证码和已使用的验证码 + Returns: + int: 清理的验证码数量 + """ try: - codes_data = self.get_verification_codes() - if not codes_data: + from datetime import datetime, timedelta + + collection = self.get_collection("verification_codes") + + current_time = datetime.now() + one_hour_ago = current_time - timedelta(hours=1) + + # 删除条件:过期的验证码 或 已使用超过1小时的验证码 + delete_query = { + "$or": [ + {"expire_at": {"$lt": current_time}}, # 过期的 + {"used": True, "used_at": {"$lt": one_hour_ago}} # 已使用超过1小时的 + ] + } + + result = collection.delete_many(delete_query) + + if result.acknowledged: + deleted_count = result.deleted_count + self.logger.info(f"清理验证码完成: 删除了 {deleted_count} 个验证码") + return deleted_count + else: + self.logger.error("清理验证码失败") return 0 - - current_time = time.time() - removed_keys = [] - - # 找出过期的验证码和已使用的验证码(超过1小时) - for qq_number, code_info in codes_data.items(): - expire_at = code_info.get("expire_at", 0) - is_used = code_info.get("used", False) - used_at = code_info.get("used_at", 0) - should_remove = False - - # 过期的验证码 - if current_time > expire_at: - should_remove = True - self.logger.info(f"移除过期验证码: QQ {qq_number}") - - # 已使用超过1小时的验证码 - elif is_used and used_at > 0 and (current_time - used_at) > 3600: - should_remove = True - self.logger.info(f"移除已使用的验证码: QQ {qq_number}") - - if should_remove: - removed_keys.append(qq_number) - - # 移除标记的验证码 - for key in removed_keys: - del codes_data[key] - - # 保存更新后的数据 - if removed_keys: - self.update_verification_codes(codes_data) - self.logger.info(f"共清理了 {len(removed_keys)} 个验证码") - - return len(removed_keys) - except Exception as e: - self.logger.error(f"清理验证码失败: {e}") + self.logger.error(f"清理验证码异常: {e}") return 0 + #=====================验证码系统====================== + # ========================= 通用数据库操作 ========================= + + def get_player_data(self, account_id: str) -> Optional[Dict[str, Any]]: + """获取玩家数据 + + Args: + account_id: 玩家账号ID + + Returns: + Dict: 玩家数据,如果未找到返回None + """ + try: + collection = self.get_collection("playerdata") + + # 根据玩家账号查找文档 + query = {"玩家账号": account_id} + result = collection.find_one(query) + + if result: + # 移除MongoDB的_id字段 + if "_id" in result: + del result["_id"] + + # 转换datetime对象为字符串,避免JSON序列化错误 + result = self._convert_datetime_to_string(result) + + self.logger.info(f"成功获取玩家数据: {account_id}") + return result + else: + self.logger.warning(f"未找到玩家数据: {account_id}") + return None + + except Exception as e: + self.logger.error(f"获取玩家数据失败 [{account_id}]: {e}") + return None + + def save_player_data(self, account_id: str, player_data: Dict[str, Any]) -> bool: + """保存玩家数据 + + Args: + account_id: 玩家账号ID + player_data: 玩家数据 + + Returns: + bool: 是否成功 + """ + try: + collection = self.get_collection("playerdata") + + # 添加更新时间 + update_data = { + "updated_at": datetime.now(), + **player_data + } + + # 使用upsert更新或插入 + query = {"玩家账号": account_id} + result = collection.replace_one(query, update_data, upsert=True) + + if result.acknowledged: + self.logger.info(f"成功保存玩家数据: {account_id}") + return True + else: + self.logger.error(f"保存玩家数据失败: {account_id}") + return False + + except Exception as e: + self.logger.error(f"保存玩家数据异常 [{account_id}]: {e}") + return False + + def delete_player_data(self, account_id: str) -> bool: + """删除玩家数据 + + Args: + account_id: 玩家账号ID + + Returns: + bool: 是否成功 + """ + try: + collection = self.get_collection("playerdata") + + query = {"玩家账号": account_id} + result = collection.delete_one(query) + + if result.acknowledged and result.deleted_count > 0: + self.logger.info(f"成功删除玩家数据: {account_id}") + return True + else: + self.logger.warning(f"删除玩家数据失败或数据不存在: {account_id}") + return False + + except Exception as e: + self.logger.error(f"删除玩家数据异常 [{account_id}]: {e}") + return False + + def get_all_players_basic_info(self, projection: Dict[str, int] = None) -> List[Dict[str, Any]]: + """获取所有玩家的基本信息(优化版本,用于排行榜等) + + Args: + projection: 字段投影,指定需要返回的字段 + + Returns: + List: 玩家基本信息列表 + """ + try: + collection = self.get_collection("playerdata") + + # 默认投影字段(只获取必要信息) + if projection is None: + projection = { + "_id": 0, + "玩家账号": 1, + "玩家昵称": 1, + "农场名称": 1, + "等级": 1, + "钱币": 1, + "经验值": 1, + "最后登录时间": 1, + "总游玩时间": 1, + "种子仓库": 1, + "点赞系统": 1, + "体力系统.当前体力值": 1 + } + + cursor = collection.find({}, projection) + players = list(cursor) + + # 转换datetime对象为字符串 + players = [self._convert_datetime_to_string(player) for player in players] + + self.logger.info(f"成功获取 {len(players)} 个玩家的基本信息") + return players + + except Exception as e: + self.logger.error(f"获取玩家基本信息失败: {e}") + return [] + + def get_players_by_condition(self, condition: Dict[str, Any], + projection: Dict[str, int] = None, + limit: int = 0) -> List[Dict[str, Any]]: + """根据条件获取玩家数据 + + Args: + condition: 查询条件 + projection: 字段投影 + limit: 限制数量 + + Returns: + List: 符合条件的玩家数据列表 + """ + try: + collection = self.get_collection("playerdata") + + cursor = collection.find(condition, projection) + if limit > 0: + cursor = cursor.limit(limit) + + players = list(cursor) + + # 移除_id字段并转换datetime对象 + for player in players: + if "_id" in player: + del player["_id"] + player = self._convert_datetime_to_string(player) + + # 重新转换整个列表确保所有datetime都被处理 + players = [self._convert_datetime_to_string(player) for player in players] + + self.logger.info(f"根据条件查询到 {len(players)} 个玩家") + return players + + except Exception as e: + self.logger.error(f"根据条件获取玩家数据失败: {e}") + return [] + + def get_offline_players(self, offline_days: int = 3) -> List[Dict[str, Any]]: + """获取长时间离线的玩家(用于杂草生长等) + + Args: + offline_days: 离线天数阈值 + + Returns: + List: 离线玩家数据列表 + """ + try: + import time + from datetime import datetime, timedelta + + # 计算阈值时间戳 + threshold_time = datetime.now() - timedelta(days=offline_days) + + collection = self.get_collection("playerdata") + + # 查询条件:最后登录时间早于阈值 + # 注意:这里需要根据实际的时间格式进行调整 + cursor = collection.find({ + "最后登录时间": {"$exists": True} + }, { + "_id": 0, + "玩家账号": 1, + "最后登录时间": 1, + "农场土地": 1 + }) + + offline_players = [] + for player in cursor: + last_login = player.get("最后登录时间", "") + if self._is_player_offline_by_time(last_login, offline_days): + offline_players.append(player) + + # 转换datetime对象为字符串 + offline_players = [self._convert_datetime_to_string(player) for player in offline_players] + + self.logger.info(f"找到 {len(offline_players)} 个离线超过 {offline_days} 天的玩家") + return offline_players + + except Exception as e: + self.logger.error(f"获取离线玩家失败: {e}") + return [] + + def _is_player_offline_by_time(self, last_login_str: str, offline_days: int) -> bool: + """检查玩家是否离线超过指定天数""" + try: + if not last_login_str or last_login_str == "未知": + return False + + # 解析时间格式:2024年01月01日12时30分45秒 + import datetime + dt = datetime.datetime.strptime(last_login_str, "%Y年%m月%d日%H时%M分%S秒") + + # 计算离线天数 + now = datetime.datetime.now() + offline_duration = now - dt + return offline_duration.days >= offline_days + + except Exception: + return False + + def _convert_datetime_to_string(self, data: Any) -> Any: + """ + 递归转换数据中的datetime对象为字符串 + + Args: + data: 要转换的数据 + + Returns: + 转换后的数据 + """ + from datetime import datetime + + if isinstance(data, datetime): + return data.strftime("%Y年%m月%d日%H时%M分%S秒") + elif isinstance(data, dict): + return {key: self._convert_datetime_to_string(value) for key, value in data.items()} + elif isinstance(data, list): + return [self._convert_datetime_to_string(item) for item in data] + else: + return data + + def count_total_players(self) -> int: + """统计玩家总数 + + Returns: + int: 玩家总数 + """ + try: + collection = self.get_collection("playerdata") + count = collection.count_documents({}) + + self.logger.info(f"玩家总数: {count}") + return count + + except Exception as e: + self.logger.error(f"统计玩家总数失败: {e}") + return 0 + + def update_player_field(self, account_id: str, field_updates: Dict[str, Any]) -> bool: + """更新玩家的特定字段 + + Args: + account_id: 玩家账号ID + field_updates: 要更新的字段和值 + + Returns: + bool: 是否成功 + """ + try: + collection = self.get_collection("playerdata") + + # 添加更新时间 + update_data = { + "updated_at": datetime.now(), + **field_updates + } + + query = {"玩家账号": account_id} + result = collection.update_one(query, {"$set": update_data}) + + if result.acknowledged and result.matched_count > 0: + self.logger.info(f"成功更新玩家字段: {account_id}") + return True + else: + self.logger.warning(f"更新玩家字段失败或玩家不存在: {account_id}") + return False + + except Exception as e: + self.logger.error(f"更新玩家字段异常 [{account_id}]: {e}") + return False + #=====================玩家数据管理====================== + + # ========================= 验证码系统 ========================= + + def save_verification_code(self, qq_number: str, verification_code: str, + expiry_time: int = 300, code_type: str = "register") -> bool: + """ + 保存验证码到MongoDB + + Args: + qq_number: QQ号 + verification_code: 验证码 + expiry_time: 过期时间(秒),默认5分钟 + code_type: 验证码类型,"register" 或 "reset_password" + + Returns: + bool: 保存成功返回True,否则返回False + """ + try: + import time + from datetime import datetime, timedelta + + collection = self.get_collection("verification_codes") + + # 计算过期时间 + expire_at = datetime.now() + timedelta(seconds=expiry_time) + + # 验证码文档 + verification_doc = { + "qq_number": qq_number, + "code": verification_code, + "code_type": code_type, + "created_at": datetime.now(), + "expire_at": expire_at, + "used": False + } + + # 使用upsert更新或插入(覆盖同一QQ号的旧验证码) + query = {"qq_number": qq_number, "code_type": code_type} + result = collection.replace_one(query, verification_doc, upsert=True) + + if result.acknowledged: + self.logger.info(f"成功保存验证码: QQ {qq_number}, 类型 {code_type}") + return True + else: + self.logger.error(f"保存验证码失败: QQ {qq_number}") + return False + + except Exception as e: + self.logger.error(f"保存验证码异常 [QQ {qq_number}]: {e}") + return False + + def verify_verification_code(self, qq_number: str, input_code: str, + code_type: str = "register") -> tuple: + """ + 验证用户输入的验证码 + + Args: + qq_number: QQ号 + input_code: 用户输入的验证码 + code_type: 验证码类型,"register" 或 "reset_password" + + Returns: + tuple: (验证成功, 消息) + """ + try: + from datetime import datetime + + collection = self.get_collection("verification_codes") + + # 查找验证码 + query = {"qq_number": qq_number, "code_type": code_type} + code_doc = collection.find_one(query) + + if not code_doc: + return False, "验证码不存在,请重新获取" + + # 检查是否已使用 + if code_doc.get("used", False): + return False, "验证码已使用,请重新获取" + + # 检查是否过期 + if datetime.now() > code_doc.get("expire_at", datetime.now()): + return False, "验证码已过期,请重新获取" + + # 验证码码 + if input_code.upper() != code_doc.get("code", "").upper(): + return False, "验证码错误,请重新输入" + + # 标记为已使用 + update_result = collection.update_one( + query, + {"$set": {"used": True, "used_at": datetime.now()}} + ) + + if update_result.acknowledged: + self.logger.info(f"验证码验证成功: QQ {qq_number}, 类型 {code_type}") + return True, "验证码验证成功" + else: + self.logger.error(f"标记验证码已使用失败: QQ {qq_number}") + return False, "验证码验证失败" + + except Exception as e: + self.logger.error(f"验证验证码异常 [QQ {qq_number}]: {e}") + return False, "验证码验证失败" + + def clean_expired_verification_codes(self) -> int: + """ + 清理过期的验证码和已使用的验证码 + + Returns: + int: 清理的验证码数量 + """ + try: + from datetime import datetime, timedelta + + collection = self.get_collection("verification_codes") + + current_time = datetime.now() + one_hour_ago = current_time - timedelta(hours=1) + + # 删除条件:过期的验证码 或 已使用超过1小时的验证码 + delete_query = { + "$or": [ + {"expire_at": {"$lt": current_time}}, # 过期的 + {"used": True, "used_at": {"$lt": one_hour_ago}} # 已使用超过1小时的 + ] + } + + result = collection.delete_many(delete_query) + + if result.acknowledged: + deleted_count = result.deleted_count + self.logger.info(f"清理验证码完成: 删除了 {deleted_count} 个验证码") + return deleted_count + else: + self.logger.error("清理验证码失败") + return 0 + + except Exception as e: + self.logger.error(f"清理验证码异常: {e}") + return 0 + + #=====================验证码系统====================== # ========================= 通用数据库操作 ========================= @@ -735,11 +1050,14 @@ class SMYMongoDBAPI: documents = list(cursor) - # 转换ObjectId为字符串 + # 转换ObjectId为字符串并转换datetime对象 for doc in documents: if "_id" in doc: doc["_id"] = str(doc["_id"]) + # 转换datetime对象为字符串 + documents = [self._convert_datetime_to_string(doc) for doc in documents] + return documents except Exception as e: @@ -789,6 +1107,202 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"删除文档失败 [{collection_name}]: {e}") return False + + # ========================= 聊天消息管理 ========================= + + def save_chat_message(self, username: str, player_name: str, content: str) -> bool: + """ + 保存聊天消息到MongoDB(按天存储) + + Args: + username: 用户名(QQ号) + player_name: 玩家昵称 + content: 消息内容 + + Returns: + bool: 是否保存成功 + """ + try: + import time + from datetime import datetime + + collection = self.get_collection("chat") + + # 获取当前日期 + current_date = datetime.now().strftime("%Y-%m-%d") + current_time = datetime.now() + + # 创建消息对象 + message = { + "username": username, + "player_name": player_name, + "content": content, + "timestamp": time.time(), + "time_str": current_time.strftime("%Y年%m月%d日 %H:%M:%S") + } + + # 查找当天的文档 + query = {"date": current_date} + existing_doc = collection.find_one(query) + + if existing_doc: + # 如果当天的文档已存在,添加消息到messages数组 + result = collection.update_one( + query, + { + "$push": {"messages": message}, + "$set": {"updated_at": current_time} + } + ) + else: + # 如果当天的文档不存在,创建新文档 + new_doc = { + "date": current_date, + "messages": [message], + "created_at": current_time, + "updated_at": current_time + } + result = collection.insert_one(new_doc) + + if result.acknowledged: + self.logger.info(f"成功保存聊天消息: {username} - {content[:20]}...") + return True + else: + self.logger.error(f"保存聊天消息失败: {username}") + return False + + except Exception as e: + self.logger.error(f"保存聊天消息异常: {e}") + return False + + def get_chat_history(self, days: int = 3, limit: int = 500) -> List[Dict[str, Any]]: + """ + 获取聊天历史消息(从按天存储的chat集合) + + Args: + days: 获取最近几天的消息 + limit: 最大消息数量 + + Returns: + List: 聊天消息列表 + """ + try: + from datetime import datetime, timedelta + + collection = self.get_collection("chat") + + # 计算日期范围 + end_date = datetime.now() + start_date = end_date - timedelta(days=days-1) + + # 生成日期列表 + date_list = [] + current_date = start_date + while current_date <= end_date: + date_list.append(current_date.strftime("%Y-%m-%d")) + current_date += timedelta(days=1) + + # 查询条件 + query = {"date": {"$in": date_list}} + + # 获取文档 + cursor = collection.find(query).sort("date", 1) + docs = list(cursor) + + # 提取所有消息 + all_messages = [] + for doc in docs: + messages = doc.get("messages", []) + for msg in messages: + # 移除MongoDB的_id字段(如果存在) + if "_id" in msg: + del msg["_id"] + all_messages.append(msg) + + # 按时间戳排序 + all_messages.sort(key=lambda x: x.get("timestamp", 0)) + + # 限制消息数量 + if len(all_messages) > limit: + all_messages = all_messages[-limit:] + + self.logger.info(f"成功获取聊天历史: {len(all_messages)} 条消息(最近{days}天)") + return all_messages + + except Exception as e: + self.logger.error(f"获取聊天历史失败: {e}") + return [] + + def get_latest_chat_message(self) -> Optional[Dict[str, Any]]: + """ + 获取最新的聊天消息(从按天存储的chat集合) + + Returns: + Dict: 最新的聊天消息,如果没有返回None + """ + try: + collection = self.get_collection("chat") + + # 按日期降序排序,获取最新的文档 + cursor = collection.find().sort("date", -1).limit(10) # 获取最近10天的文档 + docs = list(cursor) + + latest_message = None + latest_timestamp = 0 + + # 遍历文档,找到最新的消息 + for doc in docs: + messages = doc.get("messages", []) + for msg in messages: + timestamp = msg.get("timestamp", 0) + if timestamp > latest_timestamp: + latest_timestamp = timestamp + latest_message = msg.copy() + # 移除MongoDB的_id字段(如果存在) + if "_id" in latest_message: + del latest_message["_id"] + + if latest_message: + self.logger.info(f"成功获取最新聊天消息: {latest_message.get('content', '')[:20]}...") + return latest_message + else: + self.logger.info("暂无聊天消息") + return None + + except Exception as e: + self.logger.error(f"获取最新聊天消息失败: {e}") + return None + + def clean_old_chat_messages(self, keep_days: int = 30) -> int: + """ + 清理旧的聊天消息(从按天存储的chat集合) + + Args: + keep_days: 保留最近几天的消息 + + Returns: + int: 删除的文档数量 + """ + try: + from datetime import datetime, timedelta + + collection = self.get_collection("chat") + + # 计算删除日期点 + cutoff_date = datetime.now() - timedelta(days=keep_days) + cutoff_date_str = cutoff_date.strftime("%Y-%m-%d") + + # 删除旧文档 + query = {"date": {"$lt": cutoff_date_str}} + result = collection.delete_many(query) + + deleted_count = result.deleted_count + self.logger.info(f"成功清理 {deleted_count} 个旧聊天文档(保留最近{keep_days}天)") + return deleted_count + + except Exception as e: + self.logger.error(f"清理旧聊天消息失败: {e}") + return 0 # ========================= 测试和使用示例 ========================= diff --git a/Server/TCPGameServer.py b/Server/TCPGameServer.py index e48d7ac..b5850ac 100644 --- a/Server/TCPGameServer.py +++ b/Server/TCPGameServer.py @@ -21,20 +21,11 @@ from ConsoleCommandsAPI import ConsoleCommandsAPI #导入控制台命令API模 - 消息类型:请求/响应模式 ==================================================================== """ - -# ============================================================================ -# 服务器配置参数 -# ============================================================================ server_host: str = "0.0.0.0" server_port: int = 6060 buffer_size: int = 4096 server_version: str = "2.0.1" - - -# ============================================================================ -# TCP游戏服务器类 -# ============================================================================ class TCPGameServer(TCPServer): """ @@ -69,6 +60,9 @@ class TCPGameServer(TCPServer): self.crop_data_cache_time = 0 self.cache_expire_duration = 300 # 缓存过期时间5分钟 + # 偷菜免被发现临时计数器 {玩家名: {目标玩家名: 剩余免被发现次数}} + self.steal_immunity_counters = {} + self.log('INFO', f"萌芽农场TCP游戏服务器初始化完成 - 版本: {server_version}", 'SERVER') # 启动定时器 @@ -82,8 +76,15 @@ class TCPGameServer(TCPServer): """初始化MongoDB API连接""" try: # 根据配置决定使用测试环境还是生产环境 - # 这里默认使用测试环境,实际部署时可以修改为 "production" - environment = "test" # 或者从配置文件读取 + # 检查是否在Docker容器中或生产环境 + import os + if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true': + environment = "production" + else: + environment = "test" + + # 保存环境信息供其他组件使用 + self.environment = environment self.mongo_api = SMYMongoDBAPI(environment) if self.mongo_api.is_connected(): @@ -244,6 +245,8 @@ class TCPGameServer(TCPServer): # 清理用户数据 if client_id in self.user_data: + # 清理偷菜免被发现计数器 + self._clear_player_steal_immunity(username) del self.user_data[client_id] self.log('INFO', f"用户 {username} 已离开游戏", 'SERVER') @@ -283,32 +286,45 @@ class TCPGameServer(TCPServer): #=================================数据管理方法==================================== #加载玩家数据 def load_player_data(self, account_id): - """从文件加载玩家数据""" - file_path = os.path.join("game_saves", f"{account_id}.json") - + """从MongoDB加载玩家数据""" try: - if os.path.exists(file_path): - with open(file_path, 'r', encoding='utf-8') as file: - player_data = json.load(file) + if not self.use_mongodb or not self.mongo_api: + self.log('ERROR', 'MongoDB未配置或不可用,无法加载玩家数据', 'SERVER') + return None + + player_data = self.mongo_api.get_player_data(account_id) + if player_data: return player_data - return None + else: + self.log('DEBUG', f"MongoDB中未找到玩家 {account_id} 的数据", 'SERVER') + return None + except Exception as e: self.log('ERROR', f"读取玩家 {account_id} 的数据时出错: {str(e)}", 'SERVER') return None #保存玩家数据 def save_player_data(self, account_id, player_data): - """保存玩家数据到文件""" - file_path = os.path.join("game_saves", f"{account_id}.json") - + """保存玩家数据到MongoDB""" try: - with open(file_path, 'w', encoding='utf-8') as file: - json.dump(player_data, file, indent=2, ensure_ascii=False) - return True + if not self.use_mongodb or not self.mongo_api: + self.log('ERROR', 'MongoDB未配置或不可用,无法保存玩家数据', 'SERVER') + return False + + success = self.mongo_api.save_player_data(account_id, player_data) + if success: + return True + else: + self.log('ERROR', f"MongoDB保存失败: {account_id}", 'SERVER') + return False + except Exception as e: self.log('ERROR', f"保存玩家 {account_id} 的数据时出错: {str(e)}", 'SERVER') return False + #加载玩家数据(兼容旧方法名) + + #加载玩家数据 def _load_player_data_with_check(self, client_id, action_type=None): """加载玩家数据并进行错误检查的通用方法""" @@ -335,7 +351,7 @@ class TCPGameServer(TCPServer): #加载作物配置数据(优化版本) def _load_crop_data(self): - """加载作物配置数据(优先MongoDB,带缓存优化)""" + """加载作物配置数据(从MongoDB,带缓存优化)""" current_time = time.time() # 检查缓存是否有效 @@ -344,29 +360,22 @@ class TCPGameServer(TCPServer): return self.crop_data_cache # 缓存过期或不存在,重新加载 - # 优先尝试从MongoDB加载 - if self.use_mongodb and self.mongo_api: - try: - crop_data = self.mongo_api.get_crop_data_config() - if crop_data: - self.crop_data_cache = crop_data - self.crop_data_cache_time = current_time - self.log('INFO', "成功从MongoDB加载作物数据配置", 'SERVER') - return self.crop_data_cache - else: - self.log('WARNING', "MongoDB中未找到作物数据配置,尝试JSON文件", 'SERVER') - except Exception as e: - self.log('ERROR', f"从MongoDB加载作物数据失败: {str(e)},尝试JSON文件", 'SERVER') - - # MongoDB失败或不可用,尝试从JSON文件加载 + if not self.use_mongodb or not self.mongo_api: + self.log('ERROR', 'MongoDB未配置或不可用,无法加载作物配置数据', 'SERVER') + return {} + try: - with open("config/crop_data.json", 'r', encoding='utf-8') as file: - self.crop_data_cache = json.load(file) + crop_data = self.mongo_api.get_crop_data_config() + if crop_data: + self.crop_data_cache = crop_data self.crop_data_cache_time = current_time - self.log('INFO', "成功从JSON文件加载作物数据配置", 'SERVER') + self.log('INFO', "成功从MongoDB加载作物数据配置", 'SERVER') return self.crop_data_cache + else: + self.log('ERROR', "MongoDB中未找到作物数据配置", 'SERVER') + return {} except Exception as e: - self.log('ERROR', f"无法加载作物数据: {str(e)}", 'SERVER') + self.log('ERROR', f"从MongoDB加载作物数据失败: {str(e)}", 'SERVER') return {} #更新玩家登录时间 @@ -656,6 +665,8 @@ class TCPGameServer(TCPServer): return self._handle_crop_data_request(client_id) elif message_type == "request_item_config":#请求道具配置数据 return self._handle_item_config_request(client_id) + elif message_type == "request_pet_config":#请求宠物配置数据 + return self._handle_pet_config_request(client_id) elif message_type == "visit_player":#拜访其他玩家农场 return self._handle_visit_player_request(client_id, message) elif message_type == "return_my_farm":#返回我的农场 @@ -712,6 +723,8 @@ class TCPGameServer(TCPServer): return self._handle_buy_store_booth(client_id, message) elif message_type == "save_game_settings":#保存游戏设置 return self._handle_save_game_settings(client_id, message) + elif message_type == "pet_battle_result":#宠物对战结果 + return self._handle_pet_battle_result(client_id, message) #--------------------------------------------------------------------------- elif message_type == "message":#处理聊天消息(暂未实现) @@ -951,29 +964,20 @@ class TCPGameServer(TCPServer): #辅助函数-创建新用户 def _create_new_user(self, client_id, username, password, farm_name, player_name): - """创建新用户(优先从MongoDB加载模板)""" + """创建新用户(从MongoDB加载模板)""" try: - # 优先从MongoDB加载初始玩家数据模板 - player_data = None - if self.use_mongodb and self.mongo_api: - try: - player_data = self.mongo_api.get_initial_player_data_template() - if player_data: - self.log('INFO', "成功从MongoDB加载初始玩家数据模板", 'SERVER') - else: - self.log('WARNING', "MongoDB中未找到初始玩家数据模板,尝试从JSON文件加载", 'SERVER') - except Exception as e: - self.log('ERROR', f"从MongoDB加载初始玩家数据模板失败: {str(e)},尝试从JSON文件加载", 'SERVER') - - # MongoDB加载失败或不可用,从JSON文件加载 - if not player_data: - template_path = os.path.join("config", "initial_player_data_template.json") - if not os.path.exists(template_path): - return self._send_register_error(client_id, "服务器配置错误,无法注册新用户") - - with open(template_path, 'r', encoding='utf-8') as file: - player_data = json.load(file) - self.log('INFO', "成功从JSON文件加载初始玩家数据模板", 'SERVER') + # 从MongoDB加载初始玩家数据模板 + if not self.use_mongodb or not self.mongo_api: + return self._send_register_error(client_id, "MongoDB未配置或不可用,无法注册新用户") + + try: + player_data = self.mongo_api.get_initial_player_data_template() + if not player_data: + return self._send_register_error(client_id, "MongoDB中未找到初始玩家数据模板,无法注册新用户") + self.log('INFO', "成功从MongoDB加载初始玩家数据模板", 'SERVER') + except Exception as e: + self.log('ERROR', f"从MongoDB加载初始玩家数据模板失败: {str(e)}", 'SERVER') + return self._send_register_error(client_id, f"加载初始玩家数据模板失败: {str(e)}") # 更新玩家基本信息 current_time = datetime.datetime.now() @@ -993,10 +997,9 @@ class TCPGameServer(TCPServer): # 确保必要字段存在 self._ensure_player_data_fields(player_data) - # 保存新用户数据 - file_path = os.path.join("game_saves", f"{username}.json") - with open(file_path, 'w', encoding='utf-8') as file: - json.dump(player_data, file, indent=2, ensure_ascii=False) + # 保存新用户数据到MongoDB + if not self.save_player_data(username, player_data): + return self._send_register_error(client_id, "保存用户数据失败,注册失败") self.log('INFO', f"用户 {username} 注册成功,注册时间: {time_str},享受3天新玩家10倍生长速度奖励", 'SERVER') @@ -1449,11 +1452,11 @@ class TCPGameServer(TCPServer): } }) - #辅助函数-处理偷菜逻辑(访问模式下收获其他玩家作物的操作)(优化版本) + #辅助函数-处理偷菜逻辑(访问模式下收获其他玩家作物的操作) def _process_steal_crop_optimized(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, crop_data): - """处理偷菜逻辑(收益给当前玩家,清空目标玩家的作物)(优化版本)""" + """处理偷菜逻辑(收益给当前玩家,清空目标玩家的作物)""" # 偷菜体力值消耗 - stamina_cost = 2 + stamina_cost = 1 # 检查并更新当前玩家的体力值 self._check_and_update_stamina(current_player_data) @@ -1462,17 +1465,24 @@ class TCPGameServer(TCPServer): if not self._check_stamina_sufficient(current_player_data, stamina_cost): return self._send_action_error(client_id, "harvest_crop", f"体力值不足,偷菜需要 {stamina_cost} 点体力,当前体力:{current_player_data.get('体力值', 0)}") - # 检查是否被巡逻宠物发现(调试:100%概率) + # 检查是否被巡逻宠物发现(30%概率) patrol_pets = target_player_data.get("巡逻宠物", []) if patrol_pets and len(patrol_pets) > 0: - # 100%概率被发现(调试用) - import random - if random.random() <= 1.0: - # 被巡逻宠物发现了! - return self._handle_steal_caught_by_patrol( - client_id, current_player_data, current_username, - target_player_data, target_username, patrol_pets[0] - ) + # 先检查是否有免被发现次数 + immunity_count = self._get_steal_immunity_count(current_username, target_username) + if immunity_count > 0: + # 有免被发现次数,消耗一次 + self._consume_steal_immunity(current_username, target_username) + self.log('INFO', f"玩家 {current_username} 使用免被发现次数偷菜 {target_username},剩余次数:{immunity_count - 1}", 'SERVER') + else: + # 30%概率被发现 + import random + if random.random() <= 0.3: + # 被巡逻宠物发现了! + return self._handle_steal_caught_by_patrol( + client_id, current_player_data, current_username, + target_player_data, target_username, patrol_pets[0] + ) # 获取作物类型和基本信息 crop_type = target_lot["crop_type"] @@ -1571,7 +1581,7 @@ class TCPGameServer(TCPServer): "钱币": current_player_data["钱币"], "经验值": current_player_data["经验值"], "等级": current_player_data["等级"], - "体力值": current_player_data["体力值"], + "体力值": current_player_data.get("体力系统", {}).get("当前体力值", 20), "种子仓库": current_player_data.get("种子仓库", []), "作物仓库": current_player_data.get("作物仓库", []) } @@ -1647,16 +1657,13 @@ class TCPGameServer(TCPServer): """根据巡逻宠物ID获取完整宠物数据""" pet_bag = player_data.get("宠物背包", []) for pet in pet_bag: - if pet.get("基本信息", {}).get("宠物ID", "") == patrol_pet_id: + if pet.get("pet_id", "") == patrol_pet_id: # 添加场景路径 import copy pet_data = copy.deepcopy(pet) - pet_type = pet.get("基本信息", {}).get("宠物类型", "") - pet_configs = self._load_pet_config() - if pet_type in pet_configs: - pet_data["场景路径"] = pet_configs[pet_type].get("场景路径", "res://Scene/Pet/PetBase.tscn") - else: - pet_data["场景路径"] = "res://Scene/Pet/PetBase.tscn" + # 直接从pet_image字段获取场景路径 + scene_path = pet.get("pet_image", "res://Scene/Pet/PetBase.tscn") + pet_data["场景路径"] = scene_path return pet_data return None @@ -1665,16 +1672,13 @@ class TCPGameServer(TCPServer): """根据出战宠物ID获取完整宠物数据""" pet_bag = player_data.get("宠物背包", []) for pet in pet_bag: - if pet.get("基本信息", {}).get("宠物ID", "") == battle_pet_id: + if pet.get("pet_id", "") == battle_pet_id: # 添加场景路径 import copy pet_data = copy.deepcopy(pet) - pet_type = pet.get("基本信息", {}).get("宠物类型", "") - pet_configs = self._load_pet_config() - if pet_type in pet_configs: - pet_data["场景路径"] = pet_configs[pet_type].get("场景路径", "res://Scene/Pet/PetBase.tscn") - else: - pet_data["场景路径"] = "res://Scene/Pet/PetBase.tscn" + # 直接从pet_image字段获取场景路径 + scene_path = pet.get("pet_image", "res://Scene/Pet/PetBase.tscn") + pet_data["场景路径"] = scene_path return pet_data return None @@ -1842,11 +1846,6 @@ class TCPGameServer(TCPServer): affected_players = 0 total_weeds_added = 0 - # 获取所有玩家存档文件 - game_saves_dir = "game_saves" - if not os.path.exists(game_saves_dir): - return - # 获取作物数据以验证杂草类型 crop_data = self._load_crop_data() if not crop_data: @@ -1863,32 +1862,64 @@ class TCPGameServer(TCPServer): self.log('WARNING', "没有找到可用的杂草类型,跳过杂草检查", 'SERVER') return - # 遍历所有玩家文件 - for filename in os.listdir(game_saves_dir): - if not filename.endswith('.json'): - continue + # 优先使用MongoDB获取离线玩家 + if self.use_mongodb and self.mongo_api: + offline_players = self.mongo_api.get_offline_players(self.offline_threshold_days) - account_id = filename[:-5] # 移除.json后缀 - - try: - # 加载玩家数据 - player_data = self.load_player_data(account_id) - if not player_data: + for player_data in offline_players: + account_id = player_data.get("玩家账号") + if not account_id: continue - # 检查玩家是否长时间离线 - if self._is_player_long_offline(player_data, current_time): + try: + # 获取完整玩家数据 + full_player_data = self.mongo_api.get_player_data(account_id) + if not full_player_data: + continue + # 为该玩家的空地生长杂草 - weeds_added = self._grow_weeds_for_player(player_data, account_id, available_weeds) + weeds_added = self._grow_weeds_for_player(full_player_data, account_id, available_weeds) if weeds_added > 0: affected_players += 1 total_weeds_added += weeds_added # 保存玩家数据 - self.save_player_data(account_id, player_data) + self.mongo_api.save_player_data(account_id, full_player_data) - except Exception as e: - self.log('ERROR', f"处理玩家 {account_id} 的杂草生长时出错: {str(e)}", 'SERVER') - continue + except Exception as e: + self.log('ERROR', f"处理玩家 {account_id} 的杂草生长时出错: {str(e)}", 'SERVER') + continue + else: + # 降级到文件系统 + game_saves_dir = "game_saves" + if not os.path.exists(game_saves_dir): + return + + # 遍历所有玩家文件 + for filename in os.listdir(game_saves_dir): + if not filename.endswith('.json'): + continue + + account_id = filename[:-5] # 移除.json后缀 + + try: + # 加载玩家数据 + player_data = self.load_player_data(account_id) + if not player_data: + continue + + # 检查玩家是否长时间离线 + if self._is_player_long_offline(player_data, current_time): + # 为该玩家的空地生长杂草 + weeds_added = self._grow_weeds_for_player(player_data, account_id, available_weeds) + if weeds_added > 0: + affected_players += 1 + total_weeds_added += weeds_added + # 保存玩家数据 + self.save_player_data(account_id, player_data) + + except Exception as e: + self.log('ERROR', f"处理玩家 {account_id} 的杂草生长时出错: {str(e)}", 'SERVER') + continue self.log('INFO', f"杂草检查完成,共为 {affected_players} 个玩家的农场添加了 {total_weeds_added} 个杂草", 'SERVER') @@ -1974,6 +2005,64 @@ class TCPGameServer(TCPServer): +#==========================偷菜免被发现计数器管理========================== + def _get_steal_immunity_count(self, player_name, target_player_name): + """获取玩家对目标玩家的免被发现次数""" + return self.steal_immunity_counters.get(player_name, {}).get(target_player_name, 0) + + def _consume_steal_immunity(self, player_name, target_player_name): + """消耗一次免被发现次数""" + if player_name not in self.steal_immunity_counters: + return False + + if target_player_name not in self.steal_immunity_counters[player_name]: + return False + + if self.steal_immunity_counters[player_name][target_player_name] > 0: + self.steal_immunity_counters[player_name][target_player_name] -= 1 + + # 如果计数器归零,清理该条目 + if self.steal_immunity_counters[player_name][target_player_name] == 0: + del self.steal_immunity_counters[player_name][target_player_name] + + # 如果该玩家没有其他计数器,清理玩家条目 + if not self.steal_immunity_counters[player_name]: + del self.steal_immunity_counters[player_name] + + return True + + return False + + def _set_steal_immunity(self, player_name, target_player_name, count=3): + """设置玩家对目标玩家的免被发现次数""" + if player_name not in self.steal_immunity_counters: + self.steal_immunity_counters[player_name] = {} + + self.steal_immunity_counters[player_name][target_player_name] = count + self.log('INFO', f"为玩家 {player_name} 设置对 {target_player_name} 的免被发现次数: {count}", 'SERVER') + + def _clear_player_steal_immunity(self, player_name): + """清理玩家的所有免被发现计数器""" + if player_name in self.steal_immunity_counters: + del self.steal_immunity_counters[player_name] + self.log('INFO', f"清理玩家 {player_name} 的所有免被发现计数器", 'SERVER') + + def _clear_target_steal_immunity(self, player_name, target_player_name): + """清理玩家对特定目标的免被发现计数器""" + if player_name in self.steal_immunity_counters: + if target_player_name in self.steal_immunity_counters[player_name]: + del self.steal_immunity_counters[player_name][target_player_name] + + # 如果该玩家没有其他计数器,清理玩家条目 + if not self.steal_immunity_counters[player_name]: + del self.steal_immunity_counters[player_name] + + self.log('INFO', f"清理玩家 {player_name} 对 {target_player_name} 的免被发现计数器", 'SERVER') + +#==========================偷菜免被发现计数器管理========================== + + + #==========================种植作物处理========================== #处理种植作物请求 def _handle_plant_crop(self, client_id, message): @@ -2204,14 +2293,9 @@ class TCPGameServer(TCPServer): return {"success": False, "message": "该宠物不存在"} pet_info = pet_config[pet_name] - purchase_info = pet_info.get("购买信息", {}) - # 检查宠物是否可购买 - if not purchase_info.get("能否购买", False): - return {"success": False, "message": "该宠物不可购买"} - - # 验证价格 - actual_cost = purchase_info.get("购买价格", 0) + # 从配置中获取宠物价格 + actual_cost = pet_info.get("cost", 1000) # 默认价格1000 if pet_cost != actual_cost: return {"success": False, "message": f"宠物价格验证失败,实际价格为{actual_cost}元"} @@ -2224,13 +2308,12 @@ class TCPGameServer(TCPServer): #处理宠物购买逻辑 def _process_pet_purchase(self, client_id, player_data, username, pet_name, pet_info): """处理宠物购买逻辑""" - purchase_info = pet_info.get("购买信息", {}) - pet_cost = purchase_info.get("购买价格", 0) + pet_cost = pet_info.get("cost", 1000) # 从配置中获取价格,默认1000 # 检查玩家金钱 if player_data["钱币"] < pet_cost: return self._send_action_error(client_id, "buy_pet", - f"金钱不足,无法购买此宠物。需要{pet_cost}元,当前只有{player_data['money']}元") + f"金钱不足,无法购买此宠物。需要{pet_cost}元,当前只有{player_data['钱币']}元") # 扣除金钱并添加宠物 player_data["钱币"] -= pet_cost @@ -2268,16 +2351,15 @@ class TCPGameServer(TCPServer): # 生成唯一ID和设置基本信息 unique_id = str(int(time.time() * 1000)) now = datetime.datetime.now() - birthday = f"{now.year}年{now.month}月{now.day}日{now.hour}时{now.minute}分{now.second}秒" + birthday = f"{now.year}-{now.month:02d}-{now.day:02d}" - if "基本信息" in pet_instance: - pet_instance["基本信息"].update({ - "宠物主人": username, - "宠物ID": unique_id, - "宠物名称": f"{username}的{pet_name}", - "生日": birthday, - "年龄": 0 - }) + # 新格式:直接在根级别设置属性 + pet_instance.update({ + "pet_id": unique_id, + "pet_name": f"{username}的{pet_name}", + "pet_birthday": birthday, + "pet_owner": username + }) return pet_instance @@ -2286,36 +2368,29 @@ class TCPGameServer(TCPServer): """检查玩家是否已拥有指定类型的宠物""" pet_bag = player_data.get("宠物背包", []) for pet in pet_bag: - basic_info = pet.get("基本信息", {}) - pet_type = basic_info.get("宠物类型", "") + pet_type = pet.get("pet_type", "") if pet_type == pet_name: return True return False #加载宠物配置数据 def _load_pet_config(self): - """优先从MongoDB加载宠物配置数据,失败时回退到JSON文件""" + """从MongoDB加载宠物配置数据""" try: - # 优先从MongoDB加载 - if hasattr(self, 'mongo_api') and self.mongo_api: - config = self.mongo_api.get_pet_config() - if config: - self.log('INFO', "成功从MongoDB加载宠物配置", 'SERVER') - return config - else: - self.log('WARNING', "MongoDB中未找到宠物配置,回退到JSON文件", 'SERVER') - - # 回退到JSON文件 - with open("config/pet_data.json", 'r', encoding='utf-8') as file: - config = json.load(file) - self.log('INFO', "从JSON文件加载宠物配置", 'SERVER') - return config + if not hasattr(self, 'mongo_api') or not self.mongo_api: + self.log('ERROR', 'MongoDB未配置或不可用,无法加载宠物配置数据', 'SERVER') + return {} + + config = self.mongo_api.get_pet_config() + if config: + self.log('INFO', "成功从MongoDB加载宠物配置", 'SERVER') + return config + else: + self.log('ERROR', "MongoDB中未找到宠物配置", 'SERVER') + return {} - except json.JSONDecodeError as e: - self.log('ERROR', f"宠物配置JSON解析错误: {str(e)}", 'SERVER') - return {} except Exception as e: - self.log('ERROR', f"加载宠物配置失败: {str(e)}", 'SERVER') + self.log('ERROR', f"从MongoDB加载宠物配置失败: {str(e)}", 'SERVER') return {} # 将巡逻宠物ID转换为完整宠物数据 @@ -2327,17 +2402,14 @@ class TCPGameServer(TCPServer): for patrol_pet_id in patrol_pets_ids: for pet in pet_bag: - if pet.get("基本信息", {}).get("宠物ID", "") == patrol_pet_id: + if pet.get("pet_id", "") == patrol_pet_id: # 为巡逻宠物添加场景路径 import copy patrol_pet_data = copy.deepcopy(pet) - # 根据宠物类型获取场景路径 - pet_type = pet.get("基本信息", {}).get("宠物类型", "") - pet_configs = self._load_pet_config() - if pet_type in pet_configs: - scene_path = pet_configs[pet_type].get("场景路径", "") - patrol_pet_data["场景路径"] = scene_path + # 直接从pet_image字段获取场景路径 + scene_path = pet.get("pet_image", "") + patrol_pet_data["场景路径"] = scene_path patrol_pets_data.append(patrol_pet_data) break @@ -2353,17 +2425,14 @@ class TCPGameServer(TCPServer): for battle_pet_id in battle_pets_ids: for pet in pet_bag: - if pet.get("基本信息", {}).get("宠物ID", "") == battle_pet_id: + if pet.get("pet_id", "") == battle_pet_id: # 为出战宠物添加场景路径 import copy battle_pet_data = copy.deepcopy(pet) - # 根据宠物类型获取场景路径 - pet_type = pet.get("基本信息", {}).get("宠物类型", "") - pet_configs = self._load_pet_config() - if pet_type in pet_configs: - scene_path = pet_configs[pet_type].get("场景路径", "") - battle_pet_data["场景路径"] = scene_path + # 直接从pet_image字段获取场景路径 + scene_path = pet.get("pet_image", "") + battle_pet_data["场景路径"] = scene_path battle_pets_data.append(battle_pet_data) break @@ -2405,14 +2474,13 @@ class TCPGameServer(TCPServer): pet_found = False for pet in pet_bag: - basic_info = pet.get("基本信息", {}) - if basic_info.get("宠物ID", "") == pet_id: + if pet.get("pet_id", "") == pet_id: # 检查宠物主人是否正确 - if basic_info.get("宠物主人", "") != username: + if pet.get("pet_owner", "") != username: return self._send_action_error(client_id, "rename_pet", "你不是该宠物的主人") # 更新宠物名字 - basic_info["宠物名称"] = new_name + pet["pet_name"] = new_name pet_found = True break @@ -2468,7 +2536,7 @@ class TCPGameServer(TCPServer): # 查找目标宠物 target_pet = None for pet in pet_bag: - if pet.get("基本信息", {}).get("宠物ID", "") == pet_id: + if pet.get("pet_id", "") == pet_id: target_pet = pet break @@ -2476,17 +2544,16 @@ class TCPGameServer(TCPServer): return self._send_action_error(client_id, "set_patrol_pet", "未找到指定的宠物") # 检查宠物主人是否正确 - basic_info = target_pet.get("基本信息", {}) - if basic_info.get("宠物主人", "") != username: + if target_pet.get("pet_owner", "") != username: return self._send_action_error(client_id, "set_patrol_pet", "你不是该宠物的主人") - pet_name = basic_info.get("宠物名称", basic_info.get("宠物类型", "未知宠物")) + pet_name = target_pet.get("pet_name", target_pet.get("pet_type", "未知宠物")) if is_patrolling: # 添加到巡逻列表 - # 检查巡逻宠物数量限制(最多3个) - if len(patrol_pets) >= 3: - return self._send_action_error(client_id, "set_patrol_pet", "最多只能设置3个巡逻宠物") + # 检查巡逻宠物数量限制(最多4个) + if len(patrol_pets) >= 4: + return self._send_action_error(client_id, "set_patrol_pet", "最多只能设置4个巡逻宠物") # 检查是否已在巡逻列表中(现在只检查ID) for patrol_pet_id in patrol_pets: @@ -2519,17 +2586,14 @@ class TCPGameServer(TCPServer): patrol_pets_data = [] for patrol_pet_id in patrol_pets: for pet in pet_bag: - if pet.get("基本信息", {}).get("宠物ID", "") == patrol_pet_id: + if pet.get("pet_id", "") == patrol_pet_id: # 为巡逻宠物添加场景路径 import copy patrol_pet_data = copy.deepcopy(pet) - # 根据宠物类型获取场景路径 - pet_type = pet.get("基本信息", {}).get("宠物类型", "") - pet_configs = self._load_pet_config() - if pet_type in pet_configs: - scene_path = pet_configs[pet_type].get("场景路径", "") - patrol_pet_data["场景路径"] = scene_path + # 新格式中场景路径已经在pet_image字段中 + if "pet_image" in pet: + patrol_pet_data["scene_path"] = pet["pet_image"] patrol_pets_data.append(patrol_pet_data) break @@ -2576,14 +2640,14 @@ class TCPGameServer(TCPServer): # 查找宠物是否在背包中 target_pet = None for pet in pet_bag: - if pet.get("基本信息", {}).get("宠物ID", "") == pet_id: + if pet.get("pet_id", "") == pet_id: target_pet = pet break if not target_pet: return self._send_action_error(client_id, "set_battle_pet", f"宠物背包中找不到ID为 {pet_id} 的宠物") - pet_name = target_pet.get("基本信息", {}).get("宠物名称", "未知宠物") + pet_name = target_pet.get("pet_name", "未知宠物") if is_battle: # 添加到出战列表 @@ -2595,9 +2659,9 @@ class TCPGameServer(TCPServer): if pet_id in patrol_pets: return self._send_action_error(client_id, "set_battle_pet", f"{pet_name} 正在巡逻,不能同时设置为出战宠物") - # 检查出战宠物数量限制(最多1个) - if len(battle_pets) >= 1: - return self._send_action_error(client_id, "set_battle_pet", "最多只能设置1个出战宠物") + # 检查出战宠物数量限制(最多4个) + if len(battle_pets) >= 4: + return self._send_action_error(client_id, "set_battle_pet", "最多只能设置4个出战宠物") # 添加到出战列表 battle_pets.append(pet_id) @@ -2623,18 +2687,14 @@ class TCPGameServer(TCPServer): battle_pets_data = [] for battle_pet_id in battle_pets: for pet in pet_bag: - if pet.get("基本信息", {}).get("宠物ID", "") == battle_pet_id: + if pet.get("pet_id", "") == battle_pet_id: # 为出战宠物添加场景路径 import copy battle_pet_data = copy.deepcopy(pet) - # 根据宠物类型获取场景路径 - pet_type = pet.get("基本信息", {}).get("宠物类型", "") - pet_configs = self._load_pet_config() - if pet_configs and pet_type in pet_configs: - battle_pet_data["场景路径"] = pet_configs[pet_type].get("场景路径", "res://Scene/Pet/PetBase.tscn") - else: - battle_pet_data["场景路径"] = "res://Scene/Pet/PetBase.tscn" + # 新格式中场景路径已经在pet_image字段中 + if "pet_image" in pet: + battle_pet_data["scene_path"] = pet["pet_image"] battle_pets_data.append(battle_pet_data) break @@ -2673,6 +2733,8 @@ class TCPGameServer(TCPServer): new_intimacy = message.get("new_intimacy", 0) level_ups = message.get("level_ups", 0) level_bonus_multiplier = message.get("level_bonus_multiplier", 1.0) + is_steal_battle = message.get("is_steal_battle", False) + battle_winner = message.get("battle_winner", "") if not pet_id or not attacker_name: return self._send_action_error(client_id, "update_battle_pet_data", "无效的宠物ID或进攻者名称") @@ -2688,6 +2750,14 @@ class TCPGameServer(TCPServer): new_intimacy, level_ups, level_bonus_multiplier) if success: + # 检查是否是偷菜对战且玩家获胜,如果是则设置免被发现计数器 + if is_steal_battle and battle_winner == "team1": + # 获取当前访问的目标玩家名称(从客户端连接信息中获取) + target_player_name = self.user_data.get(client_id, {}).get("visiting_target", "") + if target_player_name: + self._set_steal_immunity(attacker_name, target_player_name, 3) + self.log('INFO', f"玩家 {attacker_name} 战胜巡逻宠物,获得对 {target_player_name} 的3次免被发现机会", 'SERVER') + # 保存玩家数据 self.save_player_data(attacker_name, player_data) @@ -2719,7 +2789,7 @@ class TCPGameServer(TCPServer): # 查找指定宠物 target_pet = None for pet in player_data["宠物背包"]: - if pet.get("基本信息", {}).get("宠物ID") == pet_id: + if pet.get("pet_id") == pet_id: target_pet = pet break @@ -2727,38 +2797,28 @@ class TCPGameServer(TCPServer): return False # 更新等级经验数据 - level_exp_data = target_pet.setdefault("等级经验", {}) - level_exp_data["宠物等级"] = new_level - level_exp_data["当前经验"] = new_experience - level_exp_data["最大经验"] = new_max_experience - level_exp_data["亲密度"] = new_intimacy + target_pet["pet_level"] = new_level + target_pet["pet_experience"] = new_experience + target_pet["pet_max_experience"] = new_max_experience + target_pet["pet_intimacy"] = new_intimacy # 如果有升级,更新属性 if level_ups > 0: - health_defense_data = target_pet.setdefault("生命与防御", {}) - # 计算升级后的属性(每级10%加成) - old_max_health = health_defense_data.get("最大生命值", 100.0) - old_max_shield = health_defense_data.get("最大护盾值", 0.0) - old_max_armor = health_defense_data.get("最大护甲值", 100.0) + old_max_health = target_pet.get("pet_max_health", 100.0) + old_max_armor = target_pet.get("pet_max_armor", 100.0) + old_attack_damage = target_pet.get("pet_attack_damage", 20.0) # 应用升级加成 new_max_health = old_max_health * level_bonus_multiplier - new_max_shield = old_max_shield * level_bonus_multiplier new_max_armor = old_max_armor * level_bonus_multiplier - - health_defense_data["最大生命值"] = new_max_health - health_defense_data["当前生命值"] = new_max_health # 升级回满血 - health_defense_data["最大护盾值"] = new_max_shield - health_defense_data["当前护盾值"] = new_max_shield # 升级回满护盾 - health_defense_data["最大护甲值"] = new_max_armor - health_defense_data["当前护甲值"] = new_max_armor # 升级回满护甲 - - # 更新攻击属性 - attack_data = target_pet.setdefault("基础攻击属性", {}) - old_attack_damage = attack_data.get("基础攻击伤害", 20.0) new_attack_damage = old_attack_damage * level_bonus_multiplier - attack_data["基础攻击伤害"] = new_attack_damage + + target_pet["pet_max_health"] = new_max_health + target_pet["pet_current_health"] = new_max_health # 升级回满血 + target_pet["pet_max_armor"] = new_max_armor + target_pet["pet_current_armor"] = new_max_armor # 升级回满护甲 + target_pet["pet_attack_damage"] = new_attack_damage return True #==========================更新宠物对战数据处理========================== @@ -2810,9 +2870,9 @@ class TCPGameServer(TCPServer): target_pet = None for pet in pet_bag: - if pet.get("基本信息", {}).get("宠物ID", "") == pet_id: + if pet.get("pet_id", "") == pet_id: # 检查宠物主人是否正确 - if pet.get("基本信息", {}).get("宠物主人", "") != username: + if pet.get("pet_owner", "") != username: return self._send_action_error(client_id, "feed_pet", "你不是该宠物的主人") target_pet = pet break @@ -2835,7 +2895,7 @@ class TCPGameServer(TCPServer): # 保存玩家数据 self.save_player_data(username, player_data) - pet_name = target_pet.get("基本信息", {}).get("宠物名称", "未知宠物") + pet_name = target_pet.get("pet_name", "未知宠物") # 构建效果描述 effect_descriptions = [] @@ -2877,18 +2937,12 @@ class TCPGameServer(TCPServer): # 记录实际应用的效果 applied_effects = {} - # 获取宠物各个属性数据 - level_exp_data = target_pet.setdefault("等级经验", {}) - health_defense_data = target_pet.setdefault("生命与防御", {}) - attack_data = target_pet.setdefault("基础攻击属性", {}) - movement_data = target_pet.setdefault("移动与闪避", {}) - # 处理经验效果 if "经验" in feed_effects: exp_gain = feed_effects["经验"] - current_exp = level_exp_data.get("当前经验", 0) - max_exp = level_exp_data.get("最大经验", 100) - current_level = level_exp_data.get("宠物等级", 1) + current_exp = target_pet.get("pet_experience", 0) + max_exp = target_pet.get("pet_max_experience", 100) + current_level = target_pet.get("pet_level", 1) new_exp = current_exp + exp_gain applied_effects["经验"] = exp_gain @@ -2903,9 +2957,9 @@ class TCPGameServer(TCPServer): max_exp = int(max_exp * 1.2) # 更新经验数据 - level_exp_data["当前经验"] = new_exp - level_exp_data["最大经验"] = max_exp - level_exp_data["宠物等级"] = current_level + target_pet["pet_experience"] = new_exp + target_pet["pet_max_experience"] = max_exp + target_pet["pet_level"] = current_level # 如果升级了,记录升级次数 if level_ups > 0: @@ -2916,69 +2970,58 @@ class TCPGameServer(TCPServer): # 处理生命值效果 if "生命值" in feed_effects: hp_gain = feed_effects["生命值"] - current_hp = health_defense_data.get("当前生命值", 100) - max_hp = health_defense_data.get("最大生命值", 100) + current_hp = target_pet.get("pet_current_health", 100) + max_hp = target_pet.get("pet_max_health", 100) actual_hp_gain = min(hp_gain, max_hp - current_hp) # 不能超过最大生命值 if actual_hp_gain > 0: - health_defense_data["当前生命值"] = current_hp + actual_hp_gain + target_pet["pet_current_health"] = current_hp + actual_hp_gain applied_effects["生命值"] = actual_hp_gain # 处理攻击力效果 if "攻击力" in feed_effects: attack_gain = feed_effects["攻击力"] - current_attack = attack_data.get("基础攻击伤害", 20) + current_attack = target_pet.get("pet_attack_damage", 20) new_attack = current_attack + attack_gain - attack_data["基础攻击伤害"] = new_attack + target_pet["pet_attack_damage"] = new_attack applied_effects["攻击力"] = attack_gain # 处理移动速度效果 if "移动速度" in feed_effects: speed_gain = feed_effects["移动速度"] - current_speed = movement_data.get("移动速度", 100) + current_speed = target_pet.get("pet_move_speed", 100) new_speed = current_speed + speed_gain - movement_data["移动速度"] = new_speed + target_pet["pet_move_speed"] = new_speed applied_effects["移动速度"] = speed_gain # 处理亲密度效果 if "亲密度" in feed_effects: intimacy_gain = feed_effects["亲密度"] - current_intimacy = level_exp_data.get("亲密度", 0) - max_intimacy = level_exp_data.get("最大亲密度", 1000) + current_intimacy = target_pet.get("pet_intimacy", 0) + max_intimacy = target_pet.get("pet_max_intimacy", 1000) actual_intimacy_gain = min(intimacy_gain, max_intimacy - current_intimacy) if actual_intimacy_gain > 0: - level_exp_data["亲密度"] = current_intimacy + actual_intimacy_gain + target_pet["pet_intimacy"] = current_intimacy + actual_intimacy_gain applied_effects["亲密度"] = actual_intimacy_gain # 处理护甲值效果 if "护甲值" in feed_effects: armor_gain = feed_effects["护甲值"] - current_armor = health_defense_data.get("当前护甲值", 10) - max_armor = health_defense_data.get("最大护甲值", 10) + current_armor = target_pet.get("pet_current_armor", 10) + max_armor = target_pet.get("pet_max_armor", 10) actual_armor_gain = min(armor_gain, max_armor - current_armor) if actual_armor_gain > 0: - health_defense_data["当前护甲值"] = current_armor + actual_armor_gain + target_pet["pet_current_armor"] = current_armor + actual_armor_gain applied_effects["护甲值"] = actual_armor_gain - # 处理护盾值效果 - if "护盾值" in feed_effects: - shield_gain = feed_effects["护盾值"] - current_shield = health_defense_data.get("当前护盾值", 0) - max_shield = health_defense_data.get("最大护盾值", 0) - - actual_shield_gain = min(shield_gain, max_shield - current_shield) - if actual_shield_gain > 0: - health_defense_data["当前护盾值"] = current_shield + actual_shield_gain - applied_effects["护盾值"] = actual_shield_gain - # 处理暴击率效果 if "暴击率" in feed_effects: crit_gain = feed_effects["暴击率"] / 100.0 # 转换为小数 - current_crit = attack_data.get("暴击率", 0.1) + current_crit = target_pet.get("pet_crit_rate", 0.1) new_crit = min(current_crit + crit_gain, 1.0) # 最大100% - attack_data["暴击率"] = new_crit + target_pet["pet_crit_rate"] = new_crit applied_effects["暴击率"] = feed_effects["暴击率"] # 处理闪避率效果 @@ -3002,30 +3045,154 @@ class TCPGameServer(TCPServer): level_bonus_multiplier = 1.1 ** level_ups # 更新生命和防御属性 - health_defense_data = target_pet.setdefault("生命与防御", {}) - old_max_hp = health_defense_data.get("最大生命值", 100) - old_max_armor = health_defense_data.get("最大护甲值", 10) - old_max_shield = health_defense_data.get("最大护盾值", 0) + old_max_hp = target_pet.get("pet_max_health", 100) + old_max_armor = target_pet.get("pet_max_armor", 10) new_max_hp = old_max_hp * level_bonus_multiplier new_max_armor = old_max_armor * level_bonus_multiplier - new_max_shield = old_max_shield * level_bonus_multiplier - health_defense_data["最大生命值"] = new_max_hp - health_defense_data["当前生命值"] = new_max_hp # 升级回满血 - health_defense_data["最大护甲值"] = new_max_armor - health_defense_data["当前护甲值"] = new_max_armor - health_defense_data["最大护盾值"] = new_max_shield - health_defense_data["当前护盾值"] = new_max_shield + target_pet["pet_max_health"] = new_max_hp + target_pet["pet_current_health"] = new_max_hp # 升级回满血 + target_pet["pet_max_armor"] = new_max_armor + target_pet["pet_current_armor"] = new_max_armor # 更新攻击属性 - attack_data = target_pet.setdefault("基础攻击属性", {}) - old_attack = attack_data.get("基础攻击伤害", 20) + old_attack = target_pet.get("pet_attack_damage", 20) new_attack = old_attack * level_bonus_multiplier - attack_data["基础攻击伤害"] = new_attack + target_pet["pet_attack_damage"] = new_attack #==========================宠物喂食处理========================== +#==========================宠物对战结果处理========================== + def _handle_pet_battle_result(self, client_id, message): + """处理宠物对战结果""" + # 检查用户是否已登录 + logged_in, response = self._check_user_logged_in(client_id, "提交宠物对战结果", "pet_battle_result") + if not logged_in: + return self.send_data(client_id, response) + + # 获取玩家数据 + player_data, username, response = self._load_player_data_with_check(client_id, "pet_battle_result") + if not player_data: + return self.send_data(client_id, response) + + # 获取对战结果数据 + battle_data = message.get("battle_data", {}) + winner = battle_data.get("winner", "") + attacker_name = battle_data.get("attacker_name", "") + defender_name = battle_data.get("defender_name", "") + battle_type = battle_data.get("battle_type", "") + attacker_pets = battle_data.get("attacker_pets", []) + defender_pets = battle_data.get("defender_pets", []) + duration = battle_data.get("duration", 0) + timestamp = battle_data.get("timestamp", time.time()) + + # 验证必要参数 + if not winner or not attacker_name or not defender_name: + return self._send_action_error(client_id, "pet_battle_result", "对战结果数据不完整") + + # 记录对战结果到日志 + self.log('INFO', f"宠物对战结果 - 获胜方: {winner}, 攻击方: {attacker_name}, 防守方: {defender_name}, 类型: {battle_type}, 持续时间: {duration}秒", 'BATTLE') + + # 初始化对战历史记录 + if "对战历史" not in player_data: + player_data["对战历史"] = [] + + # 添加对战记录 + battle_record = { + "获胜方": winner, + "攻击方": attacker_name, + "防守方": defender_name, + "对战类型": battle_type, + "攻击方宠物": attacker_pets, + "防守方宠物": defender_pets, + "持续时间": duration, + "时间戳": timestamp, + "日期": datetime.datetime.fromtimestamp(timestamp).strftime("%Y年%m月%d日%H时%M分%S秒") + } + + player_data["对战历史"].append(battle_record) + + # 限制历史记录数量(保留最近100条) + if len(player_data["对战历史"]) > 100: + player_data["对战历史"] = player_data["对战历史"][-100:] + + # 更新对战统计 + if "对战统计" not in player_data: + player_data["对战统计"] = { + "总对战次数": 0, + "胜利次数": 0, + "失败次数": 0, + "胜率": 0.0 + } + + stats = player_data["对战统计"] + stats["总对战次数"] += 1 + + if winner == username: + stats["胜利次数"] += 1 + else: + stats["失败次数"] += 1 + + # 计算胜率 + if stats["总对战次数"] > 0: + stats["胜率"] = round(stats["胜利次数"] / stats["总对战次数"] * 100, 2) + + # 保存玩家数据 + self.save_player_data(username, player_data) + + # 如果是与其他玩家的对战,也更新对方的记录 + if defender_name != username and defender_name != "系统": + defender_data = self.load_player_data(defender_name) + if defender_data: + # 初始化对方的对战历史和统计 + if "对战历史" not in defender_data: + defender_data["对战历史"] = [] + if "对战统计" not in defender_data: + defender_data["对战统计"] = { + "总对战次数": 0, + "胜利次数": 0, + "失败次数": 0, + "胜率": 0.0 + } + + # 添加对战记录 + defender_data["对战历史"].append(battle_record) + + # 限制历史记录数量 + if len(defender_data["对战历史"]) > 100: + defender_data["对战历史"] = defender_data["对战历史"][-100:] + + # 更新对战统计 + defender_stats = defender_data["对战统计"] + defender_stats["总对战次数"] += 1 + + if winner == defender_name: + defender_stats["胜利次数"] += 1 + else: + defender_stats["失败次数"] += 1 + + # 计算胜率 + if defender_stats["总对战次数"] > 0: + defender_stats["胜率"] = round(defender_stats["胜利次数"] / defender_stats["总对战次数"] * 100, 2) + + # 保存对方数据 + self.save_player_data(defender_name, defender_data) + + return self.send_data(client_id, { + "type": "action_response", + "action_type": "pet_battle_result", + "success": True, + "message": "对战结果已记录", + "updated_data": { + "对战统计": player_data["对战统计"] + } + }) +#==========================宠物对战结果处理========================== + + + + #==========================开垦土地处理========================== #处理开垦土地请求 def _handle_dig_ground(self, client_id, message): @@ -3108,7 +3275,7 @@ class TCPGameServer(TCPServer): self._push_crop_update_to_player(username, player_data) # 构建奖励消息 - reward_message = f"获得 {rewards['money']} 金钱、{rewards['experience']} 经验" + reward_message = f"获得 {rewards['钱币']} 金钱、{rewards['经验值']} 经验" if rewards["seeds"]: seed_list = [f"{name} x{qty}" for name, qty in rewards["seeds"].items()] reward_message += f"、种子:{', '.join(seed_list)}" @@ -3725,30 +3892,21 @@ class TCPGameServer(TCPServer): #加载道具配置数据 def _load_item_config(self): - """优先从MongoDB加载道具配置数据,失败时回退到JSON文件""" - # 首先尝试从MongoDB加载 - if self.mongo_api and self.mongo_api.is_connected(): - try: - config = self.mongo_api.get_item_config() - if config: - self.log('INFO', '成功从MongoDB加载道具配置', 'SERVER') - return config - else: - self.log('WARNING', 'MongoDB中未找到道具配置,回退到JSON文件', 'SERVER') - except Exception as e: - self.log('WARNING', f'从MongoDB加载道具配置失败: {e},回退到JSON文件', 'SERVER') - - # 回退到JSON文件 - try: - with open("config/item_config.json", 'r', encoding='utf-8') as file: - config = json.load(file) - self.log('INFO', '从JSON文件加载道具配置', 'SERVER') - return config - except json.JSONDecodeError as e: - self.log('ERROR', f'JSON文件格式错误: {e}', 'SERVER') + """从MongoDB加载道具配置数据""" + if not self.mongo_api or not self.mongo_api.is_connected(): + self.log('ERROR', 'MongoDB未配置或不可用,无法加载道具配置数据', 'SERVER') return {} + + try: + config = self.mongo_api.get_item_config() + if config: + self.log('INFO', '成功从MongoDB加载道具配置', 'SERVER') + return config + else: + self.log('ERROR', 'MongoDB中未找到道具配置', 'SERVER') + return {} except Exception as e: - self.log('ERROR', f'无法加载道具数据: {e}', 'SERVER') + self.log('ERROR', f'从MongoDB加载道具配置失败: {e}', 'SERVER') return {} #==========================购买道具处理========================== @@ -4702,7 +4860,7 @@ class TCPGameServer(TCPServer): pet_bag = player_data.get("宠物背包", []) pet_index = -1 for i, pet in enumerate(pet_bag): - if pet.get("基本信息", {}).get("宠物ID") == pet_id: + if pet.get("pet_id") == pet_id: pet_index = i break @@ -4760,27 +4918,27 @@ class TCPGameServer(TCPServer): # 启用死亡免疫机制 pet_data["特殊机制开关"]["启用死亡免疫机制"] = True pet_data["特殊属性"]["死亡免疫"] = True - return True, f"宠物 {pet_data['基本信息']['宠物名称']} 获得了死亡免疫能力!", pet_data + return True, f"宠物 {pet_data['pet_name']} 获得了死亡免疫能力!", pet_data elif item_name == "荆棘护甲": # 启用伤害反弹机制 pet_data["特殊机制开关"]["启用伤害反弹机制"] = True pet_data["特殊属性"]["伤害反弹"] = 0.3 # 反弹30%伤害 - return True, f"宠物 {pet_data['基本信息']['宠物名称']} 获得了荆棘护甲!", pet_data + return True, f"宠物 {pet_data['pet_name']} 获得了荆棘护甲!", pet_data elif item_name == "狂暴药水": # 启用狂暴模式机制 pet_data["特殊机制开关"]["启用狂暴模式机制"] = True pet_data["特殊属性"]["狂暴阈值"] = 0.3 # 血量低于30%时触发 pet_data["特殊属性"]["狂暴状态伤害倍数"] = 2.0 # 狂暴时伤害翻倍 - return True, f"宠物 {pet_data['基本信息']['宠物名称']} 获得了狂暴能力!", pet_data + return True, f"宠物 {pet_data['pet_name']} 获得了狂暴能力!", pet_data elif item_name == "援军令牌": # 启用援助召唤机制 pet_data["特殊机制开关"]["启用援助召唤机制"] = True pet_data["援助系统"]["援助触发阈值"] = 0.2 # 血量低于20%时触发 pet_data["援助系统"]["援助召唤数量"] = 3 # 召唤3个援军 - return True, f"宠物 {pet_data['基本信息']['宠物名称']} 获得了援军召唤能力!", pet_data + return True, f"宠物 {pet_data['pet_name']} 获得了援军召唤能力!", pet_data elif item_name in ["金刚图腾", "灵木图腾", "潮汐图腾", "烈焰图腾", "敦岩图腾"]: # 改变宠物元素 @@ -4806,7 +4964,7 @@ class TCPGameServer(TCPServer): pet_data["元素属性"]["元素类型"] = new_element pet_data["元素属性"]["元素克制额外伤害"] = 100.0 # 元素克制时额外伤害 - return True, f"宠物 {pet_data['基本信息']['宠物名称']} 的元素属性已改变为{element_name}元素!", pet_data + return True, f"宠物 {pet_data['pet_name']} 的元素属性已改变为{element_name}元素!", pet_data else: return False, f"未知的宠物道具: {item_name}" @@ -4819,7 +4977,6 @@ class TCPGameServer(TCPServer): - #==========================农场道具使用处理========================== def _handle_use_farm_item(self, client_id, message): """处理农场道具使用请求""" @@ -4968,6 +5125,24 @@ class TCPGameServer(TCPServer): "success": False, "message": "无法读取道具配置数据" }) + + def _handle_pet_config_request(self, client_id): + """处理客户端请求宠物配置数据""" + pet_config = self._load_pet_config() + + if pet_config: + self.log('INFO', f"向客户端 {client_id} 发送宠物配置数据,宠物种类:{len(pet_config)}", 'SERVER') + return self.send_data(client_id, { + "type": "pet_config_response", + "success": True, + "pet_config": pet_config + }) + else: + return self.send_data(client_id, { + "type": "pet_config_response", + "success": False, + "message": "无法读取宠物配置数据" + }) #==========================道具配置数据处理========================== @@ -5774,32 +5949,28 @@ class TCPGameServer(TCPServer): return self.send_data(client_id, response) # 获取排序和筛选参数 - sort_by = message.get("sort_by", "等级") # 排序字段:seed_count, level, online_time, login_time, like_num, money - sort_order = message.get("sort_order", "desc") # 排序顺序:asc, desc + sort_by = message.get("sort_by", "等级") # 排序字段 + sort_order = message.get("sort_order", "desc") # 排序顺序 filter_online = message.get("filter_online", False) # 是否只显示在线玩家 search_qq = message.get("search_qq", "") # 搜索的QQ号 - # 获取所有玩家存档文件 - save_files = glob.glob(os.path.join("game_saves", "*.json")) - players_data = [] - - # 统计注册总人数 - total_registered_players = len(save_files) - - for save_file in save_files: - try: - # 从文件名提取账号ID - account_id = os.path.basename(save_file).split('.')[0] + try: + players_data = [] + total_registered_players = 0 + + # 优先使用MongoDB + if self.use_mongodb and self.mongo_api: + # 获取所有玩家基本信息 + all_players = self.mongo_api.get_all_players_basic_info() + total_registered_players = len(all_players) - # 如果有搜索条件,先检查是否匹配 - if search_qq and search_qq not in account_id: - continue - - # 加载玩家数据 - with open(save_file, 'r', encoding='utf-8') as file: - player_data = json.load(file) - - if player_data: + for player_data in all_players: + account_id = player_data.get("玩家账号", "") + + # 如果有搜索条件,先检查是否匹配 + if search_qq and search_qq not in account_id: + continue + # 统计背包中的种子数量 seed_count = sum(item.get("count", 0) for item in player_data.get("种子仓库", [])) @@ -5821,13 +5992,13 @@ class TCPGameServer(TCPServer): last_login_str = player_data.get("最后登录时间", "未知") last_login_timestamp = self._parse_login_time_to_timestamp(last_login_str) - # 获取所需的玩家信息 + # 获取体力值 stamina_system = player_data.get("体力系统", {}) current_stamina = stamina_system.get("当前体力值", 20) player_info = { - "玩家账号": player_data.get("玩家账号", account_id), - "玩家昵称": player_data.get("玩家昵称", player_data.get("玩家账号", account_id)), + "玩家账号": account_id, + "玩家昵称": player_data.get("玩家昵称", account_id), "农场名称": player_data.get("农场名称", ""), "等级": player_data.get("等级", 1), "钱币": player_data.get("钱币", 0), @@ -5843,49 +6014,119 @@ class TCPGameServer(TCPServer): } players_data.append(player_info) - except Exception as e: - self.log('ERROR', f"读取玩家 {account_id} 的数据时出错: {str(e)}", 'SERVER') - - # 根据排序参数进行排序 - reverse_order = (sort_order == "desc") - - if sort_by == "seed_count": - players_data.sort(key=lambda x: x["seed_count"], reverse=reverse_order) - elif sort_by == "等级": - players_data.sort(key=lambda x: x["等级"], reverse=reverse_order) - elif sort_by == "online_time": - players_data.sort(key=lambda x: x["total_time_seconds"], reverse=reverse_order) - elif sort_by == "login_time": - players_data.sort(key=lambda x: x["last_login_timestamp"], reverse=reverse_order) - elif sort_by == "like_num": - players_data.sort(key=lambda x: x["like_num"], reverse=reverse_order) - elif sort_by == "钱币": - players_data.sort(key=lambda x: x["钱币"], reverse=reverse_order) - else: - # 默认按等级排序 - players_data.sort(key=lambda x: x["等级"], reverse=True) - - # 统计在线玩家数量 - online_count = sum(1 for player in players_data if player.get("is_online", False)) - - # 记录日志 - search_info = f",搜索QQ:{search_qq}" if search_qq else "" - filter_info = ",仅在线玩家" if filter_online else "" - sort_info = f",按{sort_by}{'降序' if reverse_order else '升序'}排序" - - self.log('INFO', f"玩家 {self.user_data[client_id].get('username')} 请求玩家排行榜{search_info}{filter_info}{sort_info},返回 {len(players_data)} 个玩家数据,注册总人数:{total_registered_players},在线人数:{online_count}", 'SERVER') - - # 返回排行榜数据(包含注册总人数) - return self.send_data(client_id, { - "type": "player_rankings_response", - "success": True, - "players": players_data, - "total_registered_players": total_registered_players, - "sort_by": sort_by, - "sort_order": sort_order, - "filter_online": filter_online, - "search_qq": search_qq - }) + else: + # 降级到文件系统 + save_files = glob.glob(os.path.join("game_saves", "*.json")) + total_registered_players = len(save_files) + + for save_file in save_files: + try: + # 从文件名提取账号ID + account_id = os.path.basename(save_file).split('.')[0] + + # 如果有搜索条件,先检查是否匹配 + if search_qq and search_qq not in account_id: + continue + + # 加载玩家数据 + with open(save_file, 'r', encoding='utf-8') as file: + player_data = json.load(file) + + if player_data: + # 统计背包中的种子数量 + seed_count = sum(item.get("count", 0) for item in player_data.get("种子仓库", [])) + + # 检查玩家是否在线 + is_online = any( + user_info.get("username") == account_id and user_info.get("logged_in", False) + for user_info in self.user_data.values() + ) + + # 如果筛选在线玩家,跳过离线玩家 + if filter_online and not is_online: + continue + + # 解析总游玩时间为秒数(用于排序) + total_time_str = player_data.get("总游玩时间", "0时0分0秒") + total_time_seconds = self._parse_time_to_seconds(total_time_str) + + # 解析最后登录时间为时间戳(用于排序) + last_login_str = player_data.get("最后登录时间", "未知") + last_login_timestamp = self._parse_login_time_to_timestamp(last_login_str) + + # 获取所需的玩家信息 + stamina_system = player_data.get("体力系统", {}) + current_stamina = stamina_system.get("当前体力值", 20) + + player_info = { + "玩家账号": player_data.get("玩家账号", account_id), + "玩家昵称": player_data.get("玩家昵称", player_data.get("玩家账号", account_id)), + "农场名称": player_data.get("农场名称", ""), + "等级": player_data.get("等级", 1), + "钱币": player_data.get("钱币", 0), + "经验值": player_data.get("经验值", 0), + "体力值": current_stamina, + "seed_count": seed_count, + "最后登录时间": last_login_str, + "last_login_timestamp": last_login_timestamp, + "总游玩时间": total_time_str, + "total_time_seconds": total_time_seconds, + "like_num": player_data.get("点赞系统", {}).get("总点赞数", 0), + "is_online": is_online + } + + players_data.append(player_info) + except Exception as e: + self.log('ERROR', f"读取玩家 {account_id} 的数据时出错: {str(e)}", 'SERVER') + + # 根据排序参数进行排序 + reverse_order = (sort_order == "desc") + + if sort_by == "seed_count": + players_data.sort(key=lambda x: x["seed_count"], reverse=reverse_order) + elif sort_by == "等级": + players_data.sort(key=lambda x: x["等级"], reverse=reverse_order) + elif sort_by == "online_time": + players_data.sort(key=lambda x: x["total_time_seconds"], reverse=reverse_order) + elif sort_by == "login_time": + players_data.sort(key=lambda x: x["last_login_timestamp"], reverse=reverse_order) + elif sort_by == "like_num": + players_data.sort(key=lambda x: x["like_num"], reverse=reverse_order) + elif sort_by == "钱币": + players_data.sort(key=lambda x: x["钱币"], reverse=reverse_order) + else: + # 默认按等级排序 + players_data.sort(key=lambda x: x["等级"], reverse=True) + + # 统计在线玩家数量 + online_count = sum(1 for player in players_data if player.get("is_online", False)) + + # 记录日志 + search_info = f",搜索QQ:{search_qq}" if search_qq else "" + filter_info = ",仅在线玩家" if filter_online else "" + sort_info = f",按{sort_by}{'降序' if reverse_order else '升序'}排序" + + self.log('INFO', f"玩家 {self.user_data[client_id].get('username')} 请求玩家排行榜{search_info}{filter_info}{sort_info},返回 {len(players_data)} 个玩家数据,注册总人数:{total_registered_players},在线人数:{online_count}", 'SERVER') + + # 返回排行榜数据(包含注册总人数) + return self.send_data(client_id, { + "type": "player_rankings_response", + "success": True, + "players": players_data, + "total_registered_players": total_registered_players, + "sort_by": sort_by, + "sort_order": sort_order, + "filter_online": filter_online, + "search_qq": search_qq + }) + + except Exception as e: + self.log('ERROR', f"处理玩家排行榜请求时出错: {str(e)}", 'SERVER') + return self.send_data(client_id, { + "type": "player_rankings_response", + "success": False, + "message": "获取排行榜数据失败" + }) # 辅助函数:将时间字符串转换为秒数 def _parse_time_to_seconds(self, time_str): @@ -6039,6 +6280,9 @@ class TCPGameServer(TCPServer): self.user_data[client_id]["visiting_mode"] = False self.user_data[client_id]["visiting_target"] = "" + # 清理偷菜免被发现计数器 + self._clear_player_steal_immunity(username) + self.log('INFO', f"玩家 {username} 返回了自己的农场", 'SERVER') # 返回玩家自己的农场数据 @@ -6428,7 +6672,7 @@ class TCPGameServer(TCPServer): # 保存消息到MongoDB if self.mongo_api and self.mongo_api.is_connected(): - success = self.mongo_api.save_chat_message(username, player_name, content, current_timestamp) + success = self.mongo_api.save_chat_message(username, player_name, content) if not success: self.log('WARNING', f"保存聊天消息到MongoDB失败,尝试保存到本地文件", 'BROADCAST') self._save_broadcast_message_to_log(username, player_name, content) @@ -7728,11 +7972,6 @@ class TCPGameServer(TCPServer): #==========================发送游戏操作错误处理========================== - - - - - # ================================账户设置处理方法================================ def _handle_modify_account_info_request(self, client_id, message): """处理修改账号信息请求""" @@ -7811,11 +8050,11 @@ class TCPGameServer(TCPServer): username = self.user_data[client_id]["username"] try: - # 删除玩家文件 - file_path = os.path.join("game_saves", f"{username}.json") - if os.path.exists(file_path): - os.remove(file_path) - self.log('INFO', f"已删除玩家文件: {file_path}", 'ACCOUNT') + # 优先从MongoDB删除 + if self.use_mongodb and self.mongo_api: + success = self.mongo_api.delete_player_data(username) + if not success: + self.log('WARNING', f"MongoDB删除失败,尝试删除文件: {username}", 'ACCOUNT') # 清理用户数据 if client_id in self.user_data: @@ -7856,8 +8095,8 @@ class TCPGameServer(TCPServer): username = self.user_data[client_id]["username"] try: - # 强制从文件重新加载最新数据 - player_data = self._load_player_data_from_file(username) + # 强制从数据库重新加载最新数据 + player_data = self.load_player_data(username) if not player_data: return self._send_refresh_info_error(client_id, "无法加载玩家数据") @@ -8113,7 +8352,6 @@ class TCPGameServer(TCPServer): def _load_scare_crow_config(self): """加载稻草人配置""" - # 优先从MongoDB加载配置 try: if hasattr(self, 'mongo_api') and self.mongo_api and self.mongo_api.is_connected(): config = self.mongo_api.get_scare_crow_config() @@ -8121,27 +8359,21 @@ class TCPGameServer(TCPServer): self.log('INFO', "成功从MongoDB加载稻草人配置", 'SERVER') return config else: - self.log('WARNING', "MongoDB中未找到稻草人配置,回退到JSON文件", 'SERVER') + self.log('WARNING', "MongoDB中未找到稻草人配置,使用默认配置", 'SERVER') + else: + self.log('WARNING', "MongoDB未连接,使用默认稻草人配置", 'SERVER') except Exception as e: - self.log('ERROR', f"从MongoDB加载稻草人配置失败: {str(e)},回退到JSON文件", 'SERVER') + self.log('ERROR', f"从MongoDB加载稻草人配置失败: {str(e)},使用默认配置", 'SERVER') - # 回退到从JSON文件加载 - try: - with open("config/scare_crow_config.json", 'r', encoding='utf-8') as file: - config = json.load(file) - self.log('INFO', "成功从JSON文件加载稻草人配置", 'SERVER') - return config - except Exception as e: - self.log('ERROR', f"无法加载稻草人配置: {str(e)}", 'SERVER') - # 返回默认配置 - return { - "稻草人类型": { - "稻草人1": {"图片": "res://assets/道具图片/稻草人1.webp", "价格": 1000}, - "稻草人2": {"图片": "res://assets/道具图片/稻草人2.webp", "价格": 1000}, - "稻草人3": {"图片": "res://assets/道具图片/稻草人3.webp", "价格": 1000} - }, - "修改稻草人配置花费": 300 - } + # 返回默认配置 + return { + "稻草人类型": { + "稻草人1": {"图片": "res://assets/道具图片/稻草人1.webp", "价格": 1000}, + "稻草人2": {"图片": "res://assets/道具图片/稻草人2.webp", "价格": 1000}, + "稻草人3": {"图片": "res://assets/道具图片/稻草人3.webp", "价格": 1000} + }, + "修改稻草人配置花费": 300 + } def _send_buy_scare_crow_error(self, client_id, message): """发送购买稻草人错误响应""" @@ -9326,6 +9558,8 @@ class TCPGameServer(TCPServer): #==========================小卖部管理处理========================== + + def console_input_thread(server): """控制台输入处理线程""" import threading diff --git a/Server/__pycache__/ConsoleCommandsAPI.cpython-313.pyc b/Server/__pycache__/ConsoleCommandsAPI.cpython-313.pyc index e360271..aa5adb2 100644 Binary files a/Server/__pycache__/ConsoleCommandsAPI.cpython-313.pyc and b/Server/__pycache__/ConsoleCommandsAPI.cpython-313.pyc differ diff --git a/Server/__pycache__/QQEmailSendAPI.cpython-313.pyc b/Server/__pycache__/QQEmailSendAPI.cpython-313.pyc index d3c54c2..1cf5287 100644 Binary files a/Server/__pycache__/QQEmailSendAPI.cpython-313.pyc and b/Server/__pycache__/QQEmailSendAPI.cpython-313.pyc differ diff --git a/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc b/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc index a21b90b..7e0fa99 100644 Binary files a/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc and b/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc differ diff --git a/Server/__pycache__/TCPGameServer.cpython-313.pyc b/Server/__pycache__/TCPGameServer.cpython-313.pyc index b37fd7f..078cd16 100644 Binary files a/Server/__pycache__/TCPGameServer.cpython-313.pyc and b/Server/__pycache__/TCPGameServer.cpython-313.pyc differ diff --git a/Server/__pycache__/TCPServer.cpython-313.pyc b/Server/__pycache__/TCPServer.cpython-313.pyc index d3d7905..6938e54 100644 Binary files a/Server/__pycache__/TCPServer.cpython-313.pyc and b/Server/__pycache__/TCPServer.cpython-313.pyc differ diff --git a/Server/deploy.sh b/Server/deploy.sh index 78b9fca..020338b 100644 --- a/Server/deploy.sh +++ b/Server/deploy.sh @@ -20,7 +20,6 @@ show_help() { echo " logs - 查看日志" echo " status - 查看状态" echo " build - 重新构建镜像" - echo " clean - 清理停止的容器和未使用的镜像" echo " help - 显示此帮助信息" } @@ -41,17 +40,10 @@ check_docker() { start_server() { echo "🚀 启动萌芽农场服务器..." - # 检查配置文件是否存在 - if [ ! -f "config/crop_data.json" ]; then - echo "⚠️ 警告: 配置文件不存在,请确保 config 目录包含必要的配置文件" - fi - # 启动容器 docker-compose up -d echo "✅ 服务器启动成功!" - echo "📡 服务器地址: localhost:6060" - echo "📝 查看日志: $0 logs" } # 停止服务器 @@ -83,11 +75,6 @@ show_status() { if docker-compose ps | grep -q "Up"; then echo "✅ 服务器正在运行" echo "🔗 端口映射: 6060:6060" - - # 显示资源使用情况 - echo "" - echo "📈 资源使用情况:" - docker stats --no-stream $CONTAINER_NAME 2>/dev/null || echo "无法获取资源使用情况" else echo "❌ 服务器未运行" fi @@ -100,13 +87,6 @@ build_image() { echo "✅ 镜像构建完成" } -# 清理 -clean_up() { - echo "🧹 清理停止的容器和未使用的镜像..." - docker-compose down - docker system prune -f - echo "✅ 清理完成" -} # 主函数 main() { @@ -131,9 +111,6 @@ main() { "build") build_image ;; - "clean") - clean_up - ;; "help"|*) show_help ;; diff --git a/Server/docker-compose.yml b/Server/docker-compose.yml index 661e571..dc7a190 100644 --- a/Server/docker-compose.yml +++ b/Server/docker-compose.yml @@ -18,6 +18,7 @@ services: - PYTHONUNBUFFERED=1 - LANG=C.UTF-8 - LC_ALL=C.UTF-8 + - PRODUCTION=true networks: - mengyafarm-network logging: @@ -28,4 +29,4 @@ services: networks: mengyafarm-network: - driver: bridge \ No newline at end of file + driver: bridge \ No newline at end of file diff --git a/Server/game_saves/2143323382.json b/Server/game_saves/2143323382.json deleted file mode 100644 index d4e5648..0000000 --- a/Server/game_saves/2143323382.json +++ /dev/null @@ -1,1099 +0,0 @@ -{ - "经验值": 455, - "等级": 36, - "钱币": 200796649, - "农场名称": "柚大青の小农场", - "玩家昵称": "柚大青", - "玩家账号": "2143323382", - "玩家密码": "tyh@19900420", - "最后登录时间": "2025年07月21日13时43分17秒", - "总游玩时间": "6时50分12秒", - "农场土地": [ - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 1440, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "野草1", - "grow_time": 6, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 1080, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "橘子", - "grow_time": 10234, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 10200, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 12000, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 1080, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 240, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "杂交树1", - "grow_time": 21642, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 21600, - "已浇水": false, - "已施肥": false, - "土地等级": 3, - "浇水时间": 1753019871.817958 - }, - { - "crop_type": "杂交树2", - "grow_time": 25236, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 3, - "浇水时间": 1753019874.5774846 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 240, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "藏红花", - "grow_time": 7006, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 7000, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "杂交树2", - "grow_time": 25208, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "杂交树1", - "grow_time": 21642, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 21600, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 480, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 14400, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "芦荟", - "grow_time": 6013, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 6000, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "玉米", - "grow_time": 918, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 900, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "松露", - "grow_time": 18001, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 18000, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "藏红花", - "grow_time": 7009, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 7000, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "杂交树1", - "grow_time": 21607, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 21600, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "杂交树1", - "grow_time": 21603, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 21600, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3060, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 2500, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 600, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "杂交树2", - "grow_time": 25215, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - } - ], - "种子仓库": [ - { - "name": "土豆", - "quality": "普通", - "count": 2 - }, - { - "name": "小麦", - "quality": "普通", - "count": 2 - }, - { - "name": "胡萝卜", - "quality": "普通", - "count": 1 - }, - { - "name": "番茄", - "quality": "普通", - "count": 1 - }, - { - "name": "辣椒", - "quality": "普通", - "count": 1 - }, - { - "name": "草莓", - "quality": "稀有", - "count": 1 - }, - { - "name": "花椰菜", - "quality": "优良", - "count": 1 - }, - { - "name": "葡萄", - "quality": "普通", - "count": 2 - }, - { - "name": "南瓜", - "quality": "普通", - "count": 1 - }, - { - "name": "月光草", - "quality": "传奇", - "count": 1 - }, - { - "name": "荔枝", - "count": 2 - }, - { - "name": "玫瑰花", - "count": 1 - }, - { - "name": "石榴", - "count": 2 - }, - { - "name": "芦荟", - "count": 1 - }, - { - "name": "向日葵", - "count": 3 - }, - { - "name": "黄瓜", - "quality": "普通", - "count": 3 - }, - { - "name": "野草1", - "quality": "优良", - "count": 2 - }, - { - "name": "豌豆", - "quality": "普通", - "count": 4 - }, - { - "name": "稻谷", - "quality": "普通", - "count": 4 - }, - { - "name": "山楂", - "quality": "优良", - "count": 2 - }, - { - "name": "龙果", - "quality": "传奇", - "count": 1 - }, - { - "name": "杂交树1", - "quality": "传奇", - "count": 1 - }, - { - "name": "杂交树2", - "quality": "传奇", - "count": 1 - } - ], - "注册时间": "2025年05月21日15时00分00秒", - "个人简介": "其实我是一个梨子,真的,不骗你", - "作物仓库": [ - { - "name": "番茄", - "quality": "普通", - "count": 23 - }, - { - "name": "小麦", - "quality": "普通", - "count": 44 - }, - { - "name": "龙果", - "quality": "稀有", - "count": 4 - }, - { - "name": "玉米", - "quality": "优良", - "count": 1 - }, - { - "name": "稻谷", - "quality": "普通", - "count": 3 - }, - { - "name": "胡萝卜", - "quality": "普通", - "count": 20 - }, - { - "name": "花椰菜", - "quality": "优良", - "count": 1 - }, - { - "name": "土豆", - "quality": "普通", - "count": 19 - }, - { - "name": "咖啡豆", - "quality": "稀有", - "count": 9 - }, - { - "name": "人参", - "quality": "史诗", - "count": 3 - }, - { - "name": "藏红花", - "quality": "稀有", - "count": 1 - }, - { - "name": "杨桃", - "quality": "史诗", - "count": 1 - }, - { - "name": "冬虫夏草", - "quality": "史诗", - "count": 1 - }, - { - "name": "仙人掌", - "quality": "优良", - "count": 5 - }, - { - "name": "大豆", - "quality": "普通", - "count": 5 - }, - { - "name": "荔枝", - "quality": "优良", - "count": 5 - }, - { - "name": "杂交树2", - "quality": "传奇", - "count": 1 - } - ], - "宠物背包": [ - { - "场景路径": "res://Scene/Pet/SmallBeetle.tscn", - "基本信息": { - "宠物主人": "2143323382", - "宠物名称": "柚大青的小甲虫", - "队伍标识": "team1", - "宠物ID": "1751716398095", - "宠物类型": "小甲虫", - "生日": "2025年7月5日19时53分18秒", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - "穿透属性": { - "穿透数量": 3 - }, - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - "品质系统": { - "宠物品质": "COMMON" - } - }, - { - "场景路径": "res://Scene/Pet/BigBeetle.tscn", - "基本信息": { - "宠物主人": "2143323382", - "宠物名称": "2143323382的大甲虫", - "队伍标识": "team1", - "宠物ID": "1753020931947", - "宠物类型": "大甲虫", - "生日": "2025年7月20日22时15分31秒", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - "等级经验": { - "宠物等级": 1, - "当前经验": 0, - "最大经验": 100, - "亲密度": 0, - "最大亲密度": 1000 - }, - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - "生命与防御": { - "最大生命值": 200, - "当前生命值": 200, - "生命恢复速度": 1, - "最大护盾值": 0, - "当前护盾值": 0, - "护盾恢复速度": 0, - "最大护甲值": 100, - "当前护甲值": 100 - }, - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25, - "攻击距离": 100, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0 - }, - "近战攻击": { - "近战额外伤害": 0, - "近战攻击速度": 1 - }, - "远程攻击": { - "远程额外伤害": 0, - "远程攻击速度": 1, - "远程攻击模式": "SINGLE", - "子弹速度": 300 - }, - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45 - }, - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30 - }, - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2 - }, - "穿透属性": { - "穿透数量": 3 - }, - "移动与闪避": { - "移动速度": 100, - "闪避率": 0.05, - "击退力度": 300, - "击退抗性": 0 - }, - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50 - }, - "特殊属性": { - "控制抗性": 0, - "伤害反弹": 0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5 - }, - "品质系统": { - "宠物品质": "COMMON" - } - } - ], - "巡逻宠物": [ - "1751716398095" - ], - "出战宠物": [ - "1751716398095" - ], - "签到历史": { - "2025年07月12日21时05分47秒": "金币249 经验75 土豆x3", - "2025年07月13日07时26分04秒": "金币302 经验63 土豆x5 小麦x3" - }, - "在线礼包": { - "当前日期": "2025-07-21", - "今日在线时长": 0.0, - "已领取礼包": [], - "登录时间": 1753063234.6517925 - }, - "点赞系统": { - "今日剩余点赞次数": 10, - "点赞上次刷新时间": "2025-07-21", - "总点赞数": 0 - }, - "新手礼包": { - "已领取": true, - "领取时间": "2025-07-20 20:21:04" - }, - "体力系统": { - "当前体力值": 25, - "最大体力值": 25, - "上次刷新时间": "2025-07-21", - "上次恢复时间": 1753063234.6496308 - }, - "道具背包": [ - { - "name": "铲子", - "count": 100 - }, - { - "name": "农家肥", - "count": 4 - }, - { - "name": "水桶", - "count": 3 - }, - { - "name": "荆棘护甲", - "count": 1 - }, - { - "name": "时运-镰刀", - "count": 1 - }, - { - "name": "精准采集-镰刀", - "count": 1 - }, - { - "name": "金坷垃", - "count": 1 - }, - { - "name": "水壶", - "count": 1 - }, - { - "name": "杀虫剂", - "count": 1 - }, - { - "name": "除草剂", - "count": 1 - } - ], - "小卖部配置": { - "商品列表": [], - "格子数": 50 - }, - "游戏设置": { - "背景音乐音量": 1.0, - "天气显示": true - }, - "智慧树配置": { - "距离上一次杀虫时间": 1753014929, - "距离上一次除草时间": 1753014864, - "智慧树显示的话": "你好,树萌芽", - "等级": 2, - "当前经验值": 27, - "最大经验值": 169, - "最大生命值": 102, - "当前生命值": 102, - "高度": 20, - "上次护理时间": 1753014929 - }, - "稻草人配置": { - "已拥有稻草人类型": [ - "稻草人1", - "稻草人2", - "稻草人3" - ], - "稻草人展示类型": "稻草人2", - "稻草人昵称": "稻草人", - "稻草人说的话": { - "第一句话": { - "内容": "第一句话", - "颜色": "52dceeff" - }, - "第二句话": { - "内容": "第二句话", - "颜色": "80d5ffff" - }, - "第三句话": { - "内容": "第三句话", - "颜色": "ac52ffff" - }, - "第四句话": { - "内容": "第四句话", - "颜色": "f881ffff" - } - }, - "稻草人昵称颜色": "b38282ff" - } -} \ No newline at end of file diff --git a/Server/game_saves/2804775686.json b/Server/game_saves/2804775686.json deleted file mode 100644 index 466aa5b..0000000 --- a/Server/game_saves/2804775686.json +++ /dev/null @@ -1,577 +0,0 @@ -{ - "经验值": 396, - "等级": 5, - "钱币": 11476, - "农场名称": "123", - "玩家昵称": "123", - "玩家账号": "2804775686", - "玩家密码": "123", - "最后登录时间": "2025年07月22日08时51分11秒", - "总游玩时间": "0时3分4秒", - "注册时间": "2025年07月21日20时35分51秒", - "个人简介": "21323123", - "农场土地": [ - { - "crop_type": "杂交树2", - "grow_time": 860, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "小麦", - "grow_time": 300, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "土豆", - "grow_time": 480, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 480, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "小麦", - "grow_time": 300, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "小麦", - "grow_time": 300, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "小麦", - "grow_time": 300, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "小麦", - "grow_time": 300, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "胡萝卜", - "grow_time": 240, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 240, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "小麦", - "grow_time": 300, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "龙果", - "grow_time": 840, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 14400, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "玉米", - "grow_time": 840, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 900, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "土豆", - "grow_time": 480, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 480, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "玉米", - "grow_time": 830, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 900, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "辣椒", - "grow_time": 650, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 650, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "辣椒", - "grow_time": 650, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 650, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "小麦", - "grow_time": 300, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "胡萝卜", - "grow_time": 240, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 240, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "胡萝卜", - "grow_time": 240, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 240, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "小麦", - "grow_time": 300, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "玉米", - "grow_time": 810, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 900, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - } - ], - "种子仓库": [ - { - "name": "杂交树1", - "quality": "传奇", - "count": 1 - }, - { - "name": "小麦", - "quality": "普通", - "count": 6 - }, - { - "name": "辣椒", - "quality": "普通", - "count": 1 - }, - { - "name": "胡萝卜", - "quality": "普通", - "count": 8 - }, - { - "name": "椰子", - "quality": "优良", - "count": 2 - } - ], - "作物仓库": [ - { - "name": "辣椒", - "quality": "普通", - "count": 33 - } - ], - "道具背包": [], - "宠物背包": [], - "巡逻宠物": [], - "出战宠物": [], - "稻草人配置": { - "已拥有稻草人类型": [ - "稻草人1" - ], - "稻草人展示类型": "", - "稻草人昵称": "稻草人", - "稻草人说的话": { - "第一句话": { - "内容": "第一句话", - "颜色": "52dceeff" - }, - "第二句话": { - "内容": "第二句话", - "颜色": "80d5ffff" - }, - "第三句话": { - "内容": "第三句话", - "颜色": "ac52ffff" - }, - "第四句话": { - "内容": "第四句话", - "颜色": "f881ffff" - } - }, - "稻草人昵称颜色": "b38282ff" - }, - "智慧树配置": { - "距离上一次杀虫时间": 1753004237, - "距离上一次除草时间": 1753004237, - "智慧树显示的话": "", - "等级": 1, - "当前经验值": 0, - "最大经验值": 100, - "最大生命值": 100, - "当前生命值": 100, - "高度": 20, - "上次护理时间": 1753101378 - }, - "签到历史": { - "2025年07月21日21时13分12秒": "金币276 经验56 土豆x2 小麦x4" - }, - "在线礼包": { - "当前日期": "2025-07-22", - "今日在线时长": 78.0709433555603, - "已领取礼包": [ - "1分钟" - ], - "登录时间": 1753145471.320933 - }, - "点赞系统": { - "今日剩余点赞次数": 10, - "点赞上次刷新时间": "2025-07-22", - "总点赞数": 0 - }, - "新手礼包": { - "已领取": true, - "领取时间": "2025-07-21 21:13:05" - }, - "体力系统": { - "当前体力值": 25, - "最大体力值": 25, - "上次刷新时间": "2025-07-22", - "上次恢复时间": 1753145471.3187816 - }, - "小卖部配置": { - "商品列表": [], - "格子数": 10 - }, - "游戏设置": { - "背景音乐音量": 1.0, - "天气显示": true - }, - "": "123" -} \ No newline at end of file diff --git a/Server/requirements.txt b/Server/requirements.txt index 9a8909c..fe2618d 100644 --- a/Server/requirements.txt +++ b/Server/requirements.txt @@ -1,4 +1,5 @@ # Game Server Dependencies colorama>=0.4.6 # For colored terminal output +pymongo>=4.6.0 # MongoDB driver for Python # Email Requirements -# Standard library dependencies are not listed (socket, threading, json, etc.) \ No newline at end of file +# Standard library dependencies are not listed (socket, threading, json, etc.) \ No newline at end of file diff --git a/Server/test_pet_data_migration.py b/Server/test_pet_data_migration.py new file mode 100644 index 0000000..516a625 --- /dev/null +++ b/Server/test_pet_data_migration.py @@ -0,0 +1,341 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +宠物数据格式迁移测试脚本 +用于验证从旧的嵌套数据格式到新的扁平化数据格式的迁移是否正确 +""" + +import json +import sys +import os + +# 添加当前目录到Python路径 +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +def test_old_to_new_format_conversion(): + """测试旧格式到新格式的转换""" + print("=== 测试旧格式到新格式的转换 ===") + + # 模拟旧格式的宠物数据 + old_pet_data = { + "基本信息": { + "宠物ID": "pet_001", + "宠物名称": "小火龙", + "宠物类型": "火系", + "拥有者": "player123", + "场景路径": "res://Scene/Pet/FireDragon.tscn" + }, + "等级经验": { + "等级": 5, + "经验值": 150, + "最大经验值": 200 + }, + "生命与防御": { + "当前生命值": 80, + "最大生命值": 100, + "最大护甲值": 20 + }, + "基础攻击属性": { + "攻击伤害": 25 + }, + "移动与闪避": { + "移动速度": 150 + }, + "亲密度": 75 + } + + # 转换为新格式 + def convert_to_new_format(old_data): + """将旧格式转换为新格式""" + basic_info = old_data.get("基本信息", {}) + level_exp = old_data.get("等级经验", {}) + health_defense = old_data.get("生命与防御", {}) + attack_attrs = old_data.get("基础攻击属性", {}) + movement = old_data.get("移动与闪避", {}) + + return { + "pet_id": basic_info.get("宠物ID", ""), + "pet_name": basic_info.get("宠物名称", ""), + "pet_type": basic_info.get("宠物类型", ""), + "pet_owner": basic_info.get("拥有者", ""), + "pet_image": basic_info.get("场景路径", ""), + "pet_level": level_exp.get("等级", 1), + "pet_experience": level_exp.get("经验值", 0), + "pet_max_experience": level_exp.get("最大经验值", 100), + "pet_current_health": health_defense.get("当前生命值", 100), + "pet_max_health": health_defense.get("最大生命值", 100), + "pet_max_armor": health_defense.get("最大护甲值", 0), + "pet_attack_damage": attack_attrs.get("攻击伤害", 10), + "pet_move_speed": movement.get("移动速度", 100), + "pet_intimacy": old_data.get("亲密度", 0) + } + + new_pet_data = convert_to_new_format(old_pet_data) + + print("旧格式数据:") + print(json.dumps(old_pet_data, ensure_ascii=False, indent=2)) + print("\n新格式数据:") + print(json.dumps(new_pet_data, ensure_ascii=False, indent=2)) + + # 验证转换结果 + assert new_pet_data["pet_id"] == "pet_001" + assert new_pet_data["pet_name"] == "小火龙" + assert new_pet_data["pet_type"] == "火系" + assert new_pet_data["pet_owner"] == "player123" + assert new_pet_data["pet_level"] == 5 + assert new_pet_data["pet_experience"] == 150 + assert new_pet_data["pet_max_experience"] == 200 + assert new_pet_data["pet_current_health"] == 80 + assert new_pet_data["pet_max_health"] == 100 + assert new_pet_data["pet_max_armor"] == 20 + assert new_pet_data["pet_attack_damage"] == 25 + assert new_pet_data["pet_move_speed"] == 150 + assert new_pet_data["pet_intimacy"] == 75 + + print("✅ 旧格式到新格式转换测试通过") + return new_pet_data + +def test_new_format_operations(pet_data): + """测试新格式数据的各种操作""" + print("\n=== 测试新格式数据操作 ===") + + # 测试宠物升级 + def level_up_pet(pet): + """模拟宠物升级""" + pet = pet.copy() + pet["pet_level"] += 1 + pet["pet_experience"] = 0 + pet["pet_max_experience"] = pet["pet_level"] * 100 + pet["pet_max_health"] += 10 + pet["pet_current_health"] = pet["pet_max_health"] + pet["pet_attack_damage"] += 5 + return pet + + # 测试宠物喂食 + def feed_pet(pet, exp_gain=20): + """模拟宠物喂食""" + pet = pet.copy() + pet["pet_experience"] = min(pet["pet_experience"] + exp_gain, pet["pet_max_experience"]) + pet["pet_intimacy"] = min(pet["pet_intimacy"] + 5, 100) + return pet + + # 测试宠物治疗 + def heal_pet(pet, heal_amount=20): + """模拟宠物治疗""" + pet = pet.copy() + pet["pet_current_health"] = min(pet["pet_current_health"] + heal_amount, pet["pet_max_health"]) + return pet + + print("原始宠物数据:") + print(f"等级: {pet_data['pet_level']}, 经验: {pet_data['pet_experience']}/{pet_data['pet_max_experience']}") + print(f"生命值: {pet_data['pet_current_health']}/{pet_data['pet_max_health']}") + print(f"攻击力: {pet_data['pet_attack_damage']}, 亲密度: {pet_data['pet_intimacy']}") + + # 测试喂食 + fed_pet = feed_pet(pet_data) + print("\n喂食后:") + print(f"经验: {fed_pet['pet_experience']}/{fed_pet['pet_max_experience']}") + print(f"亲密度: {fed_pet['pet_intimacy']}") + + # 测试升级 + leveled_pet = level_up_pet(fed_pet) + print("\n升级后:") + print(f"等级: {leveled_pet['pet_level']}, 经验: {leveled_pet['pet_experience']}/{leveled_pet['pet_max_experience']}") + print(f"生命值: {leveled_pet['pet_current_health']}/{leveled_pet['pet_max_health']}") + print(f"攻击力: {leveled_pet['pet_attack_damage']}") + + # 测试治疗 + # 先模拟受伤 + injured_pet = leveled_pet.copy() + injured_pet["pet_current_health"] = 50 + print("\n受伤后:") + print(f"生命值: {injured_pet['pet_current_health']}/{injured_pet['pet_max_health']}") + + healed_pet = heal_pet(injured_pet) + print("\n治疗后:") + print(f"生命值: {healed_pet['pet_current_health']}/{healed_pet['pet_max_health']}") + + print("✅ 新格式数据操作测试通过") + +def test_pet_bag_operations(): + """测试宠物背包操作""" + print("\n=== 测试宠物背包操作 ===") + + # 创建测试宠物背包 + pet_bag = [ + { + "pet_id": "pet_001", + "pet_name": "小火龙", + "pet_type": "火系", + "pet_owner": "player123", + "pet_image": "res://Scene/Pet/FireDragon.tscn", + "pet_level": 5, + "pet_experience": 150, + "pet_max_experience": 200, + "pet_current_health": 80, + "pet_max_health": 100, + "pet_max_armor": 20, + "pet_attack_damage": 25, + "pet_move_speed": 150, + "pet_intimacy": 75 + }, + { + "pet_id": "pet_002", + "pet_name": "水精灵", + "pet_type": "水系", + "pet_owner": "player123", + "pet_image": "res://Scene/Pet/WaterSpirit.tscn", + "pet_level": 3, + "pet_experience": 80, + "pet_max_experience": 150, + "pet_current_health": 60, + "pet_max_health": 80, + "pet_max_armor": 15, + "pet_attack_damage": 20, + "pet_move_speed": 120, + "pet_intimacy": 50 + } + ] + + print(f"宠物背包中有 {len(pet_bag)} 只宠物") + + # 测试遍历宠物背包 + for i, pet in enumerate(pet_bag): + print(f"\n宠物 {i+1}:") + print(f" ID: {pet['pet_id']}") + print(f" 名称: {pet['pet_name']}") + print(f" 类型: {pet['pet_type']}") + print(f" 等级: {pet['pet_level']}") + print(f" 生命值: {pet['pet_current_health']}/{pet['pet_max_health']}") + print(f" 攻击力: {pet['pet_attack_damage']}") + print(f" 亲密度: {pet['pet_intimacy']}") + + # 测试查找特定宠物 + def find_pet_by_id(pet_bag, pet_id): + for pet in pet_bag: + if pet.get("pet_id") == pet_id: + return pet + return None + + found_pet = find_pet_by_id(pet_bag, "pet_002") + if found_pet: + print(f"\n找到宠物: {found_pet['pet_name']} (ID: {found_pet['pet_id']})") + + # 测试按类型筛选宠物 + def filter_pets_by_type(pet_bag, pet_type): + return [pet for pet in pet_bag if pet.get("pet_type") == pet_type] + + fire_pets = filter_pets_by_type(pet_bag, "火系") + print(f"\n火系宠物数量: {len(fire_pets)}") + + # 测试计算总战力 + def calculate_total_power(pet_bag): + total_power = 0 + for pet in pet_bag: + power = pet.get("pet_level", 1) * 10 + pet.get("pet_attack_damage", 0) + pet.get("pet_max_health", 0) + total_power += power + return total_power + + total_power = calculate_total_power(pet_bag) + print(f"\n总战力: {total_power}") + + print("✅ 宠物背包操作测试通过") + +def test_data_validation(): + """测试数据验证""" + print("\n=== 测试数据验证 ===") + + def validate_pet_data(pet): + """验证宠物数据的完整性""" + required_fields = [ + "pet_id", "pet_name", "pet_type", "pet_owner", "pet_image", + "pet_level", "pet_experience", "pet_max_experience", + "pet_current_health", "pet_max_health", "pet_max_armor", + "pet_attack_damage", "pet_move_speed", "pet_intimacy" + ] + + missing_fields = [] + for field in required_fields: + if field not in pet: + missing_fields.append(field) + + if missing_fields: + return False, f"缺少字段: {', '.join(missing_fields)}" + + # 验证数值范围 + if pet["pet_level"] < 1: + return False, "宠物等级不能小于1" + + if pet["pet_experience"] < 0: + return False, "宠物经验值不能为负数" + + if pet["pet_current_health"] > pet["pet_max_health"]: + return False, "当前生命值不能超过最大生命值" + + if pet["pet_intimacy"] < 0 or pet["pet_intimacy"] > 100: + return False, "亲密度必须在0-100之间" + + return True, "数据验证通过" + + # 测试有效数据 + valid_pet = { + "pet_id": "pet_001", + "pet_name": "测试宠物", + "pet_type": "普通", + "pet_owner": "player123", + "pet_image": "res://Scene/Pet/Test.tscn", + "pet_level": 1, + "pet_experience": 0, + "pet_max_experience": 100, + "pet_current_health": 100, + "pet_max_health": 100, + "pet_max_armor": 0, + "pet_attack_damage": 10, + "pet_move_speed": 100, + "pet_intimacy": 0 + } + + is_valid, message = validate_pet_data(valid_pet) + print(f"有效数据验证: {message}") + assert is_valid, "有效数据应该通过验证" + + # 测试无效数据 + invalid_pet = valid_pet.copy() + del invalid_pet["pet_name"] # 删除必需字段 + + is_valid, message = validate_pet_data(invalid_pet) + print(f"无效数据验证: {message}") + assert not is_valid, "无效数据应该不通过验证" + + print("✅ 数据验证测试通过") + +def main(): + """主测试函数""" + print("开始宠物数据格式迁移测试...\n") + + try: + # 测试格式转换 + new_pet_data = test_old_to_new_format_conversion() + + # 测试新格式操作 + test_new_format_operations(new_pet_data) + + # 测试宠物背包操作 + test_pet_bag_operations() + + # 测试数据验证 + test_data_validation() + + print("\n🎉 所有测试通过!宠物数据格式迁移工作正常。") + + except Exception as e: + print(f"\n❌ 测试失败: {str(e)}") + import traceback + traceback.print_exc() + return False + + return True + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/Server/test_player_data_mongodb_migration.py b/Server/test_player_data_mongodb_migration.py new file mode 100644 index 0000000..e68ddbb --- /dev/null +++ b/Server/test_player_data_mongodb_migration.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +玩家数据MongoDB迁移测试脚本 +作者: AI Assistant +功能: 测试玩家数据从JSON文件到MongoDB的迁移和操作功能 +""" + +import json +import sys +import os +import time +from SMYMongoDBAPI import SMYMongoDBAPI + +def test_player_data_operations(): + """测试玩家数据操作""" + print("=== 玩家数据MongoDB操作测试 ===") + + # 1. 连接MongoDB + print("\n1. 连接MongoDB...") + try: + api = SMYMongoDBAPI("test") + if not api.is_connected(): + print("❌ MongoDB连接失败") + return False + print("✅ MongoDB连接成功") + except Exception as e: + print(f"❌ MongoDB连接异常: {e}") + return False + + # 2. 测试获取玩家数据 + print("\n2. 测试获取玩家数据...") + test_accounts = ["2143323382", "2804775686", "3205788256"] + + for account_id in test_accounts: + try: + player_data = api.get_player_data(account_id) + if player_data: + print(f"✅ 成功获取玩家 {account_id} 的数据") + print(f" 昵称: {player_data.get('玩家昵称', 'N/A')}") + print(f" 等级: {player_data.get('等级', 'N/A')}") + print(f" 金币: {player_data.get('钱币', 'N/A')}") + print(f" 农场土地数量: {len(player_data.get('农场土地', []))}") + else: + print(f"⚠️ 未找到玩家 {account_id} 的数据") + except Exception as e: + print(f"❌ 获取玩家 {account_id} 数据时异常: {e}") + + # 3. 测试获取所有玩家基本信息 + print("\n3. 测试获取所有玩家基本信息...") + try: + players_info = api.get_all_players_basic_info() + print(f"✅ 成功获取 {len(players_info)} 个玩家的基本信息") + + for i, player in enumerate(players_info[:3]): # 只显示前3个 + print(f" 玩家{i+1}: {player.get('玩家账号')} - {player.get('玩家昵称')} (等级{player.get('等级')})") + + if len(players_info) > 3: + print(f" ... 还有 {len(players_info) - 3} 个玩家") + + except Exception as e: + print(f"❌ 获取玩家基本信息时异常: {e}") + + # 4. 测试统计玩家总数 + print("\n4. 测试统计玩家总数...") + try: + total_count = api.count_total_players() + print(f"✅ 玩家总数: {total_count}") + except Exception as e: + print(f"❌ 统计玩家总数时异常: {e}") + + # 5. 测试获取离线玩家 + print("\n5. 测试获取离线玩家...") + try: + offline_players = api.get_offline_players(offline_days=1) # 1天内未登录 + print(f"✅ 找到 {len(offline_players)} 个离线超过1天的玩家") + + for player in offline_players[:3]: # 只显示前3个 + account_id = player.get('玩家账号') + last_login = player.get('最后登录时间', 'N/A') + print(f" {account_id}: 最后登录 {last_login}") + + except Exception as e: + print(f"❌ 获取离线玩家时异常: {e}") + + # 6. 测试更新玩家字段 + print("\n6. 测试更新玩家字段...") + if test_accounts: + test_account = test_accounts[0] + try: + # 更新测试字段 + update_fields = { + "测试字段": f"测试时间_{int(time.time())}", + "测试更新": True + } + + success = api.update_player_field(test_account, update_fields) + if success: + print(f"✅ 成功更新玩家 {test_account} 的字段") + + # 验证更新 + updated_data = api.get_player_data(test_account) + if updated_data and "测试字段" in updated_data: + print(f" 验证成功: 测试字段 = {updated_data['测试字段']}") + else: + print("⚠️ 更新验证失败") + else: + print(f"❌ 更新玩家 {test_account} 字段失败") + + except Exception as e: + print(f"❌ 更新玩家字段时异常: {e}") + + # 7. 测试条件查询 + print("\n7. 测试条件查询...") + try: + # 查询等级大于等于5的玩家 + condition = {"等级": {"$gte": 5}} + projection = {"玩家账号": 1, "玩家昵称": 1, "等级": 1, "钱币": 1} + + high_level_players = api.get_players_by_condition(condition, projection, limit=5) + print(f"✅ 找到 {len(high_level_players)} 个等级≥5的玩家") + + for player in high_level_players: + print(f" {player.get('玩家账号')}: {player.get('玩家昵称')} (等级{player.get('等级')}, 金币{player.get('钱币')})") + + except Exception as e: + print(f"❌ 条件查询时异常: {e}") + + # 8. 性能测试 + print("\n8. 性能测试...") + try: + start_time = time.time() + + # 批量获取玩家基本信息 + players_info = api.get_all_players_basic_info() + + end_time = time.time() + duration = end_time - start_time + + print(f"✅ 获取 {len(players_info)} 个玩家基本信息耗时: {duration:.3f} 秒") + + if duration < 1.0: + print(" 性能良好 ✅") + elif duration < 3.0: + print(" 性能一般 ⚠️") + else: + print(" 性能较差,建议优化 ❌") + + except Exception as e: + print(f"❌ 性能测试时异常: {e}") + + print("\n=== 测试完成 ===") + return True + +def test_compatibility_with_file_system(): + """测试与文件系统的兼容性""" + print("\n=== 文件系统兼容性测试 ===") + + try: + # 模拟服务器环境 + from TCPGameServer import TCPGameServer + + # 创建服务器实例(不启动网络服务) + server = TCPGameServer() + + # 测试加载玩家数据 + test_account = "2143323382" + + print(f"\n测试加载玩家数据: {test_account}") + player_data = server.load_player_data(test_account) + + if player_data: + print("✅ 成功加载玩家数据") + print(f" 数据源: {'MongoDB' if server.use_mongodb else '文件系统'}") + print(f" 玩家昵称: {player_data.get('玩家昵称', 'N/A')}") + print(f" 等级: {player_data.get('等级', 'N/A')}") + + # 测试保存玩家数据 + print("\n测试保存玩家数据...") + player_data["测试兼容性"] = f"测试时间_{int(time.time())}" + + success = server.save_player_data(test_account, player_data) + if success: + print("✅ 成功保存玩家数据") + + # 验证保存 + reloaded_data = server.load_player_data(test_account) + if reloaded_data and "测试兼容性" in reloaded_data: + print("✅ 保存验证成功") + else: + print("❌ 保存验证失败") + else: + print("❌ 保存玩家数据失败") + else: + print("❌ 加载玩家数据失败") + + except Exception as e: + print(f"❌ 兼容性测试异常: {e}") + import traceback + traceback.print_exc() + +def main(): + """主函数""" + try: + # 基本操作测试 + test_player_data_operations() + + # 兼容性测试 + test_compatibility_with_file_system() + + except KeyboardInterrupt: + print("\n测试被用户中断") + except Exception as e: + print(f"测试过程中发生异常: {e}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/Server/test_server_pet_functions.py b/Server/test_server_pet_functions.py new file mode 100644 index 0000000..4147b1e --- /dev/null +++ b/Server/test_server_pet_functions.py @@ -0,0 +1,401 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +服务器宠物功能测试脚本 +用于测试TCPGameServer中宠物相关功能是否正常工作 +""" + +import json +import sys +import os +from unittest.mock import Mock, patch + +# 添加当前目录到Python路径 +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +def test_pet_data_conversion_functions(): + """测试宠物数据转换函数""" + print("=== 测试宠物数据转换函数 ===") + + # 模拟TCPGameServer类的部分方法 + class MockTCPGameServer: + def _convert_patrol_pets_to_full_data(self, patrol_pets): + """模拟巡逻宠物数据转换""" + full_pets = [] + for pet in patrol_pets: + # 使用新的扁平化数据格式 + scene_path = pet.get("pet_image", "") + full_pet = { + "pet_id": pet.get("pet_id", ""), + "pet_name": pet.get("pet_name", ""), + "pet_type": pet.get("pet_type", ""), + "pet_level": pet.get("pet_level", 1), + "pet_current_health": pet.get("pet_current_health", 100), + "pet_max_health": pet.get("pet_max_health", 100), + "pet_attack_damage": pet.get("pet_attack_damage", 10), + "pet_move_speed": pet.get("pet_move_speed", 100), + "scene_path": scene_path + } + full_pets.append(full_pet) + return full_pets + + def _convert_battle_pets_to_full_data(self, battle_pets): + """模拟战斗宠物数据转换""" + return self._convert_patrol_pets_to_full_data(battle_pets) + + def _player_has_pet(self, pet_bag, pet_type): + """检查玩家是否拥有指定类型的宠物""" + for pet in pet_bag: + if pet.get("pet_type", "") == pet_type: + return True + return False + + server = MockTCPGameServer() + + # 测试数据 + test_pets = [ + { + "pet_id": "pet_001", + "pet_name": "小火龙", + "pet_type": "火系", + "pet_level": 5, + "pet_current_health": 80, + "pet_max_health": 100, + "pet_attack_damage": 25, + "pet_move_speed": 150, + "pet_image": "res://Scene/Pet/FireDragon.tscn" + }, + { + "pet_id": "pet_002", + "pet_name": "水精灵", + "pet_type": "水系", + "pet_level": 3, + "pet_current_health": 60, + "pet_max_health": 80, + "pet_attack_damage": 20, + "pet_move_speed": 120, + "pet_image": "res://Scene/Pet/WaterSpirit.tscn" + } + ] + + # 测试巡逻宠物转换 + patrol_pets = server._convert_patrol_pets_to_full_data(test_pets) + print(f"巡逻宠物转换结果: {len(patrol_pets)} 只宠物") + for pet in patrol_pets: + print(f" {pet['pet_name']} (ID: {pet['pet_id']}) - 场景路径: {pet['scene_path']}") + + # 测试战斗宠物转换 + battle_pets = server._convert_battle_pets_to_full_data(test_pets) + print(f"\n战斗宠物转换结果: {len(battle_pets)} 只宠物") + + # 测试宠物类型检查 + has_fire_pet = server._player_has_pet(test_pets, "火系") + has_grass_pet = server._player_has_pet(test_pets, "草系") + print(f"\n玩家是否拥有火系宠物: {has_fire_pet}") + print(f"玩家是否拥有草系宠物: {has_grass_pet}") + + assert has_fire_pet == True + assert has_grass_pet == False + + print("✅ 宠物数据转换函数测试通过") + +def test_pet_feeding_system(): + """测试宠物喂食系统""" + print("\n=== 测试宠物喂食系统 ===") + + class MockTCPGameServer: + def _process_pet_feeding(self, pet_data, food_item): + """模拟宠物喂食处理""" + # 使用新的扁平化数据格式 + exp_gain = food_item.get("经验加成", 10) + intimacy_gain = food_item.get("亲密度加成", 5) + + # 更新宠物数据 + pet_data["pet_experience"] = min( + pet_data.get("pet_experience", 0) + exp_gain, + pet_data.get("pet_max_experience", 100) + ) + pet_data["pet_intimacy"] = min( + pet_data.get("pet_intimacy", 0) + intimacy_gain, + 100 + ) + + return { + "success": True, + "message": f"{pet_data['pet_name']} 获得了 {exp_gain} 经验和 {intimacy_gain} 亲密度", + "pet_data": pet_data + } + + def _apply_level_up_bonus(self, pet_data): + """模拟宠物升级加成""" + level = pet_data.get("pet_level", 1) + + # 使用新的扁平化数据格式 + pet_data["pet_max_health"] = pet_data.get("pet_max_health", 100) + 10 + pet_data["pet_max_armor"] = pet_data.get("pet_max_armor", 0) + 2 + pet_data["pet_attack_damage"] = pet_data.get("pet_attack_damage", 10) + 5 + pet_data["pet_move_speed"] = pet_data.get("pet_move_speed", 100) + 5 + + # 恢复满血 + pet_data["pet_current_health"] = pet_data["pet_max_health"] + + return pet_data + + server = MockTCPGameServer() + + # 测试宠物数据 + pet_data = { + "pet_id": "pet_001", + "pet_name": "小火龙", + "pet_type": "火系", + "pet_level": 5, + "pet_experience": 180, + "pet_max_experience": 200, + "pet_current_health": 80, + "pet_max_health": 100, + "pet_max_armor": 20, + "pet_attack_damage": 25, + "pet_move_speed": 150, + "pet_intimacy": 75 + } + + # 测试食物道具 + food_item = { + "物品名称": "高级宠物食物", + "经验加成": 25, + "亲密度加成": 10 + } + + print(f"喂食前: {pet_data['pet_name']} - 经验: {pet_data['pet_experience']}/{pet_data['pet_max_experience']}, 亲密度: {pet_data['pet_intimacy']}") + + # 执行喂食 + result = server._process_pet_feeding(pet_data, food_item) + + if result["success"]: + updated_pet = result["pet_data"] + print(f"喂食后: {updated_pet['pet_name']} - 经验: {updated_pet['pet_experience']}/{updated_pet['pet_max_experience']}, 亲密度: {updated_pet['pet_intimacy']}") + print(f"消息: {result['message']}") + + # 检查是否需要升级 + if updated_pet["pet_experience"] >= updated_pet["pet_max_experience"]: + print("\n宠物可以升级!") + updated_pet["pet_level"] += 1 + updated_pet["pet_experience"] = 0 + updated_pet["pet_max_experience"] = updated_pet["pet_level"] * 100 + + # 应用升级加成 + updated_pet = server._apply_level_up_bonus(updated_pet) + print(f"升级后: {updated_pet['pet_name']} - 等级: {updated_pet['pet_level']}, 生命值: {updated_pet['pet_current_health']}/{updated_pet['pet_max_health']}, 攻击力: {updated_pet['pet_attack_damage']}") + + print("✅ 宠物喂食系统测试通过") + +def test_pet_item_usage(): + """测试宠物道具使用""" + print("\n=== 测试宠物道具使用 ===") + + class MockTCPGameServer: + def _process_pet_item_use(self, pet_data, item_data): + """模拟宠物道具使用处理""" + item_name = item_data.get("物品名称", "") + + # 使用新的扁平化数据格式获取宠物名称 + pet_name = pet_data.get("pet_name", "未知宠物") + + if "治疗" in item_name: + # 治疗道具 + heal_amount = item_data.get("治疗量", 20) + pet_data["pet_current_health"] = min( + pet_data.get("pet_current_health", 0) + heal_amount, + pet_data.get("pet_max_health", 100) + ) + return { + "success": True, + "message": f"{pet_name} 使用了 {item_name},恢复了 {heal_amount} 生命值" + } + elif "经验" in item_name: + # 经验道具 + exp_gain = item_data.get("经验加成", 50) + pet_data["pet_experience"] = min( + pet_data.get("pet_experience", 0) + exp_gain, + pet_data.get("pet_max_experience", 100) + ) + return { + "success": True, + "message": f"{pet_name} 使用了 {item_name},获得了 {exp_gain} 经验值" + } + else: + return { + "success": False, + "message": f"未知的道具类型: {item_name}" + } + + server = MockTCPGameServer() + + # 测试宠物数据 + pet_data = { + "pet_id": "pet_001", + "pet_name": "小火龙", + "pet_type": "火系", + "pet_level": 3, + "pet_experience": 50, + "pet_max_experience": 150, + "pet_current_health": 40, + "pet_max_health": 80, + "pet_attack_damage": 20, + "pet_intimacy": 60 + } + + # 测试治疗道具 + heal_item = { + "物品名称": "高级治疗药水", + "治疗量": 30 + } + + print(f"使用治疗道具前: {pet_data['pet_name']} - 生命值: {pet_data['pet_current_health']}/{pet_data['pet_max_health']}") + + result = server._process_pet_item_use(pet_data, heal_item) + if result["success"]: + print(f"使用治疗道具后: {pet_data['pet_name']} - 生命值: {pet_data['pet_current_health']}/{pet_data['pet_max_health']}") + print(f"消息: {result['message']}") + + # 测试经验道具 + exp_item = { + "物品名称": "经验药水", + "经验加成": 80 + } + + print(f"\n使用经验道具前: {pet_data['pet_name']} - 经验: {pet_data['pet_experience']}/{pet_data['pet_max_experience']}") + + result = server._process_pet_item_use(pet_data, exp_item) + if result["success"]: + print(f"使用经验道具后: {pet_data['pet_name']} - 经验: {pet_data['pet_experience']}/{pet_data['pet_max_experience']}") + print(f"消息: {result['message']}") + + print("✅ 宠物道具使用测试通过") + +def test_pet_bag_operations(): + """测试宠物背包操作""" + print("\n=== 测试宠物背包操作 ===") + + # 模拟宠物背包数据 + pet_bag = [ + { + "pet_id": "pet_001", + "pet_name": "小火龙", + "pet_type": "火系", + "pet_owner": "player123", + "pet_image": "res://Scene/Pet/FireDragon.tscn", + "pet_level": 5, + "pet_experience": 150, + "pet_max_experience": 200, + "pet_current_health": 80, + "pet_max_health": 100, + "pet_max_armor": 20, + "pet_attack_damage": 25, + "pet_move_speed": 150, + "pet_intimacy": 75 + }, + { + "pet_id": "pet_002", + "pet_name": "水精灵", + "pet_type": "水系", + "pet_owner": "player123", + "pet_image": "res://Scene/Pet/WaterSpirit.tscn", + "pet_level": 3, + "pet_experience": 80, + "pet_max_experience": 150, + "pet_current_health": 60, + "pet_max_health": 80, + "pet_max_armor": 15, + "pet_attack_damage": 20, + "pet_move_speed": 120, + "pet_intimacy": 50 + } + ] + + print(f"宠物背包中有 {len(pet_bag)} 只宠物") + + # 测试遍历宠物背包(模拟TCPGameServer中的for pet in pet_bag循环) + print("\n遍历宠物背包:") + for pet in pet_bag: + # 使用新的扁平化数据格式 + pet_id = pet.get("pet_id", "") + pet_name = pet.get("pet_name", "") + pet_type = pet.get("pet_type", "") + pet_level = pet.get("pet_level", 1) + pet_health = pet.get("pet_current_health", 0) + pet_max_health = pet.get("pet_max_health", 100) + pet_attack = pet.get("pet_attack_damage", 10) + pet_intimacy = pet.get("pet_intimacy", 0) + + print(f" 宠物ID: {pet_id}") + print(f" 名称: {pet_name} ({pet_type})") + print(f" 等级: {pet_level}") + print(f" 生命值: {pet_health}/{pet_max_health}") + print(f" 攻击力: {pet_attack}") + print(f" 亲密度: {pet_intimacy}") + print(" ---") + + # 测试查找特定宠物 + target_pet_id = "pet_002" + found_pet = None + for pet in pet_bag: + if pet.get("pet_id") == target_pet_id: + found_pet = pet + break + + if found_pet: + print(f"\n找到宠物 {target_pet_id}: {found_pet['pet_name']}") + else: + print(f"\n未找到宠物 {target_pet_id}") + + # 测试统计信息 + total_pets = len(pet_bag) + total_level = sum(pet.get("pet_level", 1) for pet in pet_bag) + avg_level = total_level / total_pets if total_pets > 0 else 0 + total_intimacy = sum(pet.get("pet_intimacy", 0) for pet in pet_bag) + avg_intimacy = total_intimacy / total_pets if total_pets > 0 else 0 + + print(f"\n统计信息:") + print(f" 总宠物数: {total_pets}") + print(f" 平均等级: {avg_level:.1f}") + print(f" 平均亲密度: {avg_intimacy:.1f}") + + print("✅ 宠物背包操作测试通过") + +def main(): + """主测试函数""" + print("开始服务器宠物功能测试...\n") + + try: + # 测试宠物数据转换函数 + test_pet_data_conversion_functions() + + # 测试宠物喂食系统 + test_pet_feeding_system() + + # 测试宠物道具使用 + test_pet_item_usage() + + # 测试宠物背包操作 + test_pet_bag_operations() + + print("\n🎉 所有服务器宠物功能测试通过!") + print("\n✅ 确认事项:") + print(" - 宠物数据转换函数正常工作") + print(" - 宠物喂食系统使用新的扁平化数据格式") + print(" - 宠物道具使用系统正确访问宠物名称") + print(" - 宠物背包遍历操作正常") + print(" - 所有宠物相关功能已适配新数据格式") + + except Exception as e: + print(f"\n❌ 测试失败: {str(e)}") + import traceback + traceback.print_exc() + return False + + return True + +if __name__ == "__main__": + success = main() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/__pycache__/SMYMongoDBAPI.cpython-313.pyc b/__pycache__/SMYMongoDBAPI.cpython-313.pyc deleted file mode 100644 index 767d4e9..0000000 Binary files a/__pycache__/SMYMongoDBAPI.cpython-313.pyc and /dev/null differ diff --git a/assets/子弹图片/01.png b/assets/子弹图片/01.png new file mode 100644 index 0000000..aab809b Binary files /dev/null and b/assets/子弹图片/01.png differ diff --git a/assets/子弹图片/01.png.import b/assets/子弹图片/01.png.import new file mode 100644 index 0000000..17fb22d --- /dev/null +++ b/assets/子弹图片/01.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://by01joyt7e4qh" +path="res://.godot/imported/01.png-95d14629d6075888bdfa42cfcf0c603f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/01.png" +dest_files=["res://.godot/imported/01.png-95d14629d6075888bdfa42cfcf0c603f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/02.png b/assets/子弹图片/02.png new file mode 100644 index 0000000..22048fd Binary files /dev/null and b/assets/子弹图片/02.png differ diff --git a/assets/子弹图片/02.png.import b/assets/子弹图片/02.png.import new file mode 100644 index 0000000..7141a0a --- /dev/null +++ b/assets/子弹图片/02.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cw337up7enoqo" +path="res://.godot/imported/02.png-8c04c6e2ccab9682d7ebe159c604764a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/02.png" +dest_files=["res://.godot/imported/02.png-8c04c6e2ccab9682d7ebe159c604764a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/03.png b/assets/子弹图片/03.png new file mode 100644 index 0000000..65cfe37 Binary files /dev/null and b/assets/子弹图片/03.png differ diff --git a/assets/子弹图片/03.png.import b/assets/子弹图片/03.png.import new file mode 100644 index 0000000..4c3a75c --- /dev/null +++ b/assets/子弹图片/03.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bf27ywrn16wcv" +path="res://.godot/imported/03.png-dab68a06be5dfaf312394a4d0cee9345.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/03.png" +dest_files=["res://.godot/imported/03.png-dab68a06be5dfaf312394a4d0cee9345.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/04.png b/assets/子弹图片/04.png new file mode 100644 index 0000000..a4a8e26 Binary files /dev/null and b/assets/子弹图片/04.png differ diff --git a/assets/子弹图片/04.png.import b/assets/子弹图片/04.png.import new file mode 100644 index 0000000..95c7c40 --- /dev/null +++ b/assets/子弹图片/04.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bwfr3uilkxq6a" +path="res://.godot/imported/04.png-847c786ac8c8e35fec1adc87f18c6efa.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/04.png" +dest_files=["res://.godot/imported/04.png-847c786ac8c8e35fec1adc87f18c6efa.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/05.png b/assets/子弹图片/05.png new file mode 100644 index 0000000..8790292 Binary files /dev/null and b/assets/子弹图片/05.png differ diff --git a/assets/子弹图片/05.png.import b/assets/子弹图片/05.png.import new file mode 100644 index 0000000..cf1882c --- /dev/null +++ b/assets/子弹图片/05.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c20yr5fi7byd" +path="res://.godot/imported/05.png-d36e682cb08cded77cc6343a325c052f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/05.png" +dest_files=["res://.godot/imported/05.png-d36e682cb08cded77cc6343a325c052f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/06.png b/assets/子弹图片/06.png new file mode 100644 index 0000000..8ced12c Binary files /dev/null and b/assets/子弹图片/06.png differ diff --git a/assets/子弹图片/06.png.import b/assets/子弹图片/06.png.import new file mode 100644 index 0000000..34321c9 --- /dev/null +++ b/assets/子弹图片/06.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bavb0dpa8pbsd" +path="res://.godot/imported/06.png-553dafed3a6433f4ccb08f17c6350162.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/06.png" +dest_files=["res://.godot/imported/06.png-553dafed3a6433f4ccb08f17c6350162.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/07.png b/assets/子弹图片/07.png new file mode 100644 index 0000000..61d6392 Binary files /dev/null and b/assets/子弹图片/07.png differ diff --git a/assets/子弹图片/07.png.import b/assets/子弹图片/07.png.import new file mode 100644 index 0000000..fb5e277 --- /dev/null +++ b/assets/子弹图片/07.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dh1wy2mn54a36" +path="res://.godot/imported/07.png-585fa9b6df3f18f4c8863246085a00f3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/07.png" +dest_files=["res://.godot/imported/07.png-585fa9b6df3f18f4c8863246085a00f3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/08.png b/assets/子弹图片/08.png new file mode 100644 index 0000000..571aaca Binary files /dev/null and b/assets/子弹图片/08.png differ diff --git a/assets/子弹图片/08.png.import b/assets/子弹图片/08.png.import new file mode 100644 index 0000000..c2a26dd --- /dev/null +++ b/assets/子弹图片/08.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dqwum77jcqkdy" +path="res://.godot/imported/08.png-5040cae4ec067dac0757eaa028cf89c3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/08.png" +dest_files=["res://.godot/imported/08.png-5040cae4ec067dac0757eaa028cf89c3.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/09.png b/assets/子弹图片/09.png new file mode 100644 index 0000000..044ed62 Binary files /dev/null and b/assets/子弹图片/09.png differ diff --git a/assets/子弹图片/09.png.import b/assets/子弹图片/09.png.import new file mode 100644 index 0000000..52d46d2 --- /dev/null +++ b/assets/子弹图片/09.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cleyjr7css15h" +path="res://.godot/imported/09.png-9238e10e67248408a3bf17849b66aceb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/09.png" +dest_files=["res://.godot/imported/09.png-9238e10e67248408a3bf17849b66aceb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/21.png b/assets/子弹图片/21.png new file mode 100644 index 0000000..4c382a8 Binary files /dev/null and b/assets/子弹图片/21.png differ diff --git a/assets/子弹图片/21.png.import b/assets/子弹图片/21.png.import new file mode 100644 index 0000000..a199dab --- /dev/null +++ b/assets/子弹图片/21.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://csmi7hdbgjqjv" +path="res://.godot/imported/21.png-5303d7aebe0a4b42c955d30435004edf.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/21.png" +dest_files=["res://.godot/imported/21.png-5303d7aebe0a4b42c955d30435004edf.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/22.png b/assets/子弹图片/22.png new file mode 100644 index 0000000..8d15d48 Binary files /dev/null and b/assets/子弹图片/22.png differ diff --git a/assets/子弹图片/22.png.import b/assets/子弹图片/22.png.import new file mode 100644 index 0000000..c983218 --- /dev/null +++ b/assets/子弹图片/22.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bknpmcntsy5lb" +path="res://.godot/imported/22.png-570b7d75cb7a42beedccaf1ee60b755d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/22.png" +dest_files=["res://.godot/imported/22.png-570b7d75cb7a42beedccaf1ee60b755d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/23.png b/assets/子弹图片/23.png new file mode 100644 index 0000000..6b5d98d Binary files /dev/null and b/assets/子弹图片/23.png differ diff --git a/assets/子弹图片/23.png.import b/assets/子弹图片/23.png.import new file mode 100644 index 0000000..753c32a --- /dev/null +++ b/assets/子弹图片/23.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://htnv7xp8fpu4" +path="res://.godot/imported/23.png-211603348421cbb9dc3c19157b492009.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/23.png" +dest_files=["res://.godot/imported/23.png-211603348421cbb9dc3c19157b492009.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/24.png b/assets/子弹图片/24.png new file mode 100644 index 0000000..a5e3493 Binary files /dev/null and b/assets/子弹图片/24.png differ diff --git a/assets/子弹图片/24.png.import b/assets/子弹图片/24.png.import new file mode 100644 index 0000000..e060408 --- /dev/null +++ b/assets/子弹图片/24.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://qwjqjsp56d5s" +path="res://.godot/imported/24.png-fc0117a03f48e5c6c011413ae5ff24ec.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/24.png" +dest_files=["res://.godot/imported/24.png-fc0117a03f48e5c6c011413ae5ff24ec.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/25.png b/assets/子弹图片/25.png new file mode 100644 index 0000000..ee52a21 Binary files /dev/null and b/assets/子弹图片/25.png differ diff --git a/assets/子弹图片/25.png.import b/assets/子弹图片/25.png.import new file mode 100644 index 0000000..93add2a --- /dev/null +++ b/assets/子弹图片/25.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://omdy6jm6mbbn" +path="res://.godot/imported/25.png-5fb7f030a337625c846241a4f9950920.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/25.png" +dest_files=["res://.godot/imported/25.png-5fb7f030a337625c846241a4f9950920.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/26.png b/assets/子弹图片/26.png new file mode 100644 index 0000000..6c92bf9 Binary files /dev/null and b/assets/子弹图片/26.png differ diff --git a/assets/子弹图片/26.png.import b/assets/子弹图片/26.png.import new file mode 100644 index 0000000..adde40b --- /dev/null +++ b/assets/子弹图片/26.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b3av75j22a5ux" +path="res://.godot/imported/26.png-0694d83edb39904e612c803997cab92a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/26.png" +dest_files=["res://.godot/imported/26.png-0694d83edb39904e612c803997cab92a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/30.png b/assets/子弹图片/30.png new file mode 100644 index 0000000..4451318 Binary files /dev/null and b/assets/子弹图片/30.png differ diff --git a/assets/子弹图片/30.png.import b/assets/子弹图片/30.png.import new file mode 100644 index 0000000..aa09651 --- /dev/null +++ b/assets/子弹图片/30.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bq4yl28vrqg0n" +path="res://.godot/imported/30.png-6d3aacaba3ae939e0d65327529fda88c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/30.png" +dest_files=["res://.godot/imported/30.png-6d3aacaba3ae939e0d65327529fda88c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/31.png b/assets/子弹图片/31.png new file mode 100644 index 0000000..1577530 Binary files /dev/null and b/assets/子弹图片/31.png differ diff --git a/assets/子弹图片/31.png.import b/assets/子弹图片/31.png.import new file mode 100644 index 0000000..b31d604 --- /dev/null +++ b/assets/子弹图片/31.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://pcgkxxyh2vtq" +path="res://.godot/imported/31.png-150a0be9a006adebb26ac0942b5f1c58.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/31.png" +dest_files=["res://.godot/imported/31.png-150a0be9a006adebb26ac0942b5f1c58.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/32.png b/assets/子弹图片/32.png new file mode 100644 index 0000000..a57e686 Binary files /dev/null and b/assets/子弹图片/32.png differ diff --git a/assets/子弹图片/32.png.import b/assets/子弹图片/32.png.import new file mode 100644 index 0000000..9798137 --- /dev/null +++ b/assets/子弹图片/32.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bpu8yonk72iqf" +path="res://.godot/imported/32.png-3a0f744edb5eab8df015c04dd875c0e9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/32.png" +dest_files=["res://.godot/imported/32.png-3a0f744edb5eab8df015c04dd875c0e9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/33.png b/assets/子弹图片/33.png new file mode 100644 index 0000000..67cba90 Binary files /dev/null and b/assets/子弹图片/33.png differ diff --git a/assets/子弹图片/33.png.import b/assets/子弹图片/33.png.import new file mode 100644 index 0000000..772563c --- /dev/null +++ b/assets/子弹图片/33.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://gi236uf383q" +path="res://.godot/imported/33.png-90fcdb8f86cd1dc860df962290b9563b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/33.png" +dest_files=["res://.godot/imported/33.png-90fcdb8f86cd1dc860df962290b9563b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/34.png b/assets/子弹图片/34.png new file mode 100644 index 0000000..f50054c Binary files /dev/null and b/assets/子弹图片/34.png differ diff --git a/assets/子弹图片/34.png.import b/assets/子弹图片/34.png.import new file mode 100644 index 0000000..fb0ffc4 --- /dev/null +++ b/assets/子弹图片/34.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://o765weckrou3" +path="res://.godot/imported/34.png-f4de89fd8a4248e753152e3d55e42abe.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/34.png" +dest_files=["res://.godot/imported/34.png-f4de89fd8a4248e753152e3d55e42abe.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/35.png b/assets/子弹图片/35.png new file mode 100644 index 0000000..91c3abd Binary files /dev/null and b/assets/子弹图片/35.png differ diff --git a/assets/子弹图片/35.png.import b/assets/子弹图片/35.png.import new file mode 100644 index 0000000..9f1019b --- /dev/null +++ b/assets/子弹图片/35.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dhng3lrwmxhh4" +path="res://.godot/imported/35.png-415e24507486b77c7eb0a12952d69887.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/35.png" +dest_files=["res://.godot/imported/35.png-415e24507486b77c7eb0a12952d69887.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/子弹图片/36.png b/assets/子弹图片/36.png new file mode 100644 index 0000000..02cd70c Binary files /dev/null and b/assets/子弹图片/36.png differ diff --git a/assets/子弹图片/36.png.import b/assets/子弹图片/36.png.import new file mode 100644 index 0000000..d9776c8 --- /dev/null +++ b/assets/子弹图片/36.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cnbaspakdb5ed" +path="res://.godot/imported/36.png-cf49b3ea30a9f491850986688d95d1c2.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/子弹图片/36.png" +dest_files=["res://.godot/imported/36.png-cf49b3ea30a9f491850986688d95d1c2.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/武器图片/Wands-x2.png b/assets/武器图片/Wands-x2.png new file mode 100644 index 0000000..59a134b Binary files /dev/null and b/assets/武器图片/Wands-x2.png differ diff --git a/assets/武器图片/Wands-x2.png.import b/assets/武器图片/Wands-x2.png.import new file mode 100644 index 0000000..a7284ab --- /dev/null +++ b/assets/武器图片/Wands-x2.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ub2lcdg5j4sg" +path="res://.godot/imported/Wands-x2.png-807b5673d4de85f7585a74ad8964b92f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/武器图片/Wands-x2.png" +dest_files=["res://.godot/imported/Wands-x2.png-807b5673d4de85f7585a74ad8964b92f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/武器图片/bronze outline x2.png b/assets/武器图片/bronze outline x2.png new file mode 100644 index 0000000..f0eba77 Binary files /dev/null and b/assets/武器图片/bronze outline x2.png differ diff --git a/assets/武器图片/bronze outline x2.png.import b/assets/武器图片/bronze outline x2.png.import new file mode 100644 index 0000000..a071281 --- /dev/null +++ b/assets/武器图片/bronze outline x2.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1e0mkrvmbba6" +path="res://.godot/imported/bronze outline x2.png-64202f8c9498d8b1924bad8ecb45e48b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/武器图片/bronze outline x2.png" +dest_files=["res://.godot/imported/bronze outline x2.png-64202f8c9498d8b1924bad8ecb45e48b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/灵创新媒.png b/assets/灵创新媒.png new file mode 100644 index 0000000..16c06c4 Binary files /dev/null and b/assets/灵创新媒.png differ diff --git a/assets/灵创新媒.png.import b/assets/灵创新媒.png.import new file mode 100644 index 0000000..fe32cd4 --- /dev/null +++ b/assets/灵创新媒.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bl1oahn5noklf" +path="res://.godot/imported/灵创新媒.png-9ba820b8b4d65b85c263c0f42fa6c4c1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/灵创新媒.png" +dest_files=["res://.godot/imported/灵创新媒.png-9ba820b8b4d65b85c263c0f42fa6c4c1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/菜单UI/灵创招新群.png b/assets/菜单UI/灵创招新群.png new file mode 100644 index 0000000..a346727 Binary files /dev/null and b/assets/菜单UI/灵创招新群.png differ diff --git a/assets/菜单UI/灵创招新群.png.import b/assets/菜单UI/灵创招新群.png.import new file mode 100644 index 0000000..f65ce23 --- /dev/null +++ b/assets/菜单UI/灵创招新群.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://be4fa6qo525y1" +path="res://.godot/imported/灵创招新群.png-8abd5b3ea34409659bf6db305e49ec8f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/菜单UI/灵创招新群.png" +dest_files=["res://.godot/imported/灵创招新群.png-8abd5b3ea34409659bf6db305e49ec8f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/装饰物图片/作物仓库.webp b/assets/装饰物图片/作物仓库.webp new file mode 100644 index 0000000..a6ae072 Binary files /dev/null and b/assets/装饰物图片/作物仓库.webp differ diff --git a/assets/装饰物图片/作物仓库.webp.import b/assets/装饰物图片/作物仓库.webp.import new file mode 100644 index 0000000..5d97288 --- /dev/null +++ b/assets/装饰物图片/作物仓库.webp.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dfeqilibb6ecs" +path="res://.godot/imported/作物仓库.webp-337a0879e7de5d22dc9f56702a508e80.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/装饰物图片/作物仓库.webp" +dest_files=["res://.godot/imported/作物仓库.webp-337a0879e7de5d22dc9f56702a508e80.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/装饰物图片/在线时长礼包.webp b/assets/装饰物图片/在线时长礼包.webp new file mode 100644 index 0000000..58ed615 Binary files /dev/null and b/assets/装饰物图片/在线时长礼包.webp differ diff --git a/assets/装饰物图片/在线时长礼包.webp.import b/assets/装饰物图片/在线时长礼包.webp.import new file mode 100644 index 0000000..cc22026 --- /dev/null +++ b/assets/装饰物图片/在线时长礼包.webp.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ic2nvi3xlwl4" +path="res://.godot/imported/在线时长礼包.webp-473b0e106777970fe7e873eb25611022.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/装饰物图片/在线时长礼包.webp" +dest_files=["res://.godot/imported/在线时长礼包.webp-473b0e106777970fe7e873eb25611022.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/装饰物图片/宠物背包.webp b/assets/装饰物图片/宠物背包.webp new file mode 100644 index 0000000..2b9f2a7 Binary files /dev/null and b/assets/装饰物图片/宠物背包.webp differ diff --git a/assets/装饰物图片/宠物背包.webp.import b/assets/装饰物图片/宠物背包.webp.import new file mode 100644 index 0000000..572b3b4 --- /dev/null +++ b/assets/装饰物图片/宠物背包.webp.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1xgmal8sw6il" +path="res://.godot/imported/宠物背包.webp-e34fe718c4b689ffef9b3abe8fd9b0be.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/装饰物图片/宠物背包.webp" +dest_files=["res://.godot/imported/宠物背包.webp-e34fe718c4b689ffef9b3abe8fd9b0be.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/装饰物图片/每日签到礼包.webp b/assets/装饰物图片/每日签到礼包.webp new file mode 100644 index 0000000..f2d2bf0 Binary files /dev/null and b/assets/装饰物图片/每日签到礼包.webp differ diff --git a/assets/装饰物图片/每日签到礼包.webp.import b/assets/装饰物图片/每日签到礼包.webp.import new file mode 100644 index 0000000..7b5c232 --- /dev/null +++ b/assets/装饰物图片/每日签到礼包.webp.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://deh0dnprkw155" +path="res://.godot/imported/每日签到礼包.webp-d7bb294e26a7b226f74642c81d63371c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/装饰物图片/每日签到礼包.webp" +dest_files=["res://.godot/imported/每日签到礼包.webp-d7bb294e26a7b226f74642c81d63371c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/装饰物图片/玩家排行榜.webp b/assets/装饰物图片/玩家排行榜.webp new file mode 100644 index 0000000..01f977f Binary files /dev/null and b/assets/装饰物图片/玩家排行榜.webp differ diff --git a/assets/装饰物图片/玩家排行榜.webp.import b/assets/装饰物图片/玩家排行榜.webp.import new file mode 100644 index 0000000..b051c57 --- /dev/null +++ b/assets/装饰物图片/玩家排行榜.webp.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cbj1s6ctp0utb" +path="res://.godot/imported/玩家排行榜.webp-e24566a7ef861b11b1f9268d2fb33e34.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/装饰物图片/玩家排行榜.webp" +dest_files=["res://.godot/imported/玩家排行榜.webp-e24566a7ef861b11b1f9268d2fb33e34.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/装饰物图片/种子仓库.webp b/assets/装饰物图片/种子仓库.webp new file mode 100644 index 0000000..2ff5d7a Binary files /dev/null and b/assets/装饰物图片/种子仓库.webp differ diff --git a/assets/装饰物图片/种子仓库.webp.import b/assets/装饰物图片/种子仓库.webp.import new file mode 100644 index 0000000..61d1c51 --- /dev/null +++ b/assets/装饰物图片/种子仓库.webp.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b738esrtjlho7" +path="res://.godot/imported/种子仓库.webp-808f273c864c7c31d5e182311f947980.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/装饰物图片/种子仓库.webp" +dest_files=["res://.godot/imported/种子仓库.webp-808f273c864c7c31d5e182311f947980.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/assets/装饰物图片/道具背包.webp b/assets/装饰物图片/道具背包.webp new file mode 100644 index 0000000..2c0f82c Binary files /dev/null and b/assets/装饰物图片/道具背包.webp differ diff --git a/assets/装饰物图片/道具背包.webp.import b/assets/装饰物图片/道具背包.webp.import new file mode 100644 index 0000000..5da696b --- /dev/null +++ b/assets/装饰物图片/道具背包.webp.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ywdg7xgq7hm8" +path="res://.godot/imported/道具背包.webp-426e5d710d831f351a8c169aadf1e00a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/装饰物图片/道具背包.webp" +dest_files=["res://.godot/imported/道具背包.webp-426e5d710d831f351a8c169aadf1e00a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.01 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/project.godot b/project.godot index ccfc166..3b31350 100644 --- a/project.godot +++ b/project.godot @@ -12,11 +12,15 @@ config_version=5 config/name="萌芽农场" config/description="一款支持多人联机的农场游戏" -config/version="1.0.3" +config/version="2.0.0" run/main_scene="uid://bypjb28h4ntdr" config/features=PackedStringArray("4.4", "Mobile") +boot_splash/bg_color=Color(0.901961, 1, 1, 1) boot_splash/fullsize=false +boot_splash/use_filter=false +boot_splash/image="uid://bl1oahn5noklf" config/icon="res://assets/logo2.png" +boot_splash/minimum_display_time=1500 [autoload] @@ -29,6 +33,8 @@ GlobalVariables="*res://GlobalScript/GlobalVariables.gd" window/size/viewport_width=1400 window/size/viewport_height=720 window/size/resizable=false +window/frame_pacing/android/enable_frame_pacing=false +window/frame_pacing/android/swappy_mode=0 window/stretch/mode="viewport" window/per_pixel_transparency/allowed=true window/vsync/vsync_mode=0 @@ -72,10 +78,16 @@ ui_load={ ] } +[physics] + +2d/run_on_separate_thread=true +common/physics_interpolation=true + [rendering] textures/canvas_textures/default_texture_filter=0 -renderer/rendering_method="mobile" +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" textures/vram_compression/import_etc2_astc=true textures/webp_compression/compression_method=3 viewport/hdr_2d=true diff --git a/server/game_saves/3205788256.json b/server/game_saves/3205788256.json deleted file mode 100644 index 499f4a8..0000000 --- a/server/game_saves/3205788256.json +++ /dev/null @@ -1,1349 +0,0 @@ -{ - "农场土地": [ - { - "crop_type": "南瓜", - "grow_time": 3600, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 3600, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "杂交树2", - "grow_time": 25230, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "南瓜", - "grow_time": 3600, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 3600, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "杂交树1", - "grow_time": 21670, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 21600, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "杂交树2", - "grow_time": 25200, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 1 - }, - { - "crop_type": "人参", - "grow_time": 7200, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 7200, - "已浇水": false, - "已施肥": false, - "土地等级": 2 - }, - { - "crop_type": "杂交树2", - "grow_time": 25254, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "树莓", - "grow_time": 2113, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 2700, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "树莓", - "grow_time": 2111, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 2700, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "杂交树2", - "grow_time": 14927, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "甘蔗", - "grow_time": 5400, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 5400, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "香蕉", - "grow_time": 8400, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 8400, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "杂交树1", - "grow_time": 21330, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 21600, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "葫芦", - "grow_time": 1080, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 1080, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "葫芦", - "grow_time": 1080, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 1080, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "葫芦", - "grow_time": 1080, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 1080, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "杂交树2", - "grow_time": 14925, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "杂交树2", - "grow_time": 14925, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "杂交树2", - "grow_time": 14925, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "豌豆", - "grow_time": 720, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 720, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "胡萝卜", - "grow_time": 240, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 240, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "土豆", - "grow_time": 480, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 480, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "椰子", - "grow_time": 1080, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 1080, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "玉米", - "grow_time": 900, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 900, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "番茄", - "grow_time": 720, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 720, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "杂交树2", - "grow_time": 14923, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "土豆", - "grow_time": 480, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 480, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "番茄", - "grow_time": 720, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 720, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "生菜", - "grow_time": 650, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 650, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "杂交树2", - "grow_time": 14922, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "梨子", - "grow_time": 2823, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 2820, - "已浇水": false, - "已施肥": false, - "土地等级": 4 - }, - { - "crop_type": "香蕉", - "grow_time": 8401, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 8400, - "已浇水": false, - "已施肥": false, - "土地等级": 3 - }, - { - "crop_type": "可可豆", - "grow_time": 2102, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 2500, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "黄瓜", - "grow_time": 1200, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 1200, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "葡萄", - "grow_time": 2102, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 2700, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "大豆", - "grow_time": 1080, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 1080, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "野草2", - "grow_time": 5, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "生菜", - "grow_time": 650, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 650, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "野草1", - "grow_time": 5, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "大豆", - "grow_time": 1080, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 1080, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "鱼腥草", - "grow_time": 2101, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 7200, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "稻谷", - "grow_time": 600, - "is_dead": false, - "is_diged": true, - "is_planted": true, - "max_grow_time": 600, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": false, - "is_planted": false, - "max_grow_time": 5, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - } - ], - "种子仓库": [ - { - "name": "小麦", - "quality": "普通", - "count": 27 - }, - { - "name": "胡萝卜", - "quality": "普通", - "count": 6 - }, - { - "name": "土豆", - "quality": "普通", - "count": 8 - }, - { - "name": "鱼腥草", - "quality": "优良", - "count": 4 - }, - { - "name": "蓝莓", - "quality": "稀有", - "count": 1 - }, - { - "name": "大蒜", - "quality": "普通", - "count": 8 - }, - { - "name": "仙人掌", - "quality": "优良", - "count": 1 - }, - { - "name": "稻谷", - "quality": "普通", - "count": 10 - }, - { - "name": "大豆", - "quality": "普通", - "count": 4 - }, - { - "name": "咖啡豆", - "quality": "稀有", - "count": 4 - }, - { - "name": "芒果", - "quality": "稀有", - "count": 2 - }, - { - "name": "荔枝", - "quality": "稀有", - "count": 9 - }, - { - "name": "椰子", - "quality": "优良", - "count": 7 - }, - { - "name": "菠萝", - "quality": "稀有", - "count": 1 - }, - { - "name": "苹果", - "quality": "优良", - "count": 4 - }, - { - "name": "藏羚羊草", - "quality": "史诗", - "count": 1 - }, - { - "name": "向日葵", - "quality": "稀有", - "count": 2 - }, - { - "name": "石榴", - "quality": "优良", - "count": 13 - }, - { - "name": "幸运草", - "quality": "传奇", - "count": 1 - }, - { - "name": "枇杷", - "quality": "优良", - "count": 1 - }, - { - "name": "藏红花", - "quality": "稀有", - "count": 1 - }, - { - "name": "橘子", - "quality": "优良", - "count": 3 - }, - { - "name": "百合花", - "quality": "史诗", - "count": 1 - }, - { - "name": "牵牛花", - "quality": "稀有", - "count": 3 - }, - { - "name": "茄子", - "quality": "普通", - "count": 3 - }, - { - "name": "草莓", - "quality": "稀有", - "count": 1 - }, - { - "name": "番茄", - "quality": "普通", - "count": 2 - }, - { - "name": "大白菜", - "quality": "普通", - "count": 3 - }, - { - "name": "杂草2", - "quality": "普通", - "count": 4 - }, - { - "name": "梨子", - "quality": "优良", - "count": 1 - }, - { - "name": "生菜", - "quality": "普通", - "count": 6 - }, - { - "name": "黄瓜", - "quality": "普通", - "count": 5 - }, - { - "name": "面包树", - "quality": "传奇", - "count": 1 - }, - { - "name": "哈密瓜", - "quality": "稀有", - "count": 3 - }, - { - "name": "野草1", - "quality": "普通", - "count": 5 - }, - { - "name": "豌豆", - "quality": "普通", - "count": 3 - }, - { - "name": "柿子", - "quality": "稀有", - "count": 5 - }, - { - "name": "葡萄", - "quality": "普通", - "count": 2 - }, - { - "name": "树莓", - "quality": "优良", - "count": 1 - }, - { - "name": "玫瑰花", - "quality": "稀有", - "count": 1 - }, - { - "name": "甜菜", - "quality": "稀有", - "count": 5 - }, - { - "name": "辣椒", - "quality": "普通", - "count": 7 - }, - { - "name": "山葵", - "quality": "稀有", - "count": 2 - }, - { - "name": "桃子", - "quality": "稀有", - "count": 3 - }, - { - "name": "甘蔗", - "quality": "稀有", - "count": 2 - }, - { - "name": "杂草1", - "quality": "普通", - "count": 3 - }, - { - "name": "香蕉", - "quality": "优良", - "count": 1 - } - ], - "经验值": 3611, - "农场名称": "树萌芽の狗窝", - "玩家昵称": "树萌芽", - "等级": 66, - "钱币": 615196880382, - "最后登录时间": "2025年07月22日12时49分34秒", - "总游玩时间": "163时34分9秒", - "玩家账号": "3205788256", - "玩家密码": "123456", - "个人简介": "人生啊,就这样吧", - "注册时间": "2025年05月21日15时00分00秒", - "作物仓库": [ - { - "name": "草莓", - "quality": "稀有", - "count": 1 - }, - { - "name": "番茄", - "quality": "普通", - "count": 11 - }, - { - "name": "生菜", - "quality": "普通", - "count": 21 - }, - { - "name": "山楂", - "quality": "优良", - "count": 57 - }, - { - "name": "柠檬", - "quality": "优良", - "count": 2 - }, - { - "name": "芒果", - "quality": "稀有", - "count": 5 - }, - { - "name": "龙果", - "quality": "稀有", - "count": 7 - }, - { - "name": "蓝莓", - "quality": "稀有", - "count": 3 - }, - { - "name": "咖啡豆", - "quality": "稀有", - "count": 1 - }, - { - "name": "小麦", - "quality": "普通", - "count": 77 - }, - { - "name": "山葵", - "quality": "稀有", - "count": 5 - }, - { - "name": "鱼腥草", - "quality": "优良", - "count": 11 - }, - { - "name": "桃子", - "quality": "优良", - "count": 3 - }, - { - "name": "富贵竹", - "quality": "史诗", - "count": 5 - }, - { - "name": "西瓜", - "quality": "优良", - "count": 1 - }, - { - "name": "蕨菜", - "quality": "史诗", - "count": 4 - }, - { - "name": "牵牛花", - "quality": "稀有", - "count": 5 - }, - { - "name": "橘子", - "quality": "优良", - "count": 9 - }, - { - "name": "仙人掌", - "quality": "优良", - "count": 2 - }, - { - "name": "胡萝卜", - "quality": "普通", - "count": 25 - }, - { - "name": "土豆", - "quality": "普通", - "count": 2 - }, - { - "name": "马铃薯", - "quality": "普通", - "count": 29 - }, - { - "name": "火龙果", - "quality": "稀有", - "count": 5 - } - ], - "道具背包": [ - { - "name": "水桶", - "count": 1 - }, - { - "name": "生长素", - "count": 1000 - }, - { - "name": "时运-镰刀", - "count": 1000 - }, - { - "name": "荆棘护甲", - "count": 1 - }, - { - "name": "狂暴药水", - "count": 1 - }, - { - "name": "援军令牌", - "count": 1 - }, - { - "name": "灵木图腾", - "count": 1 - }, - { - "name": "敦岩图腾", - "count": 1 - } - ], - "宠物背包": [ - { - "场景路径": "res://Scene/Pet/SmallGreen.tscn", - "基本信息": { - "宠物主人": "3205788256", - "宠物名称": "萌芽小绿", - "队伍标识": "team1", - "宠物ID": "1751682842035", - "宠物类型": "小绿", - "生日": "2025年7月5日10时34分2秒", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - "等级经验": { - "宠物等级": 4, - "当前经验": 26.0, - "最大经验": 172.8, - "亲密度": 204.0, - "最大亲密度": 1000.0 - }, - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - "生命与防御": { - "最大生命值": 331.0, - "当前生命值": 331.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 133.10000000000005, - "当前护甲值": 133.10000000000005 - }, - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 399.30000000000007, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - "穿透属性": { - "穿透数量": 3 - }, - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - "元素属性": { - "元素类型": "METAL", - "元素克制额外伤害": 100.0 - }, - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": true, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": true, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - "品质系统": { - "宠物品质": "COMMON" - } - }, - { - "场景路径": "res://Scene/Pet/BigBeetle.tscn", - "基本信息": { - "宠物主人": "3205788256", - "宠物名称": "树萌芽の大甲虫", - "队伍标识": "team1", - "宠物ID": "1751695044465", - "宠物类型": "大甲虫", - "生日": "2025年7月5日13时57分24秒", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - "等级经验": { - "宠物等级": 4, - "当前经验": 156.0, - "最大经验": 172.8, - "亲密度": 272.0, - "最大亲密度": 1000.0 - }, - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - "生命与防御": { - "最大生命值": 266.2000000000001, - "当前生命值": 266.2000000000001, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 133.10000000000005, - "当前护甲值": 133.10000000000005 - }, - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 33.27500000000001, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - "穿透属性": { - "穿透数量": 3 - }, - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - "元素属性": { - "元素类型": "WATER", - "元素克制额外伤害": 100.0 - }, - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - "品质系统": { - "宠物品质": "COMMON" - } - }, - { - "场景路径": "res://Scene/Pet/SmallBeetle.tscn", - "基本信息": { - "宠物主人": "3205788256", - "宠物名称": "超级可爱的小甲虫", - "队伍标识": "team1", - "宠物ID": "1751765851860", - "宠物类型": "小甲虫", - "生日": "2025年7月6日9时37分31秒", - "年龄": 0, - "性格": "活泼", - "简介": "", - "爱好": "" - }, - "等级经验": { - "宠物等级": 1, - "当前经验": 0.0, - "最大经验": 100.0, - "亲密度": 0.0, - "最大亲密度": 1000.0 - }, - "购买信息": { - "能否购买": true, - "购买价格": 1000, - "出售价格": 500 - }, - "生命与防御": { - "最大生命值": 200.0, - "当前生命值": 200.0, - "生命恢复速度": 1.0, - "最大护盾值": 0.0, - "当前护盾值": 0.0, - "护盾恢复速度": 0.0, - "最大护甲值": 100.0, - "当前护甲值": 100.0 - }, - "基础攻击属性": { - "攻击类型": "MELEE", - "基础攻击伤害": 25.0, - "攻击距离": 100.0, - "暴击率": 0.1, - "暴击伤害倍数": 1.5, - "生命汲取": 0.1, - "护甲穿透": 0.0 - }, - "近战攻击": { - "近战额外伤害": 0.0, - "近战攻击速度": 1.0 - }, - "远程攻击": { - "远程额外伤害": 0.0, - "远程攻击速度": 1.0, - "远程攻击模式": "SINGLE", - "子弹速度": 300.0 - }, - "散弹攻击": { - "散弹数量": 5, - "散弹扩散角度": 45.0 - }, - "多发射击": { - "多发射击行数": 2, - "多发射击列数": 3, - "多发射击间距": 30.0 - }, - "加特林属性": { - "加特林子弹数量": 8, - "加特林射击间隔": 0.1, - "加特林冷却时间": 2.0 - }, - "穿透属性": { - "穿透数量": 3 - }, - "移动与闪避": { - "移动速度": 100.0, - "闪避率": 0.05, - "击退力度": 300.0, - "击退抗性": 0.0 - }, - "元素属性": { - "元素类型": "NONE", - "元素克制额外伤害": 50.0 - }, - "特殊属性": { - "控制抗性": 0.0, - "伤害反弹": 0.0, - "死亡免疫": false, - "狂暴阈值": 0.3, - "狂暴状态伤害倍数": 1.5 - }, - "特殊机制开关": { - "启用伤害反弹机制": false, - "启用狂暴模式机制": false, - "启用死亡免疫机制": false, - "启用援助召唤机制": false, - "启用死亡重生机制": false - }, - "援助系统": { - "援助触发阈值": 0.2, - "援助召唤数量": 2, - "援助召唤间隔": 5.0 - }, - "品质系统": { - "宠物品质": "COMMON" - } - } - ], - "巡逻宠物": [ - "1751765851860" - ], - "出战宠物": [ - "1751695044465" - ], - "稻草人配置": { - "已拥有稻草人类型": [ - "稻草人1", - "稻草人2", - "稻草人3" - ], - "稻草人展示类型": "稻草人1", - "稻草人昵称": "树萌芽の稻草人", - "稻草人昵称颜色": "1dbc39ff", - "稻草人说的话": { - "第一句话": { - "内容": "欢迎来到萌芽农场!", - "颜色": "1dbc39ff" - }, - "第三句话": { - "内容": "我爱柚小青", - "颜色": "2625c8ff" - }, - "第二句话": { - "内容": "不许偷我的菜!", - "颜色": "1ffffaff" - }, - "第四句话": { - "内容": "你好,世界!", - "颜色": "2145b2ff" - } - } - }, - "智慧树配置": { - "距离上一次杀虫时间": 1752051316, - "距离上一次除草时间": 1752051316, - "智慧树显示的话": "你好", - "等级": 6, - "当前经验值": 204, - "最大经验值": 881, - "最大生命值": 110, - "当前生命值": 110, - "高度": 53, - "上次护理时间": 1752051799 - }, - "体力系统": { - "当前体力值": 25, - "最大体力值": 25, - "上次刷新时间": "2025-07-22", - "上次恢复时间": 1753145617.2709374 - }, - "点赞系统": { - "今日剩余点赞次数": 10, - "点赞上次刷新时间": "2025-07-22", - "总点赞数": 0 - }, - "在线礼包": { - "当前日期": "2025-07-22", - "今日在线时长": 45.17346143722534, - "已领取礼包": [], - "登录时间": 1753145617.273117 - }, - "小卖部配置": { - "商品列表": [], - "格子数": 11 - }, - "游戏设置": { - "背景音乐音量": 0.0, - "天气显示": true - }, - "新手礼包": { - "已领取": true, - "领取时间": "2025-07-21 09:30:48" - }, - "签到历史": { - "2025年07月21日10时47分55秒": "金币439 经验57 小麦x5 土豆x3", - "2025年07月22日10时34分15秒": "金币327 经验58 稻谷x4 小麦x4" - } -} \ No newline at end of file diff --git a/test_bullet_system.gd b/test_bullet_system.gd new file mode 100644 index 0000000..471496b --- /dev/null +++ b/test_bullet_system.gd @@ -0,0 +1,76 @@ +extends Node + +# 子弹系统测试脚本 +# 用于验证每种子弹的独立创建函数功能 + +func _ready(): + test_bullet_system() + +func test_bullet_system(): + print("=== 子弹系统测试开始 ===") + + # 创建子弹实例 + var bullet_scene = preload("res://Scene/NewPet/BulletBase.tscn") + var bullet = bullet_scene.instantiate() + add_child(bullet) + + # 测试获取所有子弹名称 + print("\n可用子弹类型:") + var bullet_names = bullet.get_all_bullet_names() + for name in bullet_names: + print("- %s" % name) + + # 测试每种子弹的创建 + print("\n=== 测试各种子弹创建 ===") + + # 测试小蓝弹 + print("\n测试小蓝弹:") + bullet.create_blue_bullet() + print("速度: %.1f, 伤害: %.1f, 生存时间: %.1f" % [bullet.speed, bullet.damage, bullet.lifetime]) + + # 测试小红弹 + print("\n测试小红弹:") + bullet.create_red_bullet() + print("速度: %.1f, 伤害: %.1f, 生存时间: %.1f" % [bullet.speed, bullet.damage, bullet.lifetime]) + + # 测试长橙弹 + print("\n测试长橙弹:") + bullet.create_long_orange_bullet() + print("速度: %.1f, 伤害: %.1f, 生存时间: %.1f, 最大距离: %.1f" % [bullet.speed, bullet.damage, bullet.lifetime, bullet.max_distance]) + + # 测试黄色闪电 + print("\n测试黄色闪电:") + bullet.create_yellow_lightning_bullet() + print("速度: %.1f, 伤害: %.1f, 生存时间: %.1f, 最大距离: %.1f" % [bullet.speed, bullet.damage, bullet.lifetime, bullet.max_distance]) + + # 测试紫色闪电 + print("\n测试紫色闪电:") + bullet.create_purple_lightning_bullet() + print("速度: %.1f, 伤害: %.1f, 生存时间: %.1f, 最大距离: %.1f" % [bullet.speed, bullet.damage, bullet.lifetime, bullet.max_distance]) + + # 测试通过名称创建子弹 + print("\n=== 测试通过名称创建子弹 ===") + bullet.create_bullet_by_name("小粉弹") + print("小粉弹 - 速度: %.1f, 伤害: %.1f" % [bullet.speed, bullet.damage]) + + bullet.create_bullet_by_name("长绿弹") + print("长绿弹 - 速度: %.1f, 伤害: %.1f" % [bullet.speed, bullet.damage]) + + # 测试获取子弹图标 + print("\n=== 测试获取子弹图标 ===") + for name in ["小蓝弹", "小红弹", "黄色闪电"]: + var icon_path = bullet.get_bullet_icon(name) + print("%s 图标路径: %s" % [name, icon_path]) + + # 测试setup函数的新功能 + print("\n=== 测试setup函数 ===") + bullet.setup(Vector2.RIGHT, 100.0, 10.0, null, "绿色闪电") + print("使用setup创建绿色闪电 - 速度: %.1f, 伤害: %.1f" % [bullet.speed, bullet.damage]) + + bullet.setup(Vector2.LEFT, 200.0, 15.0, null) # 不指定类型 + print("使用setup创建默认子弹 - 速度: %.1f, 伤害: %.1f" % [bullet.speed, bullet.damage]) + + print("\n=== 子弹系统测试完成 ===") + + # 清理测试对象 + bullet.queue_free() \ No newline at end of file diff --git a/test_bullet_system.gd.uid b/test_bullet_system.gd.uid new file mode 100644 index 0000000..bb4962c --- /dev/null +++ b/test_bullet_system.gd.uid @@ -0,0 +1 @@ +uid://bkxv5024t46ya diff --git a/test_weapon_system.gd b/test_weapon_system.gd new file mode 100644 index 0000000..ee6af2c --- /dev/null +++ b/test_weapon_system.gd @@ -0,0 +1,49 @@ +extends Node + +# 测试武器系统的简单脚本 +func _ready(): + print("开始测试武器系统...") + + # 创建武器系统实例 + var weapon_system = WeaponBase.new() + + # 测试武器数据 + print("武器列表:") + for weapon_name in weapon_system.get_all_weapon_names(): + print(" - %s: %s" % [weapon_name, weapon_system.get_weapon_icon(weapon_name)]) + + # 创建一个模拟宠物对象来测试武器效果 + var mock_pet = Node.new() + mock_pet.set_script(preload("res://Scene/NewPet/NewPetBase.gd")) + mock_pet.pet_name = "测试宠物" + mock_pet.base_attack_damage = 25.0 + mock_pet.crit_rate = 0.1 + mock_pet.attack_speed = 1.0 + mock_pet.armor_penetration = 0.0 + mock_pet.attack_range = 100.0 + mock_pet.knockback_force = 300.0 + + print("\n测试武器效果:") + print("装备前属性:") + print(" 攻击力: %.1f" % mock_pet.base_attack_damage) + print(" 暴击率: %.2f" % mock_pet.crit_rate) + print(" 攻击速度: %.1f" % mock_pet.attack_speed) + + # 测试装备钻石剑 + weapon_system.apply_weapon_effect(mock_pet, "钻石剑") + print("\n装备钻石剑后:") + print(" 攻击力: %.1f" % mock_pet.base_attack_damage) + print(" 暴击率: %.2f" % mock_pet.crit_rate) + print(" 攻击速度: %.1f" % mock_pet.attack_speed) + + # 测试卸下武器 + weapon_system.remove_weapon_effect(mock_pet, "钻石剑") + print("\n卸下钻石剑后:") + print(" 攻击力: %.1f" % mock_pet.base_attack_damage) + print(" 暴击率: %.2f" % mock_pet.crit_rate) + print(" 攻击速度: %.1f" % mock_pet.attack_speed) + + print("\n武器系统测试完成!") + + # 清理 + mock_pet.queue_free() \ No newline at end of file diff --git a/test_weapon_system.gd.uid b/test_weapon_system.gd.uid new file mode 100644 index 0000000..2841863 --- /dev/null +++ b/test_weapon_system.gd.uid @@ -0,0 +1 @@ +uid://cc5eho78s6v6k