From c77620127d8facaee6bac7fa63d5c819d431fe37 Mon Sep 17 00:00:00 2001 From: hantao Date: Fri, 5 Dec 2025 17:38:10 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=80=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + save.json | 556 +------------------------ src/Core/ItemDisplay.php | 86 ++-- src/Core/SpellCalculator.php | 181 ++++++++ src/Core/SpellDisplay.php | 35 +- src/Data/maps.php | 84 ++-- src/Entities/Monster.php | 25 +- src/Modules/Battle.php | 738 ++++++++++++++++----------------- src/Modules/InventoryPanel.php | 13 +- test/Test.php | 8 +- 10 files changed, 644 insertions(+), 1083 deletions(-) create mode 100644 src/Core/SpellCalculator.php diff --git a/.gitignore b/.gitignore index 96b82be..ac3bc73 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /data/ /build/ /.claude/ +/.idea diff --git a/save.json b/save.json index f423a06..be1c9d1 100644 --- a/save.json +++ b/save.json @@ -1,555 +1 @@ -{ - "player": { - "hp": 100, - "maxHp": 100, - "patk": 10, - "matk": 10, - "pdef": 10, - "mdef": 10, - "crit": 0, - "critdmg": 110, - "level": 5, - "exp": 378, - "maxExp": 505, - "inventory": [ - { - "id": "6931aad284144", - "type": "spell", - "name": "柔拳", - "quality": "common", - "level": 1, - "spellId": 10, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 20, - "spellType": "damage_single", - "desc": "发出一团火球,对单个敌人造成伤害", - "heal_ratio": 0, - "damage_ratio": 1.2, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "dropRate": 25, - "quantity": 1 - }, - { - "id": "6931aad28414a", - "type": "spell", - "name": "柔拳", - "quality": "common", - "level": 1, - "spellId": 10, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 20, - "spellType": "damage_single", - "desc": "发出一团火球,对单个敌人造成伤害", - "heal_ratio": 0, - "damage_ratio": 1.2, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "dropRate": 25, - "quantity": 1 - }, - { - "name": "金疮药", - "type": "consume", - "quality": "common", - "level": 1, - "patk": 0, - "matk": 0, - "pdef": 0, - "mdef": 0, - "hp": 0, - "crit": 0, - "critdmg": 0, - "heal": 45, - "affixes": [], - "desc": "Lv.1 common品质的药剂", - "id": "6931aad284152", - "quantity": 1 - }, - { - "name": "金疮药", - "type": "consume", - "quality": "common", - "level": 1, - "patk": 0, - "matk": 0, - "pdef": 0, - "mdef": 0, - "hp": 0, - "crit": 0, - "critdmg": 0, - "heal": 42, - "affixes": [], - "desc": "Lv.1 common品质的药剂", - "id": "6931aaebc3dfa", - "quantity": 1 - }, - { - "id": "6931ab173bb23", - "type": "spell", - "name": "刀气切割", - "quality": "common", - "level": 3, - "spellId": 10, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 20, - "spellType": "damage_single", - "desc": "发出一团火球,对单个敌人造成伤害", - "heal_ratio": 0, - "damage_ratio": 1.2, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "dropRate": 30, - "quantity": 1 - }, - { - "id": "6931ab173bb26", - "type": "spell", - "name": "寒冰爆裂", - "quality": "common", - "level": 3, - "spellId": 20, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 35, - "spellType": "damage_aoe", - "desc": "召唤冰雹,攻击所有敌人", - "heal_ratio": 0, - "damage_ratio": 0.7, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "dropRate": 25, - "quantity": 1 - }, - { - "name": "金疮药", - "type": "consume", - "quality": "common", - "level": 3, - "patk": 0, - "matk": 0, - "pdef": 0, - "mdef": 0, - "hp": 0, - "crit": 0, - "critdmg": 0, - "heal": 46, - "affixes": [], - "desc": "Lv.3 common品质的药剂", - "id": "6931ab173bb2a", - "quantity": 1 - }, - { - "name": "雷霆锤", - "type": "weapon", - "quality": "rare", - "level": 1, - "patk": 17, - "matk": 5, - "pdef": 0, - "mdef": 0, - "hp": 0, - "crit": 0, - "critdmg": 0, - "heal": 0, - "affixes": [ - "物攻 +5" - ], - "desc": "Lv.1 rare品质的武器", - "id": "6931ab54ee18e", - "quantity": 1 - }, - { - "id": "6931ab54ee193", - "type": "spell", - "name": "寒冰爆裂", - "quality": "common", - "level": 3, - "spellId": 20, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 35, - "spellType": "damage_aoe", - "desc": "召唤冰雹,攻击所有敌人", - "heal_ratio": 0, - "damage_ratio": 0.7, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "dropRate": 25, - "quantity": 1 - }, - { - "name": "金疮药", - "type": "consume", - "quality": "rare", - "level": 1, - "patk": 0, - "matk": 0, - "pdef": 0, - "mdef": 0, - "hp": 0, - "crit": 0, - "critdmg": 0, - "heal": 58, - "affixes": [], - "desc": "Lv.1 rare品质的药剂", - "id": "6931ab54ee199", - "quantity": 1 - }, - { - "id": "6931ab5b497ab", - "type": "spell", - "name": "柔拳", - "quality": "epic", - "level": 1, - "spellId": 10, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 20, - "spellType": "damage_single", - "desc": "发出一团火球,对单个敌人造成伤害", - "heal_ratio": 0, - "damage_ratio": 2, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "dropRate": 25, - "quantity": 1 - }, - { - "name": "金疮药", - "type": "consume", - "quality": "rare", - "level": 1, - "patk": 0, - "matk": 0, - "pdef": 0, - "mdef": 0, - "hp": 0, - "crit": 0, - "critdmg": 0, - "heal": 60, - "affixes": [], - "desc": "Lv.1 rare品质的药剂", - "id": "6931ab6f31d10", - "quantity": 1 - }, - { - "id": "6931ab91181f3", - "type": "spell", - "name": "柔拳", - "quality": "rare", - "level": 1, - "spellId": 10, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 20, - "spellType": "damage_single", - "desc": "发出一团火球,对单个敌人造成伤害", - "heal_ratio": 0, - "damage_ratio": 1.6, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "dropRate": 25, - "quantity": 1 - }, - { - "name": "金疮药", - "type": "consume", - "quality": "rare", - "level": 1, - "patk": 0, - "matk": 0, - "pdef": 0, - "mdef": 0, - "hp": 0, - "crit": 0, - "critdmg": 0, - "heal": 59, - "affixes": [], - "desc": "Lv.1 rare品质的药剂", - "id": "6931ab91181f6", - "quantity": 1 - }, - { - "id": "6931abc63bfdd", - "type": "spell", - "name": "刀气切割", - "quality": "common", - "level": 3, - "spellId": 10, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 20, - "spellType": "damage_single", - "desc": "发出一团火球,对单个敌人造成伤害", - "heal_ratio": 0, - "damage_ratio": 1.2, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "dropRate": 30, - "quantity": 1 - }, - { - "id": "6931abc63bfe2", - "type": "spell", - "name": "柔拳", - "quality": "rare", - "level": 1, - "spellId": 10, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 20, - "spellType": "damage_single", - "desc": "发出一团火球,对单个敌人造成伤害", - "heal_ratio": 0, - "damage_ratio": 1.6, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "dropRate": 25, - "quantity": 1 - }, - { - "name": "金疮药", - "type": "consume", - "quality": "epic", - "level": 1, - "patk": 0, - "matk": 0, - "pdef": 0, - "mdef": 0, - "hp": 0, - "crit": 0, - "critdmg": 0, - "heal": 88, - "affixes": [], - "desc": "Lv.1 epic品质的药剂", - "id": "6931abc63bfe5", - "quantity": 1 - } - ], - "equip": { - "weapon": { - "name": "寒冰剑", - "type": "weapon", - "quality": "common", - "level": 1, - "patk": 2, - "matk": 8, - "pdef": 0, - "mdef": 0, - "hp": 0, - "crit": 0, - "critdmg": 0, - "heal": 0, - "affixes": [], - "desc": "Lv.1 common品质的武器", - "id": "69319d7e966eb", - "quantity": 1 - } - }, - "spiritStones": 399, - "npcFlags": { - "gift_li_feiyu": true - }, - "talentPoints": 3, - "talents": { - "hp": 0, - "patk": 0, - "matk": 0, - "pdef": 0, - "mdef": 0, - "crit": 0, - "critdmg": 0 - }, - "mana": 39, - "maxMana": 100, - "skillSlots": { - "skill1": { - "id": "69319e185013f", - "type": "spell", - "name": "刀气切割", - "quality": "rare", - "level": 3, - "spellId": 10, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 20, - "spellType": "damage_single", - "desc": "发出一团火球,对单个敌人造成伤害", - "heal_ratio": 0, - "damage_ratio": 1.6, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "quantity": 1 - }, - "skill2": { - "id": "69319e1850143", - "type": "spell", - "name": "妙手回春", - "quality": "common", - "level": 5, - "spellId": 1, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 15, - "spellType": "heal_single", - "desc": "恢复自己或队友的生命值,效果与魔攻相关", - "heal_ratio": 0.5, - "damage_ratio": 1, - "heal_base": 20, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "quantity": 1 - }, - "skill3": { - "id": "69319e6144cf6", - "type": "spell", - "name": "柔拳", - "quality": "legendary", - "level": 1, - "spellId": 10, - "enhanceLevel": 1, - "calc_type": "matk", - "cost": 20, - "spellType": "damage_single", - "desc": "发出一团火球,对单个敌人造成伤害", - "heal_ratio": 0, - "damage_ratio": 2.6, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "quantity": 1 - }, - "skill4": null - }, - "partners": [ - { - "id": "li_feiyu", - "name": "厉飞雨", - "level": 2, - "exp": 148, - "maxExp": 150, - "equip": [], - "talents": { - "hp": 2, - "patk": 1, - "matk": 0, - "pdef": 0, - "mdef": 0, - "crit": 0, - "critdmg": 0 - }, - "talentWeights": { - "hp": 1, - "patk": 1, - "matk": 1, - "pdef": 1, - "mdef": 1, - "crit": 1, - "critdmg": 1 - }, - "mana": 100, - "maxMana": 100, - "skillSlots": { - "skill1": { - "id": "69319cc96e00b", - "type": "spell", - "name": "寒冰爆裂", - "quality": "common", - "level": 3, - "spellId": 20, - "enhanceLevel": 0, - "calc_type": "matk", - "cost": 35, - "spellType": "damage_aoe", - "desc": "召唤冰雹,攻击所有敌人", - "heal_ratio": 0, - "damage_ratio": 0.7, - "heal_base": 0, - "crit_bonus": 0, - "crit_dmg_bonus": 0, - "enemy_count_bonus": 0, - "dispersion": 1, - "team_bonus": 0, - "priority_bonus": 0, - "quantity": 1 - }, - "skill2": null, - "skill3": null, - "skill4": null - }, - "hp": 120, - "maxHp": 100, - "patk": 15, - "matk": 5, - "pdef": 5, - "mdef": 3, - "crit": 10, - "critdmg": 130 - } - ] - }, - "dungeonId": 1, - "state": 4 -} \ No newline at end of file +{"player":{"hp":2,"maxHp":100,"patk":10,"matk":10,"pdef":10,"mdef":10,"crit":0,"critdmg":110,"level":7,"exp":466,"maxExp":1135,"inventory":[{"name":"雷霆锤","type":"weapon","quality":"rare","level":1,"patk":17,"matk":5,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":0,"affixes":["物攻 +5"],"desc":"Lv.1 rare品质的武器","id":"6931ab54ee18e","quantity":1},{"id":"6931ab5b497ab","type":"spell","name":"柔拳","quality":"epic","level":1,"spellId":10,"enhanceLevel":0,"calc_type":"matk","cost":20,"spellType":"damage_single","desc":"发出一团火球,对单个敌人造成伤害","heal_ratio":0,"damage_ratio":2,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"dropRate":25,"quantity":1},{"id":"6931ab91181f3","type":"spell","name":"柔拳","quality":"rare","level":1,"spellId":10,"enhanceLevel":0,"calc_type":"matk","cost":20,"spellType":"damage_single","desc":"发出一团火球,对单个敌人造成伤害","heal_ratio":0,"damage_ratio":1.6,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"dropRate":25,"quantity":1},{"id":"6931abc63bfe2","type":"spell","name":"柔拳","quality":"rare","level":1,"spellId":10,"enhanceLevel":0,"calc_type":"matk","cost":20,"spellType":"damage_single","desc":"发出一团火球,对单个敌人造成伤害","heal_ratio":0,"damage_ratio":1.6,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"dropRate":25,"quantity":1},{"name":"金疮药","type":"consume","quality":"epic","level":1,"patk":0,"matk":0,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":88,"affixes":[],"desc":"Lv.1 epic品质的药剂","id":"6931abc63bfe5","quantity":1},{"name":"雷霆锤","type":"weapon","quality":"rare","level":1,"patk":18,"matk":5,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":0,"affixes":["暴击伤害 +4%"],"desc":"Lv.1 rare品质的武器","id":"69327b840cf71","quantity":1},{"id":"6932835613509","type":"spell","name":"寒冰爆裂","quality":"rare","level":3,"spellId":20,"enhanceLevel":0,"calc_type":"matk","cost":35,"spellType":"damage_aoe","desc":"召唤冰雹,攻击所有敌人","heal_ratio":0,"damage_ratio":1,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"dropRate":25,"quantity":1},{"id":"69329a311fbfd","type":"spell","name":"柔拳","quality":"rare","level":1,"spellId":10,"enhanceLevel":0,"calc_type":"matk","cost":20,"spellType":"damage_single","desc":"发出一团火球,对单个敌人造成伤害","heal_ratio":0,"damage_ratio":1.6,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"dropRate":25,"quantity":1},{"name":"青钢剑","type":"weapon","quality":"rare","level":1,"patk":14,"matk":4,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":0,"affixes":["物攻 +4"],"desc":"Lv.1 rare品质的武器","id":"69329a4a985f8","quantity":1},{"name":"金疮药","type":"consume","quality":"epic","level":1,"patk":0,"matk":0,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":91,"affixes":[],"desc":"Lv.1 epic品质的药剂","id":"69329a5c694b8","quantity":1},{"name":"回灵丹","type":"consume","quality":"epic","level":3,"patk":0,"matk":0,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":97,"affixes":[],"desc":"Lv.3 epic品质的药剂","id":"69329ae8762cb","quantity":1},{"id":"69329b2cab706","type":"spell","name":"刀气切割","quality":"rare","level":3,"spellId":10,"enhanceLevel":0,"calc_type":"matk","cost":20,"spellType":"damage_single","desc":"发出一团火球,对单个敌人造成伤害","heal_ratio":0,"damage_ratio":1.6,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"dropRate":20,"quantity":1},{"name":"烈焰刀","type":"weapon","quality":"rare","level":1,"patk":12,"matk":11,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":0,"affixes":["暴击率 +10"],"desc":"Lv.1 rare品质的武器","id":"69329b2cab71c","quantity":1}],"equip":{"weapon":{"name":"铁剑","type":"weapon","quality":"epic","level":1,"patk":25,"matk":0,"pdef":0,"mdef":0,"hp":0,"crit":6,"critdmg":0,"heal":0,"affixes":["暴击率 +8","物攻 +2%"],"desc":"Lv.1 epic品质的武器","id":"69327b6a60eb3","quantity":1,"enhanceLevel":0}},"spiritStones":867,"npcFlags":{"gift_li_feiyu":true},"talentPoints":3,"talents":{"hp":6,"patk":0,"matk":0,"pdef":0,"mdef":0,"crit":0,"critdmg":0},"mana":7,"maxMana":100,"skillSlots":{"skill1":{"id":"69319e185013f","type":"spell","name":"刀气切割","quality":"rare","level":3,"spellId":10,"enhanceLevel":0,"calc_type":"matk","cost":20,"spellType":"damage_single","desc":"发出一团火球,对单个敌人造成伤害","heal_ratio":0,"damage_ratio":1.6,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"quantity":1},"skill2":{"id":"69319e1850143","type":"spell","name":"妙手回春","quality":"common","level":5,"spellId":1,"enhanceLevel":0,"calc_type":"matk","cost":15,"spellType":"heal_single","desc":"恢复自己或队友的生命值,效果与魔攻相关","heal_ratio":0.5,"damage_ratio":1,"heal_base":20,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"quantity":1},"skill3":{"id":"69319e6144cf6","type":"spell","name":"柔拳","quality":"legendary","level":1,"spellId":10,"enhanceLevel":1,"calc_type":"matk","cost":20,"spellType":"damage_single","desc":"发出一团火球,对单个敌人造成伤害","heal_ratio":0,"damage_ratio":2.6,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"quantity":1},"skill4":{"id":"69329b2cab723","type":"spell","name":"寒冰爆裂","quality":"rare","level":3,"spellId":20,"enhanceLevel":0,"calc_type":"matk","cost":35,"spellType":"damage_aoe","desc":"召唤冰雹,攻击所有敌人","heal_ratio":0,"damage_ratio":1,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"dropRate":25,"quantity":1}},"partners":[{"id":"li_feiyu","name":"厉飞雨","level":6,"exp":11,"maxExp":757,"equip":{"weapon":{"name":"玄铁剑","type":"weapon","quality":"rare","level":1,"patk":22,"matk":1,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":0,"affixes":["物攻 +7"],"desc":"Lv.1 rare品质的武器","id":"69329a5c694e3","quantity":1}},"talents":{"hp":10,"patk":5,"matk":0,"pdef":0,"mdef":0,"crit":0,"critdmg":0},"talentWeights":{"hp":1,"patk":1,"matk":1,"pdef":1,"mdef":1,"crit":1,"critdmg":1},"mana":10,"maxMana":100,"skillSlots":{"skill1":{"id":"69319cc96e00b","type":"spell","name":"寒冰爆裂","quality":"common","level":3,"spellId":20,"enhanceLevel":0,"calc_type":"matk","cost":35,"spellType":"damage_aoe","desc":"召唤冰雹,攻击所有敌人","heal_ratio":0,"damage_ratio":0.7,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"quantity":1},"skill2":{"id":"69327b840cfb4","type":"spell","name":"柔拳","quality":"legendary","level":1,"spellId":10,"enhanceLevel":0,"calc_type":"matk","cost":20,"spellType":"damage_single","desc":"发出一团火球,对单个敌人造成伤害","heal_ratio":0,"damage_ratio":2.6,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"dropRate":25,"quantity":1},"skill3":{"id":"6932955a5626b","type":"spell","name":"舍身救人","quality":"epic","level":5,"spellId":2,"enhanceLevel":0,"calc_type":"hp_percent","cost":20,"spellType":"heal_single","desc":"将自己的部分生命值转移给队友","heal_ratio":0.5,"damage_ratio":1,"heal_base":0,"crit_bonus":0,"crit_dmg_bonus":0,"enemy_count_bonus":0,"dispersion":1,"team_bonus":0,"priority_bonus":0,"dropRate":25,"quantity":1},"skill4":null},"hp":200,"maxHp":100,"patk":15,"matk":5,"pdef":5,"mdef":3,"crit":10,"critdmg":130}]},"dungeonId":1,"state":2} \ No newline at end of file diff --git a/src/Core/ItemDisplay.php b/src/Core/ItemDisplay.php index 93347f2..3f99a44 100644 --- a/src/Core/ItemDisplay.php +++ b/src/Core/ItemDisplay.php @@ -292,71 +292,33 @@ class ItemDisplay // 法术特殊显示 if ($type === 'spell') { $lines[] = $linePrefix . self::$white . "--- 法术信息 ---" . self::$reset; - $spellType = $item['spellType'] ?? $item['type'] ?? 'unknown'; - $calcType = $item['calc_type'] ?? 'unknown'; - - // 计算方式映射 - $calcTypeMap = [ - 'matk' => '基于魔攻', - 'patk' => '基于物攻', - 'hp_percent' => '基于最大生命值百分比', - 'hybrid' => '基于(魔攻+物攻)混合', - 'crit_heal' => '暴击率影响治疗效果', - 'crit_damage' => '暴击伤害系数影响伤害', - 'crit_aoe' => '暴击率影响范围伤害', - 'defense' => '基于防御属性', - 'low_def_bonus' => '对低防御敌人伤害加成', - 'matk_scaled' => '随敌人数量加成', - 'dispersed_damage' => '伤害分散到所有敌人', - 'smart_heal' => '智能治疗(优先低血量)', - 'hp_missing' => '基于缺失生命值', - 'team_sync' => '基于队伍规模', - ]; - - $calcDesc = $calcTypeMap[$calcType] ?? $calcType; - $lines[] = $linePrefix . " 计算方式: " . self::$cyan . $calcDesc . self::$reset; - - // 基础数值 - if ($spellType === 'damage_single' || $spellType === 'damage_aoe') { - $damageRatio = $item['damage_ratio'] ?? [1.2, 1.6, 2.0, 2.6]; - $qualityIndex = self::getQualityIndex($quality); - $ratio = $damageRatio[$qualityIndex] ?? 1.0; - $lines[] = $linePrefix . " 伤害倍数: " . self::$green . $ratio . "x" . self::$reset; - } elseif ($spellType === 'heal_single' || $spellType === 'heal_aoe') { - $healRatio = $item['heal_ratio'] ?? [0.5, 0.8, 1.2, 1.8]; - $healBase = $item['heal_base'] ?? [20, 40, 70, 120]; - $qualityIndex = self::getQualityIndex($quality); - $ratio = $healRatio[$qualityIndex] ?? 0.5; - $base = $healBase[$qualityIndex] ?? 20; - $lines[] = $linePrefix . " 治疗系数: " . self::$green . $ratio . "x + " . $base . self::$reset; + $lines += SpellDisplay::renderDetail($item); + $lines[] = $linePrefix; + }else{ + // 主属性 + $lines[] = $linePrefix . self::$white . "--- 主属性 ---" . self::$reset; + $statLines = self::formatStatsDetailed($item, $linePrefix . " "); + if (!empty($statLines)) { + $lines = array_merge($lines, $statLines); + } else { + $lines[] = $linePrefix . " " . self::$gray . "(无)" . self::$reset; } - $lines[] = $linePrefix; - } + // 词条 + $affixes = $item['affixes'] ?? []; + if (!empty($affixes)) { + $lines[] = $linePrefix; + $lines[] = $linePrefix . self::$white . "--- 词条 ---" . self::$reset; + $affixLines = self::formatAffixes($item, $linePrefix . " "); + $lines = array_merge($lines, $affixLines); + } - // 主属性 - $lines[] = $linePrefix . self::$white . "--- 主属性 ---" . self::$reset; - $statLines = self::formatStatsDetailed($item, $linePrefix . " "); - if (!empty($statLines)) { - $lines = array_merge($lines, $statLines); - } else { - $lines[] = $linePrefix . " " . self::$gray . "(无)" . self::$reset; - } - - // 词条 - $affixes = $item['affixes'] ?? []; - if (!empty($affixes)) { - $lines[] = $linePrefix; - $lines[] = $linePrefix . self::$white . "--- 词条 ---" . self::$reset; - $affixLines = self::formatAffixes($item, $linePrefix . " "); - $lines = array_merge($lines, $affixLines); - } - - // 描述 - $desc = $item['desc'] ?? ''; - if ($desc) { - $lines[] = $linePrefix; - $lines[] = $linePrefix . self::$gray . $desc . self::$reset; + // 描述 + $desc = $item['desc'] ?? ''; + if ($desc) { + $lines[] = $linePrefix; + $lines[] = $linePrefix . self::$gray . $desc . self::$reset; + } } return $lines; diff --git a/src/Core/SpellCalculator.php b/src/Core/SpellCalculator.php new file mode 100644 index 0000000..1285792 --- /dev/null +++ b/src/Core/SpellCalculator.php @@ -0,0 +1,181 @@ + int, 'isCrit' => bool, 'baseDamage' => int, 'multiplier' => float] + */ + public static function calculateDamage(array $spellInfo, array $attackerStats, array $defenderStats, int $bonus = 0, bool $isAoe = false): array + { + + + // 基础倍率 + $baseMultiplier = $spellInfo['damage_ratio'] ?? ($spellInfo['damage'] ?? 1.0); + + // 实际倍率 (含强化加成) + $actualMultiplier = $baseMultiplier * (1 + $bonus / 100); + + // 计算方式 + $calcType = $spellInfo['calc_type'] ?? 'matk'; + + // 基础伤害 (根据 calc_type) + switch ($calcType) { + case 'patk': + $baseDamage = (int)($attackerStats['patk'] * $actualMultiplier); + break; + case 'hybrid': + // 取魔攻与物攻的平均值 + $combined = ($attackerStats['matk'] + $attackerStats['patk']) / 2; + $baseDamage = (int)($combined * $actualMultiplier); + break; + case 'low_def_bonus': + // 对低防御目标额外加成 20% + $baseDamage = (int)($attackerStats['matk'] * $actualMultiplier); + $def = $defenderStats['pdef'] ?? 0; + if ($def < 5) { + $baseDamage = (int)($baseDamage * 1.2); + } + break; + case 'matk_scaled': + // 根据敌人数加成,假设 spellInfo 包含 enemy_count_bonus (每个敌人加成比例) 和 enemy_count + $baseDamage = (int)($attackerStats['matk'] * $actualMultiplier); + if (isset($spellInfo['enemy_count_bonus'])) { + $bonusPct = $spellInfo['enemy_count_bonus']; + $enemyCount = $spellInfo['enemy_count'] ?? 1; + $baseDamage = (int)($baseDamage * (1 + $bonusPct * $enemyCount)); + } + break; + case 'hp_percent': + // 基于目标最大生命值的百分比 + $baseDamage = (int)(($defenderStats['maxHp'] ?? 0) * $actualMultiplier); + break; + case 'defense': + // 基于目标防御属性 (物防+魔防) + $defSum = ($defenderStats['pdef'] ?? 0) + ($defenderStats['mdef'] ?? 0); + $baseDamage = (int)($defSum * $actualMultiplier); + break; + case 'crit_damage': + // 使用物攻作为基础,后续在暴击阶段乘以暴击系数 + $baseDamage = (int)($attackerStats['patk'] * $actualMultiplier); + break; + case 'crit_aoe': + // 同 crit_damage,AOE 暴击率已在上层处理 + $baseDamage = (int)($attackerStats['patk'] * $actualMultiplier); + break; + case 'dispersed_damage': + // 基于魔攻,乘以分散系数 (spellInfo['dispersion']) + $baseDamage = (int)($attackerStats['matk'] * $actualMultiplier); + if (isset($spellInfo['dispersion'])) { + $baseDamage = (int)($baseDamage * $spellInfo['dispersion']); + } + break; + default: + // 默认使用魔攻 + $baseDamage = (int)($attackerStats['matk'] * $actualMultiplier); + } + + // 计算防御减免 + $resistance = $defenderStats['mdef'] ?? 0; + $damage = max(1, $baseDamage - $resistance); + + // 暴击计算 + $critRate = $attackerStats['crit'] ?? 0; + if ($isAoe) { + $critRate /= 2; // AOE 暴击率减半 + } + + $isCrit = rand(1, 100) <= $critRate; + if ($isCrit) { + $critDmg = $attackerStats['critdmg'] ?? 150; + $damage = (int)($damage * ($critDmg / 100)); + } + + return [ + 'damage' => $damage, + 'isCrit' => $isCrit, + 'baseDamage' => $baseDamage, + 'multiplier' => $baseMultiplier // 返回基础倍率用于显示 + ]; + } + + /** + * 计算治疗量 + */ + public static function calculateHeal(array $spellInfo, array $casterStats, int $bonus = 0): int + { + // 计算方式 + $calcType = $spellInfo['calc_type'] ?? 'matk'; + $healRatio = $spellInfo['heal'] ?? 0.5; + $healBase = $spellInfo['heal_base'] ?? 20; + + // 基础治疗量 (根据 calc_type) + switch ($calcType) { + case 'patk': + $healAmount = (int)($casterStats['patk'] * $healRatio + $healBase); + break; + case 'hybrid': + $combined = ($casterStats['matk'] + $casterStats['patk']) / 2; + $healAmount = (int)($combined * $healRatio + $healBase); + break; + case 'defense': + $defSum = ($casterStats['pdef'] ?? 0) + ($casterStats['mdef'] ?? 0); + $healAmount = (int)($defSum * $healRatio + $healBase); + break; + case 'hp_percent': + $healAmount = (int)($casterStats['maxHp'] * $healRatio + $healBase); + break; + case 'team_sync': + $teamSize = $spellInfo['team_size'] ?? 1; + $teamBonus = $spellInfo['team_bonus'] ?? 0; + $healAmount = (int)($casterStats['matk'] * $healRatio + $healBase); + $healAmount = (int)($healAmount * (1 + $teamBonus * $teamSize)); + break; + case 'smart_heal': + // 智能治疗,先按魔攻计算,后续在 Battle 中会选择低血量目标 + $healAmount = (int)($casterStats['matk'] * $healRatio + $healBase); + break; + case 'crit_heal': + $healAmount = (int)($casterStats['matk'] * $healRatio + $healBase); + $critRate = $casterStats['crit'] ?? 0; + $isCrit = rand(1, 100) <= $critRate; + if ($isCrit) { + $critDmg = $casterStats['critdmg'] ?? 150; + $healAmount = (int)($healAmount * ($critDmg / 100)); + } + break; + default: + $healAmount = (int)($casterStats['matk'] * $healRatio + $healBase); + break; + } + + // 应用强化加成 + return (int)($healAmount * (1 + $bonus / 100)); + } + + /** + * 根据品质获取数组索引 + */ + public static function getQualityIndex(string $quality): int + { + return match($quality) { + 'common' => 0, + 'rare' => 1, + 'epic' => 2, + 'legendary' => 3, + default => 0, + }; + } +} diff --git a/src/Core/SpellDisplay.php b/src/Core/SpellDisplay.php index 33a3fe7..8f55cbd 100644 --- a/src/Core/SpellDisplay.php +++ b/src/Core/SpellDisplay.php @@ -236,10 +236,8 @@ class SpellDisplay private static function formatDamageValues(array $spell, string $linePrefix, int $enhanceLevel): string { $calcType = $spell['calc_type'] ?? 'matk'; - $damageRatio = $spell['damage_ratio'] ?? [1.2, 1.6, 2.0, 2.6]; - $qualityIndex = self::getQualityIndex($spell['quality'] ?? 'common'); - $ratio = $damageRatio[$qualityIndex] ?? 1.2; + $ratio = $spell['damage_ratio']; if ($calcType === 'matk') { return $linePrefix . "伤害倍数: " . self::$green . "{$ratio}x魔攻" . self::$reset; @@ -248,25 +246,21 @@ class SpellDisplay } elseif ($calcType === 'hybrid') { return $linePrefix . "伤害倍数: " . self::$green . "{$ratio}x(魔攻+物攻)" . self::$reset; } elseif ($calcType === 'crit_damage') { - $critBonus = $spell['crit_dmg_bonus'] ?? [0.3, 0.5, 0.8, 1.2]; - $bonus = $critBonus[$qualityIndex] ?? 0.3; + $bonus = $spell['crit_dmg_bonus']; return $linePrefix . "伤害倍数: " . self::$green . "{$ratio}x物攻" . self::$reset . "\n" . $linePrefix . "暴击加成: " . self::$yellow . "{$bonus}x暴伤系数" . self::$reset; } elseif ($calcType === 'low_def_bonus') { return $linePrefix . "伤害倍数: " . self::$green . "{$ratio}x物攻(低防御加成)" . self::$reset; } elseif ($calcType === 'matk_scaled') { - $enemyBonus = $spell['enemy_count_bonus'] ?? [0.1, 0.15, 0.2, 0.3]; - $bonus = $enemyBonus[$qualityIndex] ?? 0.1; + $bonus = $spell['enemy_count_bonus']; return $linePrefix . "伤害倍数: " . self::$green . "{$ratio}x魔攻" . self::$reset . "\n" . $linePrefix . "敌人数加成: " . self::$yellow . "+" . ($bonus*100) . "%/敌人" . self::$reset; } elseif ($calcType === 'dispersed_damage') { - $dispersion = $spell['dispersion'] ?? [0.8, 0.75, 0.7, 0.65]; - $disp = $dispersion[$qualityIndex] ?? 0.8; + $disp = $spell['dispersion']; return $linePrefix . "伤害倍数: " . self::$green . "{$ratio}x魔攻" . self::$reset . "\n" . $linePrefix . "分散系数: " . self::$yellow . "{$disp}(敌人越多衰减越严重)" . self::$reset; } elseif ($calcType === 'crit_aoe') { - $critBonus = $spell['crit_bonus'] ?? [0.4, 0.6, 0.9, 1.3]; - $bonus = $critBonus[$qualityIndex] ?? 0.4; + $bonus = $spell['crit_bonus']; return $linePrefix . "伤害倍数: " . self::$green . "{$ratio}x魔攻" . self::$reset . "\n" . $linePrefix . "暴击影响: " . self::$yellow . "{$bonus}x暴击率" . self::$reset; } @@ -410,10 +404,10 @@ class SpellDisplay $valueStr = ''; if ($spellType === 'damage_single' || $spellType === 'damage_aoe') { $damageRatio = $spell['damage_ratio']; - $valueStr = self::$green . "{$damageRatio}x" . self::$reset; + $valueStr = self::$green . "x{$damageRatio}" . self::$reset; } elseif ($spellType === 'heal_single' || $spellType === 'heal_aoe') { $healRatio = $spell['heal_ratio']; - $valueStr = self::$green . "{$healRatio}x" . self::$reset; + $valueStr = self::$green . "x{$healRatio}" . self::$reset; } return self::$cyan . "[{$calcDesc}]" . self::$reset . $valueStr; @@ -457,17 +451,12 @@ class SpellDisplay // 显示基础数值 if ($spellType === 'damage_single' || $spellType === 'damage_aoe') { - $damageRatio = $spell['damage_ratio'] ?? [1.2, 1.6, 2.0, 2.6]; - $qualityIndex = self::getQualityIndex($spell['quality'] ?? 'common'); - $ratio = $damageRatio[$qualityIndex] ?? 1.0; - $lines[] = $linePrefix . " " . self::$white . "倍数: " . self::$yellow . "{$ratio}x" . self::$reset; + $ratio = $spell['damage_ratio']; + $lines[] = $linePrefix . " " . self::$white . "倍数: " . self::$yellow . "x{$ratio}" . self::$reset; } elseif ($spellType === 'heal_single' || $spellType === 'heal_aoe') { - $healRatio = $spell['heal_ratio'] ?? [0.5, 0.8, 1.2, 1.8]; - $healBase = $spell['heal_base'] ?? [20, 40, 70, 120]; - $qualityIndex = self::getQualityIndex($spell['quality'] ?? 'common'); - $ratio = $healRatio[$qualityIndex] ?? 0.5; - $base = $healBase[$qualityIndex] ?? 20; - $lines[] = $linePrefix . " " . self::$white . "治疗: " . self::$yellow . "{$ratio}x + {$base}" . self::$reset; + $ratio = $spell['heal_ratio']; + $base = $spell['heal_base']; + $lines[] = $linePrefix . " " . self::$white . "治疗: " . self::$yellow . "x{$ratio} + {$base}" . self::$reset; } // 显示消耗 diff --git a/src/Data/maps.php b/src/Data/maps.php index 67365bf..d2db412 100644 --- a/src/Data/maps.php +++ b/src/Data/maps.php @@ -95,48 +95,48 @@ return [ 'min_level' => 1, 'desc' => '镜州边境的江湖门派,韩立修仙之路的起点。', 'monsters' => [ - [ - 'name' => '野狼帮帮众', - 'level' => 1, - 'hp' => 30, - 'patk' => 5, - 'matk' => 2, - 'pdef' => 0, - 'mdef' => 0, - 'exp' => 10, - 'spirit_stones' => 2, - 'drops' => [ - ['type' => 'weapon', 'name' => '铁刀', 'rate' => 25] + $weaponTemplate, - ['type' => 'consume', 'name' => '金疮药', 'rate' => 20, 'heal' => 30], - ], - 'spells' => [ - ['id' => 10, 'name' => '柔拳', 'rate' => 25], - ], - 'weight' => 60, - ], - [ - 'name' => '野狼帮精锐', - 'level' => 3, - 'hp' => 50, - 'patk' => 10, - 'matk' => 3, - 'pdef' => 2, - 'mdef' => 1, - 'exp' => 20, - 'spirit_stones' => 5, - 'drops' => [ - ['type' => 'armor', 'name' => '皮甲', 'rate' => 25] + $armorTemplate, - ['type' => 'consume', 'name' => '黄龙丹', 'rate' => 25, 'heal' => 50], - ], - 'spells' => [ - ['id' => 10, 'name' => '刀气切割', 'rate' => 20], - ['id' => 20, 'name' => '寒冰爆裂', 'rate' => 25], - ], - 'minions' => [ - ['name' => '野狼帮帮众', 'hp' => 30, 'patk' => 5, 'matk' => 2, 'pdef' => 0, 'mdef' => 0, 'exp' => 10, 'count' => 2], - ], - 'weight' => 30, - ], +// [ +// 'name' => '野狼帮帮众', +// 'level' => 1, +// 'hp' => 30, +// 'patk' => 5, +// 'matk' => 2, +// 'pdef' => 0, +// 'mdef' => 0, +// 'exp' => 10, +// 'spirit_stones' => 2, +// 'drops' => [ +// ['type' => 'weapon', 'name' => '铁刀', 'rate' => 25] + $weaponTemplate, +// ['type' => 'consume', 'name' => '金疮药', 'rate' => 20, 'heal' => 30], +// ], +// 'spells' => [ +// ['id' => 10, 'name' => '柔拳', 'rate' => 25], +// ], +// 'weight' => 60, +// ], +// [ +// 'name' => '野狼帮精锐', +// 'level' => 3, +// 'hp' => 50, +// 'patk' => 10, +// 'matk' => 3, +// 'pdef' => 2, +// 'mdef' => 1, +// 'exp' => 20, +// 'spirit_stones' => 5, +// 'drops' => [ +// ['type' => 'armor', 'name' => '皮甲', 'rate' => 25] + $armorTemplate, +// ['type' => 'consume', 'name' => '黄龙丹', 'rate' => 25, 'heal' => 50], +// ], +// 'spells' => [ +// ['id' => 10, 'name' => '刀气切割', 'rate' => 20], +// ['id' => 20, 'name' => '寒冰爆裂', 'rate' => 25], +// ], +// 'minions' => [ +// ['name' => '野狼帮帮众', 'hp' => 30, 'patk' => 5, 'matk' => 2, 'pdef' => 0, 'mdef' => 0, 'exp' => 10, 'count' => 2], +// ], +// 'weight' => 30, +// ], [ 'name' => '墨大夫', 'level' => 5, diff --git a/src/Entities/Monster.php b/src/Entities/Monster.php index 0ebb4af..eb47d68 100644 --- a/src/Entities/Monster.php +++ b/src/Entities/Monster.php @@ -17,9 +17,6 @@ class Monster extends Actor // Monster特有的掉落表 public array $dropTable = []; - // Monster穿着的法术(类似 $equip) - public array $spells = []; - public static function create(int $dungeonId): self { // Load data @@ -219,6 +216,7 @@ class Monster extends Actor $spellId = $spellConfig['id']; $customName = $spellConfig['name'] ?? null; $dropRate = $spellConfig['rate'] ?? 30; + if (rand(1, 100) > $dropRate) continue; // 随机决定法术品质:70% 普通, 20% 稀有, 8% 史诗, 2% 传奇 $roll = rand(1, 100); @@ -234,12 +232,12 @@ class Monster extends Actor if ($customName) { $spell['name'] = $customName; } - - // 添加掉落概率信息到法术对象 - $spell['dropRate'] = $dropRate; - - // 存储到 spells 数组 - $this->spells[] = $spell; + foreach ($this->skillSlots as $slot => $content) { + if ($content === null) { + $this->skillSlots[$slot] = $spell; + break; + } + } } } @@ -268,16 +266,13 @@ class Monster extends Actor * @param int $defaultRate 默认掉落概率(0-100),当法术未指定rate时使用 * @return array 掉落的法术列表 */ - public function getRandomSpellDrops(int $defaultRate = 50): array + public function getRandomSpellDrops(int $dropRate = 50): array { $drops = []; - foreach ($this->spells as $spell) { + foreach ($this->skillSlots as $spell) { if (!empty($spell)) { - // 优先使用法术配置的掉落概率,否则使用默认值 - $spellDropRate = $spell['dropRate'] ?? $defaultRate; - // 每个法术有独立的掉落概率 - if (rand(1, 100) <= $spellDropRate) { + if (rand(1, 100) <= $dropRate) { $drops[] = $spell; } } diff --git a/src/Modules/Battle.php b/src/Modules/Battle.php index e4d611e..762b69a 100644 --- a/src/Modules/Battle.php +++ b/src/Modules/Battle.php @@ -6,6 +6,7 @@ use Game\Core\Input; use Game\Core\Screen; use Game\Core\ItemDisplay; use Game\Core\SpellDisplay; +use Game\Core\SpellCalculator; use Game\Core\Colors; use Game\Entities\Player; use Game\Entities\Actor; @@ -120,7 +121,14 @@ class Battle // 初始化同伴HP $this->initPartnerHp(); - while ($this->player->hp >= 0) { + if ($this->player->hp <= 0) { + $out->writeln("{$this->red}你已经重伤倒地,无法继续战斗!请先去恢复生命值。{$this->reset}"); + Screen::pause($out); + $this->game->state = Game::MENU; + return; + } + + while ($this->player->hp > 0) { Screen::delay(500000); // 创建敌人群组 @@ -347,84 +355,146 @@ class Battle } /** - * 玩家施放法术 + * 获取敌对阵营 + * @return Actor[] */ - private function playerCastSpell($out): bool + private function getOpponents(Actor $actor): array { - $stats = $this->player->getStats(); - - // 筛选可用法术 - $availableSpells = []; - foreach ($this->player->skillSlots as $slot => $spellItem) { - if ($spellItem === null) continue; - - $baseCost = $spellItem['cost'] ?? 20; - $enhanceLevel = $spellItem['enhanceLevel'] ?? 0; - // 强化减少消耗: 每级 -2 - $costReduction = $enhanceLevel * 2; - $actualCost = max(1, $baseCost - $costReduction); - - if ($this->player->mana >= $actualCost) { - $availableSpells[] = [ - 'item' => $spellItem, - 'cost' => $actualCost, - 'level' => $enhanceLevel - ]; + if ($actor instanceof Player || $actor instanceof Partner) { + return $this->getAliveEnemies(); + } else { + // 怪物视角的敌人是玩家和同伴 + $opponents = []; + if ($this->player->hp > 0) $opponents[] = $this->player; + foreach ($this->getAlivePartners() as $partner) { + $opponents[] = $partner; } + return $opponents; } - - if (empty($availableSpells)) { - // 如果没有可用的法术,改用普通攻击 - $out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}✦ 魔法值不足,改为普通攻击{$this->reset}"); - return $this->executePhysicalAttack($out); - } - - // 随机选择一个可用的法术 - $selected = $availableSpells[array_rand($availableSpells)]; - $spellItem = $selected['item']; - $actualCost = $selected['cost']; - $enhanceLevel = $selected['level']; - - // 消耗魔法值 - $this->player->spendMana($actualCost); - - // 计算强化加成: 每级 +5% 伤害或治疗 - $damageBonus = $enhanceLevel * 5; - - $type = $spellItem['spellType'] ?? 'damage_single'; - $name = $spellItem['name'] ?? '未知法术'; - - // 构造兼容旧方法的 spellInfo - $spellInfo = $spellItem; - $spellInfo['type'] = $type; // 映射 spellType 到 type 以兼容 - - if ($type === 'damage_single') { - return $this->castDamageSingleSpell($out, 0, $spellInfo, $stats, $damageBonus, $name); - } elseif ($type === 'damage_aoe') { - return $this->castDamageAoeSpell($out, 0, $spellInfo, $stats, $damageBonus, $name); - } elseif ($type === 'heal_single') { - return $this->castHealSingleSpell($out, $spellInfo, $stats, $damageBonus, $name); - } elseif ($type === 'heal_aoe') { - return $this->castHealAoeSpell($out, $spellInfo, $stats, $damageBonus, $name); - } elseif ($type === 'support') { - // 兼容旧版本的 support 类型 - return $this->castSupportSpell($out, $spellInfo, $stats, $name); - } - - return false; } /** - * 施放单体伤害法术 + * 获取友方阵营 + * @return Actor[] */ - private function castDamageSingleSpell($out, int $spellId, array $spellInfo, array $stats, int $damageBonus, string $name): bool + private function getAllies(Actor $actor): array { - // 选择目标 - $target = null; - foreach ($this->enemies as $enemy) { - if ($enemy->hp > 0) { - $target = $enemy; - break; + if ($actor instanceof Player || $actor instanceof Partner) { + $allies = []; + if ($this->player->hp > 0) $allies[] = $this->player; + foreach ($this->getAlivePartners() as $partner) { + $allies[] = $partner; + } + return $allies; + } else { + return $this->getAliveEnemies(); // 怪物的友方是其他怪物 + } + } + + /** + * 智能选择法术 (通用) + */ + private function smartSelectSpell(Actor $actor): ?array + { + // 1. 分析战场状态 + $opponents = $this->getOpponents($actor); + $allies = $this->getAllies($actor); + + $enemyCount = count($opponents); + + $lowHpAllies = []; + foreach ($allies as $ally) { + if ($ally->hp < $ally->maxHp * 0.5) { + $lowHpAllies[] = $ally; + } + } + $lowHpCount = count($lowHpAllies); + + // 2. 筛选可用法术并分类 + $availableSpells = [ + 'heal_aoe' => [], + 'heal_single' => [], + 'damage_aoe' => [], + 'damage_single' => [], + 'support' => [], + 'all' => [] + ]; + + foreach ($actor->skillSlots as $slot => $spellItem) { + if ($spellItem === null) continue; + + $actualCost = SpellCalculator::calculateCost($spellItem); + + if ($actor->mana >= $actualCost) { + $spellData = [ + 'item' => $spellItem, + 'cost' => $actualCost, + 'level' => $spellItem['enhanceLevel'] ?? 0 + ]; + + $type = $spellItem['spellType'] ?? 'damage_single'; + + if (isset($availableSpells[$type])) { + $availableSpells[$type][] = $spellData; + } + $availableSpells['all'][] = $spellData; + } + } + + if (empty($availableSpells['all'])) { + return null; + } + + // 3. 智能选择策略 + $selected = null; + + // 策略A: 优先治疗 + if ($lowHpCount > 0) { + if ($lowHpCount > 1 && !empty($availableSpells['heal_aoe'])) { + $selected = $availableSpells['heal_aoe'][array_rand($availableSpells['heal_aoe'])]; + } elseif (!empty($availableSpells['heal_single'])) { + $selected = $availableSpells['heal_single'][array_rand($availableSpells['heal_single'])]; + } elseif (!empty($availableSpells['heal_aoe'])) { + $selected = $availableSpells['heal_aoe'][array_rand($availableSpells['heal_aoe'])]; + } + } + + // 策略B: 伤害输出 + if ($selected === null) { + if ($enemyCount > 1 && !empty($availableSpells['damage_aoe'])) { + $selected = $availableSpells['damage_aoe'][array_rand($availableSpells['damage_aoe'])]; + } elseif (!empty($availableSpells['damage_single'])) { + $selected = $availableSpells['damage_single'][array_rand($availableSpells['damage_single'])]; + } elseif (!empty($availableSpells['damage_aoe'])) { + $selected = $availableSpells['damage_aoe'][array_rand($availableSpells['damage_aoe'])]; + } + } + + // 策略C: 兜底 + if ($selected === null) { + $selected = $availableSpells['all'][array_rand($availableSpells['all'])]; + } + + if ($selected && in_array($selected['item']['spellType'],['heal_aoe', 'heal_single']) && $lowHpCount == 0){ + return []; + } + + return $selected; + } + + /** + * 施放单体伤害法术 (通用) + */ + private function castDamageSingleSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, int $damageBonus, string $name): bool + { + // 自动选择目标 + if (!$target) { + $opponents = $this->getOpponents($caster); + foreach ($opponents as $enemy) { + if ($enemy->hp > 0) { + $target = $enemy; + break; + } } } @@ -433,34 +503,29 @@ class Battle // 显示法术基础信息 $calcType = $spellInfo['calc_type'] ?? 'matk'; $calcDesc = SpellDisplay::getCalcTypeDescription($calcType); - $cost = $spellInfo['cost'] ?? 20; - $actualCost = max(1, $cost - (($spellInfo['enhanceLevel'] ?? 0) * 2)); + $actualCost = SpellCalculator::calculateCost($spellInfo); $quality = $spellInfo['quality'] ?? 'common'; $qualityColor = SpellDisplay::getQualityColor($quality); // 计算法术伤害 - $baseDamageMultiplier = $spellInfo['damage_ratio'][$this->getQualityIndex($quality)] ?? ($spellInfo['damage'] ?? 1.0); - $actualDamageMultiplier = $baseDamageMultiplier * (1 + $damageBonus / 100); - $baseDamage = (int)($stats['matk'] * $actualDamageMultiplier); - - // 计算抵抗 - $resistance = $target->mdef; - $damage = max(5, $baseDamage - $resistance); - - // 暴击机制同样适用 - $critRate = $stats['crit']; - $isCrit = rand(1, 100) <= $critRate; + $damageResult = SpellCalculator::calculateDamage($spellInfo, $stats, $target->getStats(), $damageBonus); + $damage = $damageResult['damage']; + $isCrit = $damageResult['isCrit']; + $baseDamageMultiplier = $damageResult['multiplier']; // 显示法术施放信息 - $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✦{$this->reset} 你施放 {$qualityColor}{$name}{$this->reset}"); - $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost} | 倍数: {$baseDamageMultiplier}x{$this->reset}"); + $casterName = ($caster instanceof Player) ? "你" : $caster->name; + $actionVerb = ($caster instanceof Player) ? "施放" : "施放了"; + + $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}"); + if ($caster instanceof Player) { + $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost} | 倍数: {$baseDamageMultiplier}x{$this->reset}"); + } if ($isCrit) { - $critDmg = $stats['critdmg']; - $damage = (int)($damage * ($critDmg / 100)); - $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ {$this->red}{$this->bold}暴击!{$this->reset} 造成 {$this->red}{$damage}{$this->reset} 点魔法伤害!"); + $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ {$this->red}{$this->bold}暴击!{$this->reset} 对 {$target->name} 造成 {$this->red}{$damage}{$this->reset} 点魔法伤害!"); } else { - $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ 造成 {$this->green}{$damage}{$this->reset} 点魔法伤害"); + $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ 对 {$target->name} 造成 {$this->green}{$damage}{$this->reset} 点魔法伤害"); } $target->hp -= $damage; @@ -468,10 +533,18 @@ class Battle if ($target->hp <= 0) { $target->hp = 0; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}"); - - if (empty($this->getAliveEnemies())) { + + // 如果是玩家击败了所有敌人 + if (($caster instanceof Player || $caster instanceof Partner) && empty($this->getAliveEnemies())) { Screen::delay(500000); - $this->showVictory($out, $stats); + $this->showVictory($out, $stats); + return true; + } + + // 如果是敌人击败了玩家 + if ($target instanceof Player) { + Screen::delay(500000); + $this->showDefeat($out, $caster); return true; } } @@ -480,41 +553,34 @@ class Battle } /** - * 施放AOE伤害法术 + * 施放AOE伤害法术 (通用) */ - private function castDamageAoeSpell($out, int $spellId, array $spellInfo, array $stats, int $damageBonus, string $name): bool + private function castDamageAoeSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, int $damageBonus, string $name): bool { // 显示法术基础信息 $calcType = $spellInfo['calc_type'] ?? 'matk'; $calcDesc = SpellDisplay::getCalcTypeDescription($calcType); - $cost = $spellInfo['cost'] ?? 20; - $actualCost = max(1, $cost - (($spellInfo['enhanceLevel'] ?? 0) * 2)); + $actualCost = SpellCalculator::calculateCost($spellInfo); $quality = $spellInfo['quality'] ?? 'common'; $qualityColor = SpellDisplay::getQualityColor($quality); - $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✦{$this->reset} 你施放 {$qualityColor}{$name}{$this->reset}"); - $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost}{$this->reset}"); + $casterName = ($caster instanceof Player) ? "你" : $caster->name; + $actionVerb = ($caster instanceof Player) ? "施放" : "施放了"; + + $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}"); + if ($caster instanceof Player) { + $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost}{$this->reset}"); + } $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ 魔法在整个战场爆炸!{$this->reset}"); - // 计算法术伤害 - $baseDamageMultiplier = $spellInfo['damage_ratio'][$this->getQualityIndex($quality)] ?? ($spellInfo['damage'] ?? 0.8); - $actualDamageMultiplier = $baseDamageMultiplier * (1 + $damageBonus / 100); + $opponents = $this->getOpponents($caster); + $allOpponentsDefeated = true; - $allEnemiesDefeated = true; - foreach ($this->enemies as $enemy) { + foreach ($opponents as $enemy) { if ($enemy->hp <= 0) continue; - $baseDamage = (int)($stats['matk'] * $actualDamageMultiplier); - $resistance = $enemy->mdef; - $damage = max(5, $baseDamage - $resistance); - - // AOE 法术也可以暴击 - $critRate = $stats['crit']; - $isCrit = rand(1, 100) <= ($critRate / 2); // AOE 暴击率减半 - if ($isCrit) { - $critDmg = $stats['critdmg']; - $damage = (int)($damage * ($critDmg / 100)); - } + $damageResult = SpellCalculator::calculateDamage($spellInfo, $stats, $enemy->getStats(), $damageBonus, true); + $damage = $damageResult['damage']; $out->writeln("{$this->cyan}║{$this->reset} {$enemy->name} 受到 {$damage} 点伤害"); $enemy->hp -= $damage; @@ -522,12 +588,27 @@ class Battle if ($enemy->hp <= 0) { $enemy->hp = 0; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$enemy->name} 被击败了!{$this->reset}"); + + if ($enemy instanceof Player) { + Screen::delay(500000); + $this->showDefeat($out, $caster); + return true; + } } else { - $allEnemiesDefeated = false; + $allOpponentsDefeated = false; } } + + // 更新同伴血量显示 (如果敌人是同伴) + if (!($caster instanceof Player) && !($caster instanceof Partner)) { + foreach ($this->player->partners as $partner) { + if (isset($this->partnerHp[$partner->id])) { + $this->partnerHp[$partner->id] = $partner->hp; + } + } + } - if ($allEnemiesDefeated && empty($this->getAliveEnemies())) { + if ($allOpponentsDefeated && ($caster instanceof Player || $caster instanceof Partner) && empty($this->getAliveEnemies())) { Screen::delay(500000); $this->showVictory($out, $stats); return true; @@ -537,121 +618,132 @@ class Battle } /** - * 施放单体治疗法术 + * 施放单体治疗法术 (通用) */ - private function castHealSingleSpell($out, array $spellInfo, array $stats, int $healBonus, string $name): bool + private function castHealSingleSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, int $healBonus, string $name): bool { + // 自动选择目标 (优先治疗血量最低的友方) + if (!$target) { + $allies = $this->getAllies($caster); + $lowestHpRatio = 1.0; + foreach ($allies as $ally) { + $ratio = $ally->hp / $ally->maxHp; + if ($ratio < $lowestHpRatio) { + $lowestHpRatio = $ratio; + $target = $ally; + } + } + // 如果都满血,治疗自己 + if (!$target) $target = $caster; + } + // 显示法术基础信息 $calcType = $spellInfo['calc_type'] ?? 'matk'; $calcDesc = SpellDisplay::getCalcTypeDescription($calcType); - $cost = $spellInfo['cost'] ?? 20; + $actualCost = SpellCalculator::calculateCost($spellInfo); $quality = $spellInfo['quality'] ?? 'common'; $qualityColor = SpellDisplay::getQualityColor($quality); - $actualCost = max(1, $cost - (($spellInfo['enhanceLevel'] ?? 0) * 2)); - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} 你施放 {$qualityColor}{$name}{$this->reset}"); - $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost}{$this->reset}"); + $casterName = ($caster instanceof Player) ? "你" : $caster->name; + $actionVerb = ($caster instanceof Player) ? "施放" : "施放了"; - $heal = $spellInfo['heal'] ?? 0.5; - $healBase = $spellInfo['heal_base'] ?? 20; - $healAmount = (int)($stats['matk'] * $heal + $healBase); + $out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}"); + if ($caster instanceof Player) { + $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost}{$this->reset}"); + } - // 应用治疗加成 - $healAmount = (int)($healAmount * (1 + $healBonus / 100)); + $healAmount = SpellCalculator::calculateHeal($spellInfo, $stats, $healBonus); - $actualHeal = $this->player->heal($healAmount); - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 恢复了 {$actualHeal} 点生命值{$this->reset}"); + $actualHeal = $target->heal($healAmount); + + // 更新同伴血量显示 + if ($target instanceof Partner && isset($this->partnerHp[$target->id])) { + $this->partnerHp[$target->id] = $target->hp; + } + + $targetName = ($target === $this->player) ? "你" : $target->name; + $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 {$targetName} 恢复了 {$actualHeal} 点生命值{$this->reset}"); return false; } /** - * 施放群体治疗法术 + * 施放群体治疗法术 (通用) */ - private function castHealAoeSpell($out, array $spellInfo, array $stats, int $healBonus, string $name): bool + private function castHealAoeSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, int $healBonus, string $name): bool { // 显示法术基础信息 $calcType = $spellInfo['calc_type'] ?? 'matk'; $calcDesc = SpellDisplay::getCalcTypeDescription($calcType); - $cost = $spellInfo['cost'] ?? 20; + $actualCost = SpellCalculator::calculateCost($spellInfo); $quality = $spellInfo['quality'] ?? 'common'; $qualityColor = SpellDisplay::getQualityColor($quality); - $actualCost = max(1, $cost - (($spellInfo['enhanceLevel'] ?? 0) * 2)); - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} 你施放 {$qualityColor}{$name}{$this->reset}"); - $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost}{$this->reset}"); + $casterName = ($caster instanceof Player) ? "你" : $caster->name; + $actionVerb = ($caster instanceof Player) ? "施放" : "施放了"; - $heal = $spellInfo['heal'] ?? 0.5; - $healBase = $spellInfo['heal_base'] ?? 20; - $healAmount = (int)($stats['matk'] * $heal + $healBase); + $out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}"); + if ($caster instanceof Player) { + $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost}{$this->reset}"); + } - // 应用治疗加成 - $healAmount = (int)($healAmount * (1 + $healBonus / 100)); + $healAmount = SpellCalculator::calculateHeal($spellInfo, $stats, $healBonus); - $actualHeal = $this->player->heal($healAmount); - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 你恢复了 {$actualHeal} 点生命值{$this->reset}"); + $allies = $this->getAllies($caster); + + foreach ($allies as $ally) { + if ($ally->hp <= 0) continue; + + // 如果是施法者本人,全额治疗;如果是队友,80%效果 + $finalHeal = ($ally === $caster) ? $healAmount : (int)($healAmount * 0.8); + + $actualHeal = $ally->heal($finalHeal); + + // 更新同伴血量显示 + if ($ally instanceof Partner && isset($this->partnerHp[$ally->id])) { + $this->partnerHp[$ally->id] = $ally->hp; + } - // 同伴也恢复 - $alivePartners = $this->getAlivePartners(); - foreach ($alivePartners as $partner) { - $partnerHeal = (int)($healAmount * 0.8); // 同伴恢复量为玩家的80% - $actualPartnerHeal = $partner->heal($partnerHeal); - $this->partnerHp[$partner->id] = $partner->hp; - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 {$partner->name} 恢复了 {$actualPartnerHeal} 点生命值{$this->reset}"); + $allyName = ($ally === $this->player) ? "你" : $ally->name; + $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 {$allyName} 恢复了 {$actualHeal} 点生命值{$this->reset}"); } return false; } /** - * 施放辅助法术 + * 施放辅助法术 (通用) */ - private function castSupportSpell($out, array $spellInfo, array $stats, string $name): bool + /** + * 施放辅助法术 (通用) + */ + private function castSupportSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, string $name): bool { // 显示法术基础信息 $quality = $spellInfo['quality'] ?? 'common'; $qualityColor = SpellDisplay::getQualityColor($quality); - $cost = $spellInfo['cost'] ?? 20; - $actualCost = max(1, $cost - (($spellInfo['enhanceLevel'] ?? 0) * 2)); + $actualCost = SpellCalculator::calculateCost($spellInfo); - $out->writeln("{$this->cyan}║{$this->reset} {$this->cyan}✦{$this->reset} 你施放 {$qualityColor}{$name}{$this->reset}"); - $out->writeln("{$this->cyan}║{$this->reset} {$this->white}消耗: {$actualCost}{$this->reset}"); + $casterName = ($caster instanceof Player) ? "你" : $caster->name; + $actionVerb = ($caster instanceof Player) ? "施放" : "施放了"; + + $out->writeln("{$this->cyan}║{$this->reset} {$this->cyan}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}"); + if ($caster instanceof Player) { + $out->writeln("{$this->cyan}║{$this->reset} {$this->white}消耗: {$actualCost}{$this->reset}"); + } $subtype = $spellInfo['subtype'] ?? ''; - if ($subtype === 'heal' || $subtype === 'heal_all') { + // 委托给治疗逻辑 + $healBonus = 0; // 辅助类治疗通常没有额外加成 if ($subtype === 'heal') { - // 恢复自己 - $heal = $spellInfo['heal'] ?? 0.5; - $healBase = $spellInfo['heal_base'] ?? 20; - $healAmount = (int)($stats['matk'] * $heal + $healBase); - - $actualHeal = $this->player->heal($healAmount); - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} 你施放 {$name}..."); - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 恢复了 {$actualHeal} 点生命值{$this->reset}"); + return $this->castHealSingleSpell($out, $caster, $target, $spellInfo, $stats, $healBonus, $name); } else { - // 恢复全体 - $heal = $spellInfo['heal'] ?? 0.6; - $healBase = $spellInfo['heal_base'] ?? 40; - $healAmount = (int)($stats['matk'] * $heal + $healBase); - - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} 你施放 {$name}..."); - $actualHeal = $this->player->heal($healAmount); - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 你恢复了 {$actualHeal} 点生命值{$this->reset}"); - - // 同伴也恢复 - $alivePartners = $this->getAlivePartners(); - foreach ($alivePartners as $partner) { - $partnerHeal = (int)($healAmount * 0.8); // 同伴恢复量为玩家的80% - $actualPartnerHeal = $partner->heal($partnerHeal); - $this->partnerHp[$partner->id] = $partner->hp; - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 {$partner->name} 恢复了 {$actualPartnerHeal} 点生命值{$this->reset}"); - } + return $this->castHealAoeSpell($out, $caster, $target, $spellInfo, $stats, $healBonus, $name); } } elseif ($subtype === 'defend') { - $defenseBoost = $spellInfo['defense_boost'] ?? 30; - $out->writeln("{$this->cyan}║{$this->reset} {$this->cyan}✦{$this->reset} 你施放 {$name}..."); + // 简单的防御提升逻辑 (目前只是显示,实际效果需在伤害计算中支持buff) $out->writeln("{$this->cyan}║{$this->reset} {$this->cyan}🛡️ 防御力提升!{$this->reset}"); } @@ -659,143 +751,124 @@ class Battle } /** - * 执行普通物理攻击 + * 执行角色的回合行动 (通用) */ - private function executePhysicalAttack($out): bool + private function executeActorTurn(Actor $actor, $out): bool { - $stats = $this->player->getStats(); + if ($actor->hp <= 0){ + return false; + } + // 1. 尝试使用法术 + $selectedSpell = $this->smartSelectSpell($actor); + if ($selectedSpell) { + $spellItem = $selectedSpell['item']; + $actualCost = $selectedSpell['cost']; + $enhanceLevel = $selectedSpell['level']; + + // 消耗魔法值 + $actor->spendMana($actualCost); + + // 计算强化加成 + $damageBonus = $enhanceLevel * 5; + $type = $spellItem['spellType'] ?? 'damage_single'; + $name = $spellItem['name'] ?? '未知法术'; + + $spellInfo = $spellItem; + $spellInfo['type'] = $type; + + $stats = $actor->getStats(); + + if ($type === 'damage_single') { + return $this->castDamageSingleSpell($out, $actor, null, $spellInfo, $stats, $damageBonus, $name); + } elseif ($type === 'damage_aoe') { + return $this->castDamageAoeSpell($out, $actor, null, $spellInfo, $stats, $damageBonus, $name); + } elseif ($type === 'heal_single') { + return $this->castHealSingleSpell($out, $actor, null, $spellInfo, $stats, $damageBonus, $name); + } elseif ($type === 'heal_aoe') { + return $this->castHealAoeSpell($out, $actor, null, $spellInfo, $stats, $damageBonus, $name); + } elseif ($type === 'support') { + return $this->castSupportSpell($out, $actor, null, $spellInfo, $stats, $name); + } + } + + // 2. 如果没有使用法术,执行普通攻击 + // 寻找目标 $target = null; - foreach ($this->enemies as $enemy) { + $opponents = $this->getOpponents($actor); + foreach ($opponents as $enemy) { if ($enemy->hp > 0) { $target = $enemy; break; } } - if (!$target) return true; + if (!$target) return true; // 无目标,回合结束 - $physicalDamage = max(1, $stats['patk'] - $target->pdef); - $magicDamage = max(0, $stats['matk'] - $target->mdef); + $stats = $actor->getStats(); + $targetStats = $target->getStats(); + + // 计算伤害 + $physicalDamage = max(1, $stats['patk'] - $targetStats['pdef']); + $magicDamage = max(0, $stats['matk'] - $targetStats['mdef']); $baseDamage = $physicalDamage + $magicDamage; $critRate = $stats['crit']; $critDmg = $stats['critdmg']; - $isCrit = rand(1, 100) <= $critRate; + $actorName = ($actor instanceof Player) ? "你" : $actor->name; + $targetName = ($target === $this->player) ? "你" : $target->name; + $actionVerb = ($actor instanceof Player) ? "攻击" : "向 {$targetName} 发起攻击"; + + if ($actor instanceof Player) { + $out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$targetName}..."); + } else { + $out->writeln("{$this->cyan}║{$this->reset} {$this->red}➤{$this->reset} {$actorName} {$actionVerb}..."); + } + if ($isCrit) { $damage = (int)($baseDamage * ($critDmg / 100)); - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$target->name}... {$this->red}{$this->bold}暴击!{$this->reset}"); - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点伤害!{$this->reset}"); + $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点暴击伤害!{$this->reset}"); } else { $damage = $baseDamage; - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$target->name}..."); $out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}"); } $target->hp -= $damage; + + // 更新同伴血量显示 + if ($target instanceof Partner && isset($this->partnerHp[$target->id])) { + $this->partnerHp[$target->id] = $target->hp; + } if ($target->hp <= 0) { $target->hp = 0; - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}"); - - if (empty($this->getAliveEnemies())) { + $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$targetName} 倒下了!{$this->reset}"); + + if (($actor instanceof Player || $actor instanceof Partner) && empty($this->getAliveEnemies())) { Screen::delay(500000); $this->showVictory($out, $stats); return true; } + + if ($target instanceof Player) { + Screen::delay(500000); + $this->showDefeat($out, $actor); + return true; + } } return false; } - /** - * 获取法术信息 - */ - private function getSpellInfo(int $spellId): ?array - { - foreach ($this->spellsData as $category => $spells) { - if (is_array($spells) && $category !== 'quality_levels' && $category !== 'upgrades') { - if (isset($spells[$spellId])) { - return $spells[$spellId]; - } - } - } - return null; - } - - /** - * 根据品质获取数组索引 - */ - private function getQualityIndex(string $quality): int - { - return match($quality) { - 'common' => 0, - 'rare' => 1, - 'epic' => 2, - 'legendary' => 3, - default => 0, - }; - } - private function playerAttack($out): bool { - $stats = $this->player->getStats(); - // 显示玩家选择菜单 - $choice = $this->playerChooseAction(); - - if ($choice === 'spell') { - return $this->playerCastSpell($out); + if ($this->executeActorTurn($this->player, $out)) { + return true; // 战斗结束 } - - // 普通攻击逻辑 - // Target first alive enemy - $target = null; - foreach ($this->enemies as $enemy) { - if ($enemy->hp > 0) { - $target = $enemy; - break; - } - } - - if (!$target) return true; // All dead - - // 计算物理伤害和魔法伤害 - $physicalDamage = max(1, $stats['patk'] - $target->pdef); - $magicDamage = max(0, $stats['matk'] - $target->mdef); - $baseDamage = $physicalDamage + $magicDamage; - - $critRate = $stats['crit']; - $critDmg = $stats['critdmg']; - - $isCrit = rand(1, 100) <= $critRate; - - if ($isCrit) { - $damage = (int)($baseDamage * ($critDmg / 100)); - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$target->name}... {$this->red}{$this->bold}暴击!{$this->reset}"); - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点伤害!{$this->reset}"); - } else { - $damage = $baseDamage; - $out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$target->name}..."); - $out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}"); - } - - $target->hp -= $damage; - - if ($target->hp <= 0) { - $target->hp = 0; - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}"); - - // Check if all enemies are dead - if (empty($this->getAliveEnemies())) { - Screen::delay(500000); - $this->showVictory($out, $stats); - return true; - } - } - + Screen::delay(300000); return false; } @@ -807,110 +880,27 @@ class Battle $alivePartners = $this->getAlivePartners(); foreach ($alivePartners as $partner) { - // Target first alive enemy - $target = null; - foreach ($this->enemies as $enemy) { - if ($enemy->hp > 0) { - $target = $enemy; - break; - } + if ($this->executeActorTurn($partner, $out)) { + return true; // 战斗结束 } - - if (!$target) return true; // All dead - - $stats = $partner->getStats(); - - // 计算物理伤害和魔法伤害 - $physicalDamage = max(1, $stats['patk'] - $target->pdef); - $magicDamage = max(0, $stats['matk'] - $target->mdef); - $baseDamage = $physicalDamage + $magicDamage; - - $critRate = $stats['crit']; - $critDmg = $stats['critdmg']; - - $isCrit = rand(1, 100) <= $critRate; - - if ($isCrit) { - $damage = (int)($baseDamage * ($critDmg / 100)); - $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}➤{$this->reset} {$partner->name} 攻击 {$target->name}... {$this->red}{$this->bold}暴击!{$this->reset}"); - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点伤害!{$this->reset}"); - } else { - $damage = $baseDamage; - $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}➤{$this->reset} {$partner->name} 攻击 {$target->name}..."); - $out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}"); - } - - $target->hp -= $damage; - - if ($target->hp <= 0) { - $target->hp = 0; - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}"); - - if (empty($this->getAliveEnemies())) { - Screen::delay(500000); - $this->showVictory($out, $this->player->getStats()); - return true; - } - } - - Screen::delay(400000); // 每个同伴攻击间隔 + Screen::delay(300000); } return false; } + /** + * 怪物攻击 + */ private function enemiesAttack($out): bool { - $aliveEnemies = $this->getAliveEnemies(); - - foreach ($aliveEnemies as $enemy) { - // 选择攻击目标:玩家或存活的同伴 - $alivePartners = $this->getAlivePartners(); - $targets = ['player']; - foreach ($alivePartners as $partner) { - $targets[] = $partner->id; + foreach ($this->enemies as $enemy) { + if ($enemy->hp <= 0) continue; + + if ($this->executeActorTurn($enemy, $out)) { + return true; // 战斗结束 } - - $targetIdx = array_rand($targets); - $target = $targets[$targetIdx]; - - if ($target === 'player') { - // 攻击玩家 - $playerStats = $this->player->getStats(); - $physicalDamage = max(1, $enemy->patk - $playerStats['pdef']); - $magicDamage = max(0, $enemy->matk - $playerStats['mdef']); - $damage = $physicalDamage + $magicDamage; - - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}➤{$this->reset} {$enemy->name} 向你发起攻击..."); - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💢 你受到 {$damage} 点伤害{$this->reset}"); - - $this->player->hp -= $damage; - - if ($this->player->hp <= 0) { - $this->player->hp = 0; - Screen::delay(500000); - $this->showDefeat($out, $enemy); - return true; - } - } else { - // 攻击同伴 - $partner = $this->player->partners[$target]; - $partnerStats = $partner->getStats(); - $physicalDamage = max(1, $enemy->patk - $partnerStats['pdef']); - $magicDamage = max(0, $enemy->matk - $partnerStats['mdef']); - $damage = $physicalDamage + $magicDamage; - - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}➤{$this->reset} {$enemy->name} 向 {$partner->name} 发起攻击..."); - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💢 {$partner->name} 受到 {$damage} 点伤害{$this->reset}"); - - $this->partnerHp[$target] -= $damage; - - if ($this->partnerHp[$target] <= 0) { - $this->partnerHp[$target] = 0; - $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$partner->name} 倒下了!{$this->reset}"); - } - } - Screen::delay(400000); + Screen::delay(300000); } return false; diff --git a/src/Modules/InventoryPanel.php b/src/Modules/InventoryPanel.php index eff6e89..6aa84bd 100644 --- a/src/Modules/InventoryPanel.php +++ b/src/Modules/InventoryPanel.php @@ -85,19 +85,10 @@ class InventoryPanel } foreach ($pageItems as $i => $item) { - $index = $start + $i + 1; - - // 根据物品类型使用不同的显示方式 - if (($item['type'] ?? '') === 'spell') { - // 法术使用 SpellDisplay 显示 - $displayStr = ItemDisplay::renderListItem($item, true, true); - $spellInfo = SpellDisplay::formatSpellCompact($item); - $displayStr = trim($displayStr) . " " . $spellInfo; - } else { // 装备使用 ItemDisplay 显示 - $displayStr = ItemDisplay::renderListItem($item, true, true); - } + $displayStr = ItemDisplay::renderListItem($item, true, true); + $out->writeln("[{$index}] {$displayStr}"); } diff --git a/test/Test.php b/test/Test.php index c572aed..84f8348 100644 --- a/test/Test.php +++ b/test/Test.php @@ -1,6 +1,12 @@ getRandomEquipmentDrops()); \ No newline at end of file +$player = new \Game\Entities\Player(); + +$res = Item::createSpell(1, 'common', 10); + dd($monster->equip,$monster->getStats(),$monster->name,$monster->skillSlots); \ No newline at end of file