Compare commits
2 Commits
5caff28f37
...
cd98a5a452
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd98a5a452 | ||
|
|
b2037e2eed |
|
|
@ -3,7 +3,7 @@
|
||||||
"description": "A PHP terminal game using Symfony src/Console",
|
"description": "A PHP terminal game using Symfony src/Console",
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.0",
|
"php": ">=8.1",
|
||||||
"symfony/console": "^6.4",
|
"symfony/console": "^6.4",
|
||||||
"symfony/var-dumper": "^6.4",
|
"symfony/var-dumper": "^6.4",
|
||||||
"cboden/ratchet": "^0.4"
|
"cboden/ratchet": "^0.4"
|
||||||
|
|
|
||||||
62
src/Data/map.json
Normal file
62
src/Data/map.json
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "凡尘遇仙缘",
|
||||||
|
"key_item": null,
|
||||||
|
"desc": "游戏初始阶段。玩家作为七玄门弟子,经历墨大夫事件,意外获得掌天瓶,踏入修仙世界。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "黄枫谷筑基",
|
||||||
|
"key_item": "升仙令",
|
||||||
|
"desc": "持升仙令加入黄枫谷。完成宗门基础任务,在血色禁地试炼中成功采集到足够灵药,为筑基做准备。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "魔道入侵与流亡",
|
||||||
|
"key_item": "古传送阵残图",
|
||||||
|
"desc": "完成金鼓原前线系列战役任务。在宗门覆灭的混乱中,根据残图线索找到并修复古传送阵,输入灵石后激活,传送至乱星海。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "乱星海崛起",
|
||||||
|
"key_item": "结丹期修为",
|
||||||
|
"desc": "在乱星海积累资源,修为达到筑基后期巅峰,并成功获取一枚“降尘丹”或同级丹药,方可尝试结丹,开启本阶段核心内容。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "逆星盟风云",
|
||||||
|
"key_item": "虚天残图",
|
||||||
|
"desc": "至少拥有一张虚天残图,并修为达到结丹后期。卷入星宫与逆星盟的争斗,获得进入核心事件圈的资格。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "元婴大成与回归",
|
||||||
|
"key_item": "九曲灵参",
|
||||||
|
"desc": "集齐凝结元婴所需的全部辅助材料(关键物品“九曲灵参”),且修为达到结丹后期大圆满,方可闭关冲击元婴期。成功后触发回归天南任务。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "天南霸主之路",
|
||||||
|
"key_item": "元婴初期修为",
|
||||||
|
"desc": "成功凝结元婴,并加入天南某个大宗门(如落云宗),获得宗门长老身份,方可参与天南顶级修士的会议与事件。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "大晋游历",
|
||||||
|
"key_item": "大晋地域图",
|
||||||
|
"desc": "从天南顶级交易会或任务中,获得通往修仙圣地“大晋”的完整路线图与信物,修为需达元婴中期。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "昆吾山诛仙",
|
||||||
|
"key_item": "开启昆吾山的封印密钥",
|
||||||
|
"desc": "在“大晋游历”阶段完成一系列上古秘闻任务后,获得昆吾山现世的关键信息及封印密钥,修为需达元婴后期。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "化神之境",
|
||||||
|
"key_item": "五行灵婴(集齐)",
|
||||||
|
"desc": "集齐所有五行属性的天地灵婴或替代品,并完成“元磁神光”修炼前置任务。此为冲击化神境的必备资源条件。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "破碎虚空",
|
||||||
|
"key_item": "稳定的空间节点坐标",
|
||||||
|
"desc": "成功进阶化神期,并在游历天下任务中,搜集齐五块“坐标石碑”碎片,合成出通往灵界的稳定空间节点坐标。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "人界终章与传承",
|
||||||
|
"key_item": "飞升通道的临时通行符",
|
||||||
|
"desc": "在“破碎虚空”阶段,与冰凤合作稳固空间节点后,由天地法则凝聚而成的一次性通行符箓。使用它即可触发最终飞升剧情,并进入传承安排阶段。"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -16,7 +16,6 @@ return [
|
||||||
// ============================================================
|
// ============================================================
|
||||||
1 => [
|
1 => [
|
||||||
'name' => '七玄门',
|
'name' => '七玄门',
|
||||||
'min_level' => 1,
|
|
||||||
'key_item' => null, // 初始区域,无需钥匙
|
'key_item' => null, // 初始区域,无需钥匙
|
||||||
'desc' => '镜州边境的江湖门派,韩立在此获得神秘小瓶,开启修仙之路。',
|
'desc' => '镜州边境的江湖门派,韩立在此获得神秘小瓶,开启修仙之路。',
|
||||||
'monsters' => [
|
'monsters' => [
|
||||||
|
|
@ -29,11 +28,7 @@ return [
|
||||||
'pdef' => 5,
|
'pdef' => 5,
|
||||||
'mdef' => 0,
|
'mdef' => 0,
|
||||||
'exp' => 10,
|
'exp' => 10,
|
||||||
'spirit_stones' => 0, // 凡人没有灵石,改为银两(逻辑上)
|
'spirit_stones' => 0,
|
||||||
'drops' => [],
|
|
||||||
'spells' => [
|
|
||||||
['id' => 11, 'name' => '罗烟步(伪)', 'rate' => 20],
|
|
||||||
],
|
|
||||||
'weight' => 50,
|
'weight' => 50,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|
|
||||||
3698
src/Data/monster.json
Normal file
3698
src/Data/monster.json
Normal file
File diff suppressed because it is too large
Load Diff
202
src/Data/monster_skills.json
Normal file
202
src/Data/monster_skills.json
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
[
|
||||||
|
"连环刀法:连续攻击2-3次,每次造成90%伤害",
|
||||||
|
"狼群召唤:召唤2只野狼助战(hp: 30, patk: 8)",
|
||||||
|
"绝地反击:生命低于30%时,攻击力提升30%,持续3回合",
|
||||||
|
"长春真气:恢复自身15%最大生命值",
|
||||||
|
"毒术·七步断魂:使目标中毒,每回合损失5%最大生命值,持续5回合",
|
||||||
|
"元神冲击:高概率造成眩晕1回合",
|
||||||
|
"夺舍准备:生命低于20%时,防御力清零,但法术攻击力提升50%",
|
||||||
|
"金光术:群体光系法术,对全体造成85%法术伤害",
|
||||||
|
"飞剑术:单体高伤害法术,无视30%法术防御",
|
||||||
|
"金刚符:为自己施加护盾,吸收200点伤害",
|
||||||
|
"遁地符:生命低于30%时,有50%概率逃跑(战斗失败)",
|
||||||
|
"蛟龙摆尾:对前排造成150%物理伤害,概率击退",
|
||||||
|
"玄冰吐息:对全体造成120%法术伤害,附加冰冻效果(减速50%,持续2回合)",
|
||||||
|
"水幕天华:召唤水幕护盾,减免30%受到的所有伤害,持续3回合",
|
||||||
|
"狂蛟之怒:生命低于40%时进入狂暴状态,攻击力提升40%,防御力降低20%",
|
||||||
|
"冰封千里:对全体造成135%冰系法术伤害,概率冻结1回合",
|
||||||
|
"月华之盾:生成吸收500点伤害的护盾,持续2回合",
|
||||||
|
"寒冰箭雨:对随机3个目标造成180%法术伤害",
|
||||||
|
"镜花水月:制造幻象分身,躲避一次致命攻击(每场战斗触发一次)",
|
||||||
|
"幻象重生:召唤玩家的心魔幻象(属性为玩家的80%),必须击败",
|
||||||
|
"筑基雷劫:象征性天劫,每回合对玩家造成固定50点真实伤害(无视防御),持续5回合",
|
||||||
|
"烈火剑法:对单个目标造成200%物理伤害,附加灼烧效果(每回合损失5%最大生命值,持续3回合)",
|
||||||
|
"土遁术:闪避下一次攻击,并恢复10%最大生命值",
|
||||||
|
"召唤灵兽:召唤一只炼气十三层的铁甲兽助战(hp: 350, patk: 40)",
|
||||||
|
"焚天魔焰:对全体造成150%火系法术伤害,附加灼烧效果(每回合损失8%最大生命值,持续3回合)",
|
||||||
|
"炎魔护体:自身施加火焰护盾,反弹30%受到的物理伤害,持续3回合",
|
||||||
|
"火雨流星:对随机4个目标造成200%法术伤害",
|
||||||
|
"分身自爆:生命低于20%时,分身自爆,对全体造成300%法术伤害(可被中断)",
|
||||||
|
"百鬼夜行:召唤5只厉鬼攻击全体,每只造成80%法术伤害",
|
||||||
|
"尸王召唤:召唤一只铁甲尸王(hp: 800, patk: 70, pdef: 35)助战",
|
||||||
|
"阴魂锁链:束缚单个目标2回合,使其无法行动并每回合损失10%最大生命值",
|
||||||
|
"血遁术:生命低于30%时,有概率使用血遁逃走(若成功逃走则战斗结束,但奖励减半)",
|
||||||
|
"空间撕裂:对前排造成180%物理伤害,并降低其防御30%,持续2回合",
|
||||||
|
"次元转移:将自身受到的50%伤害转移给随机友方单位(包括玩家召唤物)",
|
||||||
|
"时空扭曲:使全体敌人的下一个技能延迟1回合发动",
|
||||||
|
"虚空吞噬:吞噬一个敌方目标(非玩家控制角色),使其暂时退出战斗,2回合后吐出并造成其最大生命值50%的伤害",
|
||||||
|
"黄枫剑诀·叛:对单个目标造成220%物理伤害,对原黄枫谷弟子(玩家)额外造成30%伤害",
|
||||||
|
"护身法宝:激活一件护身法宝,减免40%受到的伤害,持续2回合(每场战斗可使用2次)",
|
||||||
|
"召唤援军:召唤2名魔道精英弟子助战(hp: 300, patk: 45)",
|
||||||
|
"临阵倒戈:战斗中概率触发,若玩家有“令狐老祖的密令”道具,可说服叶师叔倒戈(战斗直接胜利)",
|
||||||
|
"元婴威压:每回合开始时,对全体造成固定100点真实伤害(无视防御)",
|
||||||
|
"魔爪擒拿:试图擒拿玩家,若成功则战斗立即失败",
|
||||||
|
"天罗地网:封锁战场,降低全体玩家单位50%速度,持续3回合",
|
||||||
|
"灭魂一击:对生命值低于30%的目标直接斩杀(对玩家无效,但对召唤物和NPC有效)",
|
||||||
|
"千目幻光:对全体造成130%法术伤害,并有概率使目标陷入混乱状态(攻击随机目标),持续2回合",
|
||||||
|
"缠绕触手:束缚单个目标,使其无法行动并每回合损失10%最大生命值,持续3回合(可被驱散)",
|
||||||
|
"剧毒墨汁:对前排造成160%法术伤害,并附加中毒效果(每回合损失8%最大生命值),持续4回合",
|
||||||
|
"再生之力:每回合恢复5%最大生命值,持续5回合(可叠加)",
|
||||||
|
"星宫执法令:召唤星宫执法队虚影,对全体造成150%法术伤害",
|
||||||
|
"天星护体:为自己施加护盾,吸收800点伤害,持续3回合",
|
||||||
|
"锁灵链:封印单个目标的灵力,使其2回合内无法使用法术",
|
||||||
|
"星辰陨落:蓄力1回合,下一回合对全体造成250%法术伤害(可被中断)",
|
||||||
|
"冰火两重天:对全体造成140%混合伤害(一半冰系,一半火系)",
|
||||||
|
"形态切换:每3回合切换一次形态。冰形态下,冰系技能伤害提升50%,火系技能伤害降低50%;火形态相反",
|
||||||
|
"绝对零度(冰形态专属):冻结单个目标2回合,并造成200%冰系伤害",
|
||||||
|
"烈焰焚身(火形态专属):对随机3个目标施加灼烧,每回合损失12%最大生命值,持续3回合",
|
||||||
|
"玄阴魔气:对全体造成160%暗系法术伤害,降低目标20%攻击力,持续2回合",
|
||||||
|
"天都尸火:召唤尸火攻击单个目标,造成250%法术伤害,并附加持续灼烧",
|
||||||
|
"炼尸召唤:召唤2具结丹期炼尸助战(hp: 800, patk: 70, pdef: 30)",
|
||||||
|
"血影遁:生命低于25%时,化为血影逃跑(若成功则战斗结束,但玩家无法获得完整奖励)",
|
||||||
|
"雷霆万钧:对全体造成180%雷系法术伤害,并有概率麻痹目标(无法行动)1回合",
|
||||||
|
"风雷双翼:提升自身50%速度,持续3回合,并闪避下一次攻击",
|
||||||
|
"雷击长空:对单个目标造成300%雷系法术伤害",
|
||||||
|
"残魂不灭:死亡后若未在3回合内净化其残魂,则会复活并恢复30%最大生命值(仅触发一次)",
|
||||||
|
"心魔幻境:将玩家拉入幻境,需要连续做出3个正确选择(每个错误选择扣除当前生命值的40%)",
|
||||||
|
"幻象重生:召唤结丹期心魔幻象(属性为玩家的120%),必须击败",
|
||||||
|
"金丹雷劫:象征性天劫,每回合对玩家造成固定80点真实伤害(无视防御),持续8回合",
|
||||||
|
"问心三问:提出三个关于道心的终极问题,回答错误则全属性降低20%,持续至战斗结束",
|
||||||
|
"血煞魔掌:对前排造成180%物理伤害,并吸取造成伤害的30%恢复自身生命",
|
||||||
|
"血海滔天:对全体造成160%法术伤害,并附加流血效果(每回合损失6%最大生命值),持续3回合",
|
||||||
|
"血煞护体:生成一个护盾,吸收1000点伤害,持续3回合,护盾存在期间反弹30%受到的伤害",
|
||||||
|
"狂暴血祭:生命低于40%时,消耗自身20%当前生命值,使攻击力提升50%,持续5回合",
|
||||||
|
"星辰坠落:对全体造成170%法术伤害,并有概率使目标眩晕1回合",
|
||||||
|
"星辉护体:为自身和友方单位施加护盾,吸收800点伤害,持续3回合",
|
||||||
|
"星河剑阵:对随机4个目标造成220%法术伤害",
|
||||||
|
"星移斗转:转移一个负面状态给敌方单位,并恢复自身15%最大生命值",
|
||||||
|
"战场混乱:每回合随机触发战场事件(如箭雨、法术余波、友军支援等)",
|
||||||
|
"势力声望:根据玩家选择帮助的阵营(星宫或逆星盟),获得对应声望",
|
||||||
|
"混战求生:每回合随机受到1-3次攻击,每次造成固定200点伤害(无视部分防御)",
|
||||||
|
"风雷合击:两人同时攻击,对全体造成200%混合伤害(风雷各半)",
|
||||||
|
"风之束缚:风煞使用,降低全体敌方单位30%速度,持续3回合",
|
||||||
|
"雷之怒击:雷煞使用,对单个目标造成350%雷系伤害",
|
||||||
|
"双煞合体:生命低于50%时,两人合体成为风雷巨人,全属性提升40%,持续5回合",
|
||||||
|
"星宫秘法·叛:使用星宫秘法攻击,对星宫单位(包括玩家若为星宫阵营)造成额外50%伤害",
|
||||||
|
"逆星盟援助:召唤2名逆星盟元婴期修士助战(hp: 1000, matk: 100)",
|
||||||
|
"双重护盾:同时激活星宫和逆星盟的护盾,吸收1500点伤害",
|
||||||
|
"狡诈逃脱:生命低于30%时,试图使用传送符逃走(可被空间禁锢技能阻止)",
|
||||||
|
"落云剑诀:对前排造成190%物理伤害,并降低目标20%防御,持续2回合",
|
||||||
|
"云海翻腾:对全体造成170%法术伤害,并有概率驱散目标身上的增益状态",
|
||||||
|
"宗主威仪:提升自身及友方单位30%攻击力,持续3回合",
|
||||||
|
"云隐术:进入隐身状态,闪避下一次攻击,并在现身时对随机目标造成250%法术伤害",
|
||||||
|
"焚天煮海:对全体造成200%火系法术伤害,并附加灼烧效果(每回合损失10%最大生命值),持续3回合",
|
||||||
|
"火凤燎原:召唤火凤攻击随机3个目标,造成250%法术伤害",
|
||||||
|
"熔岩护甲:为自身施加护甲,反弹40%受到的物理伤害,持续3回合",
|
||||||
|
"怒火焚身:生命低于40%时,进入狂暴状态,攻击力提升60%,但每回合损失5%最大生命值",
|
||||||
|
"噬魂魔爪:对单个目标造成300%物理伤害,并吸取目标30%当前灵力",
|
||||||
|
"万魂哭嚎:对全体造成180%法术伤害,并有概率使目标陷入恐惧(无法行动)1回合",
|
||||||
|
"魔魂不灭:死亡后若未在3回合内净化魔魂,则会复活并恢复50%最大生命值(仅触发一次)",
|
||||||
|
"魔气滔天:每回合结束时,对全体敌方单位造成其最大生命值5%的真实伤害,持续至战斗结束",
|
||||||
|
"裂地斩:对前排造成220%物理伤害,并有概率击退目标",
|
||||||
|
"侯爷威压:降低全体敌方单位20%攻击力和防御力,持续3回合",
|
||||||
|
"召唤亲卫:召唤4名南陇侯亲卫助战(hp: 1000, patk: 120)",
|
||||||
|
"金蝉脱壳:生命低于20%时,留下一个替身傀儡,真身逃脱(若成功逃脱,则战斗结束,但玩家无法获得完整奖励)",
|
||||||
|
"问心九问:连续提出九个关于道心和过往经历的问题,每个错误回答都会导致心魔增强(全属性提升10%)",
|
||||||
|
"心魔幻象:召唤元婴期心魔幻象(属性为玩家的150%),必须击败",
|
||||||
|
"元婴雷劫:象征性天劫,每回合对玩家造成固定120点真实伤害(无视防御),持续10回合",
|
||||||
|
"执念化形:将玩家内心最深的执念化为实体进行攻击(执念实体属性根据玩家经历生成)",
|
||||||
|
"大地震裂:对全体造成220%物理伤害,并有概率使目标眩晕1回合",
|
||||||
|
"岩壁千重:为自身施加岩壁护盾,吸收2000点伤害,持续4回合,护盾存在期间免疫控制效果",
|
||||||
|
"山岳镇压:对单个目标造成350%物理伤害,并降低其50%速度,持续3回合",
|
||||||
|
"地脉连接:每回合结束时,恢复自身10%最大生命值,持续至战斗结束(可被驱散)",
|
||||||
|
"纯阳真火:对全体造成240%火系法术伤害,并驱散目标身上的所有增益状态",
|
||||||
|
"金乌降世:召唤三足金乌虚影,对随机3个目标造成300%法术伤害",
|
||||||
|
"真火护体:生成纯阳真火护罩,免疫所有负面状态,持续3回合",
|
||||||
|
"焚天煮海(终极技):蓄力2回合,第三回合对全体造成400%法术伤害(若成功打断蓄力,则至阳上人受到反噬,全属性降低30%持续3回合)",
|
||||||
|
"风火轮转:每2回合自动切换属性(风属性:提升30%速度,闪避率提高;火属性:提升30%攻击力,暴击率提高)",
|
||||||
|
"飓风烈焰:根据当前属性,对全体造成对应属性的220%法术伤害(风属性附加减速,火属性附加灼烧)",
|
||||||
|
"圣禽之翼:提升自身50%闪避率,持续2回合",
|
||||||
|
"双属性爆发:生命低于30%时,同时激活风火双重属性,全属性提升40%,但每回合损失8%最大生命值",
|
||||||
|
"阴罗鬼手:对后排造成200%法术伤害,并有概率使目标中毒(每回合损失8%最大生命值),持续3回合",
|
||||||
|
"血影分身:制造2个拥有本体30%属性的分身,分身存在期间,本体受到的伤害降低50%",
|
||||||
|
"封魂咒:封印单个目标的灵力恢复,持续4回合",
|
||||||
|
"万魂幡:召唤阴魂攻击全体,每个阴魂造成目标最大生命值3%的真实伤害(最多召唤10个阴魂)",
|
||||||
|
"化神一指:对单个目标造成其最大生命值30%的真实伤害(每场战斗限用3次)",
|
||||||
|
"万千化身:召唤3个实力不同的化身助战(hp: 2000\/1500\/1000)",
|
||||||
|
"空间禁锢:禁锢所有敌方单位1回合,使其无法行动",
|
||||||
|
"道法自然:每回合随机清除自身1-2个负面状态,并恢复10%最大生命值",
|
||||||
|
"五子同心魔:召唤五个子魔,每个子魔拥有本体20%属性,子魔存在期间,本体受到的伤害降低50%",
|
||||||
|
"阴魔斩:对单个目标造成350%法术伤害,并附加诅咒效果(降低目标30%防御,持续3回合)",
|
||||||
|
"万魔噬心:对全体造成250%法术伤害,并有概率使目标陷入混乱(攻击随机目标)1回合",
|
||||||
|
"魔魂替身:当受到致命伤害时,消耗一个子魔替死,并恢复30%最大生命值(最多触发3次)",
|
||||||
|
"风卷残云:对全体造成270%风系法术伤害,并有概率击退目标行动条",
|
||||||
|
"疾风步:提升自身100%速度,持续3回合,期间闪避率大幅提高",
|
||||||
|
"裂风刃:对随机3个目标造成330%法术伤害,无视20%法术防御",
|
||||||
|
"风之领域:展开风之领域,降低全体敌方单位30%速度,持续4回合",
|
||||||
|
"元刹魔光:对全体造成280%暗系法术伤害,并降低目标20%攻击力,持续3回合",
|
||||||
|
"魔魂分裂:分裂出3个魔魂分身,每个分身拥有本体30%属性,分身存在期间,本体免疫控制",
|
||||||
|
"噬魂魔焰:对单个目标造成400%法术伤害,并吸取目标30%当前灵力",
|
||||||
|
"古魔真身:生命低于50%时,显现部分古魔真身,全属性提升40%,但受到光系伤害增加50%",
|
||||||
|
"太一神光:对全体造成260%光系法术伤害,并驱散目标身上所有增益状态",
|
||||||
|
"乾坤剑阵:召唤剑阵困住单个目标,使其3回合内无法行动,并每回合受到180%法术伤害",
|
||||||
|
"清心咒:清除自身所有负面状态,并恢复20%最大生命值",
|
||||||
|
"天道威压:降低全体敌方单位25%全属性,持续3回合",
|
||||||
|
"元磁神光:对全体造成300%元磁伤害(无视30%防御),并有概率使目标法宝失效1回合",
|
||||||
|
"重力领域:降低全体敌方单位50%速度,持续3回合",
|
||||||
|
"双首咆哮:两个头颅同时攻击,对随机2个目标造成400%物理伤害",
|
||||||
|
"元磁护体:生成元磁护盾,吸收3000点伤害,持续3回合,护盾存在期间反弹50%受到的伤害",
|
||||||
|
"圣祖魔威:每回合开始时,对全体敌方单位施加威压,降低20%攻击力和防御力,持续至回合结束",
|
||||||
|
"万魔朝宗:召唤12只魔侍(hp: 2000, patk: 300)包围战场,魔侍存在期间,元刹圣祖免疫控制效果",
|
||||||
|
"破界魔光:对单个目标造成500%暗系法术伤害,并破坏目标身上所有护盾和增益状态",
|
||||||
|
"魔魂重生:死亡后若未在5回合内用纯阳法宝或佛门神通彻底净化,则消耗所有魔侍复活并恢复50%最大生命值",
|
||||||
|
"化神领域:展开化神领域,领域内所有友方单位全属性提升20%,敌方单位全属性降低15%",
|
||||||
|
"空间切割:对随机3个目标造成空间撕裂,造成350%法术伤害并无视30%防御",
|
||||||
|
"万法归宗:驱散全体敌方单位身上所有增益状态,并使其3回合内无法获得新增益",
|
||||||
|
"天道之剑:凝聚天道之力,对单个目标造成600%法术伤害(对魔物伤害翻倍)",
|
||||||
|
"山岳镇压:对全体造成300%土系法术伤害,并有高概率使目标眩晕1回合",
|
||||||
|
"石碑守护:为自身施加绝对守护,3回合内免疫所有伤害和控制效果",
|
||||||
|
"地脉抽取:从大地抽取灵力,恢复自身30%最大生命值,并提升50%防御力,持续3回合",
|
||||||
|
"玉石俱焚:生命低于10%时,引爆镇山石碑,对全场造成9999点真实伤害(此伤害可被特定防护法宝或神通抵挡)",
|
||||||
|
"银月狼嚎:对全体造成280%音波伤害,并有概率打断目标正在施放的技能",
|
||||||
|
"月华守护:生成月光护盾,吸收4000点伤害,持续3回合,护盾存在期间免疫精神控制",
|
||||||
|
"灵魂撕裂(双重攻击):玲珑与元刹圣祖同时攻击,对单个目标造成物理和法术混合伤害(总计450%)",
|
||||||
|
"意识争夺战:每3回合进行一次意识检定,若玩家帮助玲珑,则检定成功率提高,成功则元刹圣祖属性降低20%",
|
||||||
|
"虚空吞噬:吞噬一个敌方单位(优先选择召唤物),使其永久离场,并恢复自身20%最大生命值",
|
||||||
|
"空间扭曲:扭曲战场空间,使所有单体技能有50%概率随机选择目标",
|
||||||
|
"裂隙喷发:从身体裂隙中喷发出虚空能量,对全体造成350%法术伤害,并附加“空间侵蚀”效果(每回合损失5%最大生命值和灵力)",
|
||||||
|
"维度跃迁:每回合有30%概率闪避所有攻击,成功闪避后立即进行一次反击",
|
||||||
|
"吞噬星空:对全体造成350%法术伤害,并有概率使目标被放逐到异空间1回合(无法行动,不受伤害)",
|
||||||
|
"北冥之跃:瞬移到战场任意位置,清除自身所有负面状态,并对原位置周围造成300%物理伤害",
|
||||||
|
"空间折叠:使自身进入折叠空间,3回合内免疫所有伤害,但也无法行动",
|
||||||
|
"界空怒吼:生命低于50%时发出怒吼,对所有敌方单位造成200%真实伤害,并击退行动条",
|
||||||
|
"化神领域·展开:展开完整的化神领域,领域内敌方单位全属性降低25%,且每回合损失5%最大灵力",
|
||||||
|
"岁月之息:蕴含时间法则的攻击,对单个目标造成其已损失生命值50%的真实伤害(最大不超过9999)",
|
||||||
|
"万法皆空:驱散全体敌方单位身上所有状态(包括增益和减益),并使其3回合内无法获得任何新状态",
|
||||||
|
"临终感悟:生命低于20%时,车骑恭看破生死,停止攻击,进入传道状态(若玩家通过其最终考验,可获得完整传承)",
|
||||||
|
"金之锐:需要用水系灵力化解,失败则受到持续穿刺伤害",
|
||||||
|
"木之生:需要用金系灵力克制,失败则敌方持续恢复生命",
|
||||||
|
"水之柔:需要用土系灵力吸纳,失败则被冰冻减速",
|
||||||
|
"火之烈:需要用木系灵力助长并转化,失败则承受巨额燃烧伤害",
|
||||||
|
"土之厚:需要用木系灵力渗透,失败则被石化控制",
|
||||||
|
"完全复制:拥有玩家所有的主动技能和被动天赋,冷却时间独立计算",
|
||||||
|
"法宝镜像:召唤与玩家当前装备的主力法宝完全相同的镜像进行攻击",
|
||||||
|
"战斗本能:能预判玩家的常见操作,有概率闪避或格挡攻击",
|
||||||
|
"执念爆发:生命低于40%时,复制体陷入狂暴,攻击力提升70%,但防御力清零",
|
||||||
|
"绝对零度:冻结战场,使所有敌方单位速度降低50%,持续3回合,并有概率冰冻1回合",
|
||||||
|
"冰凤真身:显化冰凤真身,全属性提升30%,普通攻击变为范围伤害,持续5回合",
|
||||||
|
"冰封万里:对全体造成400%冰系法术伤害,并附加“深度冻结”效果(每回合损失10%最大生命值,持续3回合)",
|
||||||
|
"空间冰晶:创造空间冰晶护盾,吸收5000点伤害,并在护盾破裂时对周围造成300%冰系溅射伤害",
|
||||||
|
"混沌吸引:每回合开始时,将所有敌方单位向漩涡中心拉扯,造成其最大生命值10%的真实伤害,并降低30%速度",
|
||||||
|
"空间湮灭:对随机3个目标施放空间湮灭球,造成500%法术伤害,并永久降低目标10%最大生命值上限(可叠加)",
|
||||||
|
"乱流喷发:从漩涡中喷发出混乱的空间能量,对全体造成350%混乱伤害(属性随机),并附加随机负面状态",
|
||||||
|
"吞噬万物:吞噬一个生命值低于20%的敌方单位(优先选择召唤物、傀儡),立即恢复自身20%最大生命值",
|
||||||
|
"前三九·风火雷劫:连续九道混合属性劫雷,每道造成200%法术伤害(风、火、雷轮换),需对应属性抗性或法宝减免",
|
||||||
|
"中三九·心魔劫雷:劫雷中蕴含心魔攻击,每道劫雷后需进行一次道心检定,失败则陷入心魔幻境,持续掉血掉蓝",
|
||||||
|
"后三九·五行灭劫:金木水火土五行劫雷轮番轰击,需五行平衡或使用五行法宝化解,否则伤害递增",
|
||||||
|
"末九·混沌归一劫:最后九道为混沌劫雷,无视大部分防御和抗性,每道造成固定5000+玩家最大生命值10%的真实伤害",
|
||||||
|
"万魂哭诉:对全体造成300%精神伤害,并有高概率使目标陷入“绝望”(攻击力下降50%),持续2回合",
|
||||||
|
"怨念汲取:从所有敌方单位身上吸取怨念,每个负面状态为集合体恢复5%最大生命值",
|
||||||
|
"同归于尽:召唤上古偷渡者残影自爆,每个残影对随机目标造成其最大生命值30%的真实伤害",
|
||||||
|
"空间锚定:标记一个目标,使其无法被治疗、无法使用遁术,且受到的伤害增加50%,持续至集合体死亡"
|
||||||
|
]
|
||||||
254
src/Modules/Bag/Consume.php
Normal file
254
src/Modules/Bag/Consume.php
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Game\Modules\Bag;
|
||||||
|
|
||||||
|
use Game\Core\Colors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple representation of an equipment/consumable item.
|
||||||
|
*/
|
||||||
|
class Consume extends Item
|
||||||
|
{
|
||||||
|
|
||||||
|
private static array $typeNames = [
|
||||||
|
'heal_single' => '单体治疗',
|
||||||
|
'damage_single' => '单体伤害',
|
||||||
|
'damage_aoe' => '群体伤害',
|
||||||
|
'heal_aoe' => '群体治疗',
|
||||||
|
'support' => '辅助',
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 计算方式说明
|
||||||
|
private static array $calcTypeDescriptions = [
|
||||||
|
'matk' => '基于魔攻',
|
||||||
|
'patk' => '基于物攻',
|
||||||
|
'hp_percent' => '基于最大生命值百分比',
|
||||||
|
'hybrid' => '基于(魔攻+物攻)混合',
|
||||||
|
'crit_heal' => '暴击率影响治疗效果',
|
||||||
|
'crit_damage' => '暴击伤害系数影响伤害',
|
||||||
|
'crit_aoe' => '暴击率影响范围伤害',
|
||||||
|
'defense' => '基于防御属性',
|
||||||
|
'def_pierce' => '防御穿透伤害',
|
||||||
|
'status_bonus' => '目标状态加成伤害',
|
||||||
|
'enemy_count_bonus' => '敌人数量加成伤害',
|
||||||
|
'dispersed_damage' => '伤害分散到所有敌人',
|
||||||
|
'smart_heal' => '智能治疗(优先低血量)',
|
||||||
|
'hp_missing' => '基于缺失生命值',
|
||||||
|
'low_def_bonus' => '对低防御敌人伤害加成',
|
||||||
|
'matk_scaled' => '随敌人数量加成',
|
||||||
|
'team_sync' => '基于队伍规模',
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* 创建法术物品 - 支持新的丰富法术系统
|
||||||
|
* @param int $spellId 法术ID
|
||||||
|
* @param int $level 物品等级
|
||||||
|
* @return array 法术物品数组
|
||||||
|
*/
|
||||||
|
public static function createItem(int $spellId, int $level = 1): array
|
||||||
|
{
|
||||||
|
static $spellsData = null;
|
||||||
|
if ($spellsData === null) {
|
||||||
|
$spellsData = require __DIR__ . '/../../../src/Data/spells.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机品质
|
||||||
|
$roll = rand(1, 100);
|
||||||
|
// $roll = 100;
|
||||||
|
if ($roll <= 70) $quality = 'common';
|
||||||
|
elseif ($roll <= 90) $quality = 'rare';
|
||||||
|
elseif ($roll <= 98) $quality = 'epic';
|
||||||
|
else $quality = 'legendary';
|
||||||
|
// 查找法术信息
|
||||||
|
$spellInfo = null;
|
||||||
|
foreach ($spellsData as $category => $spells) {
|
||||||
|
if (is_array($spells) && !in_array($category, ['quality_levels', 'upgrades', 'dungeon_spell_drops', 'quality_drop_rates', 'spells_by_quality'])) {
|
||||||
|
if (isset($spells[$spellId])) {
|
||||||
|
$spellInfo = $spells[$spellId];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$spellInfo) {
|
||||||
|
// 默认法术
|
||||||
|
return [
|
||||||
|
'id' => uniqid('spell_'),
|
||||||
|
'type' => 'spell',
|
||||||
|
'name' => '未知法术',
|
||||||
|
'quality' => $quality,
|
||||||
|
'level' => $level,
|
||||||
|
'spellId' => $spellId,
|
||||||
|
'enhanceLevel' => 0,
|
||||||
|
'calc_type' => 'matk',
|
||||||
|
'cost' => 20,
|
||||||
|
'spellType' => 'damage_single',
|
||||||
|
'desc' => '未知的法术',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 品质映射到数组索引 (common=0, rare=1, epic=2, legendary=3)
|
||||||
|
$qualityIndex = match ($quality) {
|
||||||
|
'common' => 0,
|
||||||
|
'rare' => 1,
|
||||||
|
'epic' => 2,
|
||||||
|
'legendary' => 3,
|
||||||
|
default => 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 提取品质相关的参数
|
||||||
|
$healRatio = $spellInfo['heal_ratio'][$qualityIndex] ?? ($spellInfo['heal_ratio'][0] ?? 0);
|
||||||
|
$damageRatio = $spellInfo['damage_ratio'][$qualityIndex] ?? ($spellInfo['damage_ratio'][0] ?? 1.0);
|
||||||
|
$healBase = $spellInfo['heal_base'][$qualityIndex] ?? ($spellInfo['heal_base'][0] ?? 0);
|
||||||
|
$critBonus = $spellInfo['crit_bonus'][$qualityIndex] ?? ($spellInfo['crit_bonus'][0] ?? 0);
|
||||||
|
$critDmgBonus = $spellInfo['crit_dmg_bonus'][$qualityIndex] ?? ($spellInfo['crit_dmg_bonus'][0] ?? 0);
|
||||||
|
$enemyCountBonus = $spellInfo['enemy_count_bonus'][$qualityIndex] ?? ($spellInfo['enemy_count_bonus'][0] ?? 0);
|
||||||
|
$dispersion = $spellInfo['dispersion'][$qualityIndex] ?? ($spellInfo['dispersion'][0] ?? 1.0);
|
||||||
|
$teamBonus = $spellInfo['team_bonus'][$qualityIndex] ?? ($spellInfo['team_bonus'][0] ?? 0);
|
||||||
|
$priorityBonus = $spellInfo['priority_bonus'][$qualityIndex] ?? ($spellInfo['priority_bonus'][0] ?? 0);
|
||||||
|
|
||||||
|
// 计算基础伤害值(根据法术模板的基础值和成长系数)
|
||||||
|
$baseValue = 0;
|
||||||
|
$growth = 0;
|
||||||
|
if (isset($spellInfo['base']) && isset($spellInfo['growth'])) {
|
||||||
|
$baseArray = $spellInfo['base'];
|
||||||
|
|
||||||
|
$growthArray = $spellInfo['growth'];
|
||||||
|
|
||||||
|
// 确保索引在范围内
|
||||||
|
if (is_array($baseArray) && is_array($growthArray)) {
|
||||||
|
$baseValue = $baseArray[$qualityIndex] ?? ($baseArray[0] ?? 0);
|
||||||
|
$growth = $growthArray[$qualityIndex] ?? ($growthArray[0] ?? 0);
|
||||||
|
|
||||||
|
// 应用计算公式:finalValue = baseValue + (level * growth) + randomBonus
|
||||||
|
$randomBonus = rand(0, max(1, (int)($baseValue * 3)));
|
||||||
|
$finalBaseValue = (int)($baseValue + ($level * $growth * 10) + $randomBonus);
|
||||||
|
} else {
|
||||||
|
$finalBaseValue = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$finalBaseValue = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => uniqid('spell_'),
|
||||||
|
'type' => 'spell',
|
||||||
|
'name' => $spellInfo['name'],
|
||||||
|
'quality' => $quality,
|
||||||
|
'level' => $level,
|
||||||
|
'spellId' => $spellId,
|
||||||
|
'enhanceLevel' => 0,
|
||||||
|
'calc_type' => $spellInfo['calc_type'] ?? 'matk',
|
||||||
|
'cost' => $spellInfo['cost'] ?? 20,
|
||||||
|
'spellType' => $spellInfo['type'] ?? 'damage_single',
|
||||||
|
'desc' => $spellInfo['desc'] ?? '',
|
||||||
|
|
||||||
|
// 品质参数
|
||||||
|
'heal_ratio' => $healRatio,
|
||||||
|
'damage_ratio' => $damageRatio,
|
||||||
|
'heal_base' => $healBase,
|
||||||
|
'crit_bonus' => $critBonus,
|
||||||
|
'crit_dmg_bonus' => $critDmgBonus,
|
||||||
|
'enemy_count_bonus' => $enemyCountBonus,
|
||||||
|
'dispersion' => $dispersion,
|
||||||
|
'team_bonus' => $teamBonus,
|
||||||
|
'priority_bonus' => $priorityBonus,
|
||||||
|
// 基础伤害值(已计算)
|
||||||
|
'base' => $finalBaseValue,
|
||||||
|
'growth' => $growth,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function calculateSellPrice(array $item): int{
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getLineShow($item): string
|
||||||
|
{
|
||||||
|
$parts = [];
|
||||||
|
$spell = $item;
|
||||||
|
// 名称(带品质颜色和强化)
|
||||||
|
$parts[] = self::formatName($spell);
|
||||||
|
|
||||||
|
// 法术类型
|
||||||
|
$spellType = $spell['spellType'] ?? $spell['type'] ?? 'unknown';
|
||||||
|
$typeName = self::$typeNames[$spellType];
|
||||||
|
$parts[] = Colors::GRAY . "[{$typeName}]" . Colors::RESET;
|
||||||
|
|
||||||
|
// 计算方式
|
||||||
|
$calcType = $spell['calc_type'] ?? 'matk';
|
||||||
|
$calcDesc = self::getCalcTypeDescription($calcType);
|
||||||
|
$ratio = $spell['damage_ratio'];
|
||||||
|
$parts[] = Colors::GRAY . "{$calcDesc} x $ratio" . Colors::RESET;
|
||||||
|
// 基础值
|
||||||
|
$base = $spell['base'] ?? 0;
|
||||||
|
$parts[] = Colors::YELLOW . "基础:{$base}" . Colors::RESET;
|
||||||
|
|
||||||
|
// 消耗
|
||||||
|
$cost = $spell['cost'] ?? 0;
|
||||||
|
$enhanceLevel = $spell['enhanceLevel'] ?? 0;
|
||||||
|
$actualCost = max(1, $cost - ($enhanceLevel * 2));
|
||||||
|
if ($enhanceLevel > 0) {
|
||||||
|
$parts[] = Colors::CYAN . "消耗:{$actualCost}(原:{$cost})" . Colors::RESET;
|
||||||
|
} else {
|
||||||
|
$parts[] = Colors::CYAN . "消耗:{$cost}" . Colors::RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(" ", $parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getCalcTypeDescription(string $calcType): string
|
||||||
|
{
|
||||||
|
return self::$calcTypeDescriptions[$calcType] ?? $calcType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function formatName(array $spell): string
|
||||||
|
{
|
||||||
|
$quality = $spell['quality'] ?? 'common';
|
||||||
|
$color = Colors::getColor($quality);
|
||||||
|
$name = $spell['name'] ?? '未知法术';
|
||||||
|
$level = $spell['level'] ?? 1;
|
||||||
|
|
||||||
|
$enhanceLevel = $spell['enhanceLevel'] ?? 0;
|
||||||
|
$enhanceStr = $enhanceLevel > 0 ? Colors::YELLOW . "+{$enhanceLevel}" . Colors::RESET : "";
|
||||||
|
|
||||||
|
return $color . $name . Colors::RESET . " Lv.{$level}" . $enhanceStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDetailShow($item): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static function getBaseValue(string $quality, string $type, int $level,bool $isMain = true): float
|
||||||
|
{
|
||||||
|
$qualityMultiplier = match ($quality) {
|
||||||
|
'legendary' => 2.0,
|
||||||
|
'epic' => 1.5,
|
||||||
|
'rare' => 1.2,
|
||||||
|
default => 1.0
|
||||||
|
};
|
||||||
|
|
||||||
|
$base = rand(2*$level, 8*$level);
|
||||||
|
$multiplier = match ($type) {
|
||||||
|
'heal_single' => [1.5, 2],
|
||||||
|
'damage_single' => [1.5, 2],
|
||||||
|
'damage_aoe' => [1.0, 1.3],
|
||||||
|
'heal_aoe' => [1.0, 1.3],
|
||||||
|
default => [1, 1.1]
|
||||||
|
};
|
||||||
|
$random = random_int(1, 10);
|
||||||
|
$multiplier = ($multiplier[1] - $multiplier[0]) * $random / 10 + $multiplier[0];
|
||||||
|
if ($isMain){
|
||||||
|
return floor(($base + ($level * $multiplier)) * $qualityMultiplier);
|
||||||
|
}else{
|
||||||
|
return floor(($base + ($level * $multiplier)) * $qualityMultiplier / 5 * 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,4 +18,30 @@ abstract class Item
|
||||||
public abstract static function getDetailShow($item): array;
|
public abstract static function getDetailShow($item): array;
|
||||||
|
|
||||||
public static abstract function calculateSellPrice(array $item): int;
|
public static abstract function calculateSellPrice(array $item): int;
|
||||||
|
|
||||||
|
public static function show($item): string
|
||||||
|
{
|
||||||
|
if ($item['type'] == 'spell'){
|
||||||
|
return Spell::getLineShow($item);
|
||||||
|
}elseif($item['type'] == 'quest'){
|
||||||
|
return Quest::getLineShow($item);
|
||||||
|
}elseif ($item['type'] == 'consume'){
|
||||||
|
return Consume::getLineShow($item);
|
||||||
|
}else{
|
||||||
|
return Equipment::getLineShow($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static function calcPrice($item): int
|
||||||
|
{
|
||||||
|
if ($item['type'] == 'spell'){
|
||||||
|
return Spell::calculateSellPrice($item);
|
||||||
|
}elseif($item['type'] == 'quest'){
|
||||||
|
return Quest::calculateSellPrice($item);
|
||||||
|
}elseif ($item['type'] == 'consume'){
|
||||||
|
return Consume::calculateSellPrice($item);
|
||||||
|
}else{
|
||||||
|
return Equipment::calculateSellPrice($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,12 @@ class Quest extends Item
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLineShow(): string
|
public static function getLineShow($item): string
|
||||||
{
|
{
|
||||||
// TODO: Implement getLineShow() method.
|
// TODO: Implement getLineShow() method.
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDetailShow(): array
|
public static function getDetailShow($item): array
|
||||||
{
|
{
|
||||||
// TODO: Implement getDetailShow() method.
|
// TODO: Implement getDetailShow() method.
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use Game\Core\Game;
|
||||||
use Game\Core\ItemDisplay;
|
use Game\Core\ItemDisplay;
|
||||||
use Game\Core\Screen;
|
use Game\Core\Screen;
|
||||||
use Game\Core\Colors;
|
use Game\Core\Colors;
|
||||||
|
use Game\Modules\Bag\Item;
|
||||||
use Game\Services\EquipmentEnhancer;
|
use Game\Services\EquipmentEnhancer;
|
||||||
use Game\Entities\Partner;
|
use Game\Entities\Partner;
|
||||||
|
|
||||||
|
|
@ -114,7 +115,7 @@ class EquipmentEnhancePanel
|
||||||
$item = $player->equip[$slot] ?? null;
|
$item = $player->equip[$slot] ?? null;
|
||||||
|
|
||||||
if ($item) {
|
if ($item) {
|
||||||
$str = ItemDisplay::renderListItem($item);
|
$str = Item::show($item);
|
||||||
$out->writeln("{$this->cyan}║[$slotIndex]$str");
|
$out->writeln("{$this->cyan}║[$slotIndex]$str");
|
||||||
$equipped[$slotIndex] = ['slot' => $slot, 'item' => $item];
|
$equipped[$slotIndex] = ['slot' => $slot, 'item' => $item];
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -132,7 +133,7 @@ class EquipmentEnhancePanel
|
||||||
|
|
||||||
foreach ($player->inventory as $invIndex => $item) {
|
foreach ($player->inventory as $invIndex => $item) {
|
||||||
if (in_array($item['type'] ?? '', ['weapon', 'armor', 'boots', 'ring', 'necklace'])) {
|
if (in_array($item['type'] ?? '', ['weapon', 'armor', 'boots', 'ring', 'necklace'])) {
|
||||||
$str = ItemDisplay::renderListItem($item);
|
$str = Item::show($item);
|
||||||
$out->writeln("{$this->cyan}║[$slotIndex]$str");
|
$out->writeln("{$this->cyan}║[$slotIndex]$str");
|
||||||
$inventoryItems[$slotIndex] = ['invIndex' => $invIndex, 'item' => $item];
|
$inventoryItems[$slotIndex] = ['invIndex' => $invIndex, 'item' => $item];
|
||||||
$slotIndex++;
|
$slotIndex++;
|
||||||
|
|
@ -224,7 +225,7 @@ class EquipmentEnhancePanel
|
||||||
$item = $partner->equip[$slot] ?? null;
|
$item = $partner->equip[$slot] ?? null;
|
||||||
|
|
||||||
if ($item) {
|
if ($item) {
|
||||||
$str = ItemDisplay::renderListItem($item);
|
$str = Item::show($item);
|
||||||
$out->writeln("{$this->cyan}║[$slotIndex]$str");
|
$out->writeln("{$this->cyan}║[$slotIndex]$str");
|
||||||
$equipped[$slotIndex] = ['slot' => $slot, 'item' => $item];
|
$equipped[$slotIndex] = ['slot' => $slot, 'item' => $item];
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use Game\Core\ItemDisplay;
|
||||||
use Game\Core\SpellDisplay;
|
use Game\Core\SpellDisplay;
|
||||||
use Game\Core\Colors;
|
use Game\Core\Colors;
|
||||||
use Game\Modules\Bag\Equipment;
|
use Game\Modules\Bag\Equipment;
|
||||||
|
use Game\Modules\Bag\Item;
|
||||||
|
|
||||||
class InventoryPanel
|
class InventoryPanel
|
||||||
{
|
{
|
||||||
|
|
@ -99,7 +100,7 @@ class InventoryPanel
|
||||||
if ($item['type'] == 'quest_item'){
|
if ($item['type'] == 'quest_item'){
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
$displayStr = Equipment::getLineShow($item);
|
$displayStr = Item::show($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
$out->writeln("[{$index}] {$displayStr}");
|
$out->writeln("[{$index}] {$displayStr}");
|
||||||
|
|
@ -153,7 +154,7 @@ class InventoryPanel
|
||||||
$out->writeln("╔════════════════════════════════════════╗");
|
$out->writeln("╔════════════════════════════════════════╗");
|
||||||
|
|
||||||
// 使用统一的详细显示
|
// 使用统一的详细显示
|
||||||
$detailLines = Equipment::getLineShow($item);
|
$detailLines = Item::show($item);
|
||||||
$out->writeln($detailLines);
|
$out->writeln($detailLines);
|
||||||
if ($quantity > 1) {
|
if ($quantity > 1) {
|
||||||
$out->writeln("║");
|
$out->writeln("║");
|
||||||
|
|
@ -305,37 +306,13 @@ class InventoryPanel
|
||||||
|
|
||||||
$slot = $item['type'];
|
$slot = $item['type'];
|
||||||
|
|
||||||
// 获取新装备原有的强化等级
|
|
||||||
$newItemEnhanceLevel = $item['enhanceLevel'] ?? 0;
|
|
||||||
|
|
||||||
// $newItemEnhanceLevel = 0;
|
|
||||||
|
|
||||||
// If there's already an item in the slot, swap enhance levels
|
|
||||||
// 调整为不能继承强化等级
|
|
||||||
// if (isset($player->equip[$slot]) && !empty($player->equip[$slot])) {
|
|
||||||
// $oldItem = $player->equip[$slot];
|
|
||||||
// $oldEnhanceLevel = $oldItem['enhanceLevel'] ?? 0;
|
|
||||||
//
|
|
||||||
// // 旧装备继承新装备的强化等级
|
|
||||||
// $oldItem['enhanceLevel'] = $newItemEnhanceLevel;
|
|
||||||
// $player->addItem($oldItem);
|
|
||||||
//
|
|
||||||
// $oldEnhanceStr = $oldEnhanceLevel > 0 ? "+{$oldEnhanceLevel}" : "";
|
|
||||||
// $newEnhanceStr = $newItemEnhanceLevel > 0 ? "+{$newItemEnhanceLevel}" : "";
|
|
||||||
// $out->writeln("已取下 {$oldItem['name']}{$newEnhanceStr} 并放入背包");
|
|
||||||
//
|
|
||||||
// // 新装备继承旧装备的强化等级
|
|
||||||
// $item['enhanceLevel'] = $oldEnhanceLevel;
|
|
||||||
// }
|
|
||||||
|
|
||||||
$player->equip[$slot] = $item;
|
$player->equip[$slot] = $item;
|
||||||
|
|
||||||
// Remove from inventory
|
// Remove from inventory
|
||||||
unset($player->inventory[$index]);
|
unset($player->inventory[$index]);
|
||||||
$player->inventory = array_values($player->inventory);
|
$player->inventory = array_values($player->inventory);
|
||||||
|
|
||||||
$enhanceStr = ($item['enhanceLevel'] ?? 0) > 0 ? "+{$item['enhanceLevel']}" : "";
|
$out->writeln("你装备了:{$item['name']}");
|
||||||
$out->writeln("你装备了:{$item['name']}{$enhanceStr}");
|
|
||||||
Screen::sleep(1);
|
Screen::sleep(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,7 +339,7 @@ class InventoryPanel
|
||||||
$player = $this->game->player;
|
$player = $this->game->player;
|
||||||
|
|
||||||
// 计算售价
|
// 计算售价
|
||||||
$sellPrice = \Game\Entities\Item::calculateSellPrice($item);
|
$sellPrice = Item::calcPrice($item);
|
||||||
|
|
||||||
// 消耗品可能有多个,卖一个
|
// 消耗品可能有多个,卖一个
|
||||||
$quantity = $item['quantity'] ?? 1;
|
$quantity = $item['quantity'] ?? 1;
|
||||||
|
|
@ -545,7 +522,7 @@ class InventoryPanel
|
||||||
$isEquipment = in_array($item['type'], ['weapon', 'armor', 'ring', 'necklace', 'boots','spell']);
|
$isEquipment = in_array($item['type'], ['weapon', 'armor', 'ring', 'necklace', 'boots','spell']);
|
||||||
|
|
||||||
if ($isEquipment && in_array($quality, $qualitiesToSell)) {
|
if ($isEquipment && in_array($quality, $qualitiesToSell)) {
|
||||||
$price = \Game\Entities\Item::calculateSellPrice($item);
|
$price = Item::calcPrice($item);
|
||||||
$itemsToSell[] = [
|
$itemsToSell[] = [
|
||||||
'index' => $index,
|
'index' => $index,
|
||||||
'item' => $item,
|
'item' => $item,
|
||||||
|
|
@ -727,7 +704,7 @@ class InventoryPanel
|
||||||
foreach ($slots as $slot => $slotName) {
|
foreach ($slots as $slot => $slotName) {
|
||||||
$item = $player->equip[$slot] ?? null;
|
$item = $player->equip[$slot] ?? null;
|
||||||
if ($item){
|
if ($item){
|
||||||
$slotLines = Equipment::getLineShow($item);
|
$slotLines = Item::show($item);
|
||||||
$out->writeln($slotLines);
|
$out->writeln($slotLines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use Game\Core\Input;
|
||||||
use Game\Core\ItemDisplay;
|
use Game\Core\ItemDisplay;
|
||||||
use Game\Core\SpellDisplay;
|
use Game\Core\SpellDisplay;
|
||||||
use Game\Modules\Bag\Equipment;
|
use Game\Modules\Bag\Equipment;
|
||||||
|
use Game\Modules\Bag\Item;
|
||||||
use Game\Services\EquipmentEnhancer;
|
use Game\Services\EquipmentEnhancer;
|
||||||
use Game\Entities\Actor;
|
use Game\Entities\Actor;
|
||||||
use Game\Entities\Partner;
|
use Game\Entities\Partner;
|
||||||
|
|
@ -230,8 +231,14 @@ class StatsPanel
|
||||||
$slotName = $slotNames[$slot];
|
$slotName = $slotNames[$slot];
|
||||||
|
|
||||||
if ($item) {
|
if ($item) {
|
||||||
$slotLines = Equipment::getLineShow($item);
|
$slotLines = ItemDisplay::renderSlot($slotName, $item, "║ [{$slotIndex}] ");
|
||||||
$out->writeln($slotLines);
|
foreach ($slotLines as $i => $line) {
|
||||||
|
if ($i === 0) {
|
||||||
|
$out->writeln($line);
|
||||||
|
} else {
|
||||||
|
$out->writeln("║ " . substr($line, 6)); // 缩进对齐
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$out->writeln("║ [{$slotIndex}] {$this->cyan}{$slotName}{$this->reset}: " .
|
$out->writeln("║ [{$slotIndex}] {$this->cyan}{$slotName}{$this->reset}: " .
|
||||||
Colors::GRAY . '(空)' . Colors::RESET);
|
Colors::GRAY . '(空)' . Colors::RESET);
|
||||||
|
|
@ -350,7 +357,7 @@ class StatsPanel
|
||||||
foreach ($slots as $slotKey => $slotName) {
|
foreach ($slots as $slotKey => $slotName) {
|
||||||
$currentItem = $actor->equip[$slotKey] ?? null;
|
$currentItem = $actor->equip[$slotKey] ?? null;
|
||||||
if ($currentItem) {
|
if ($currentItem) {
|
||||||
$this->game->output->writeln("[{$slotIdx}] {$slotName}: " . Equipment::getLineShow($currentItem));
|
$this->game->output->writeln("[{$slotIdx}] {$slotName}: " . Item::show($currentItem));
|
||||||
} else {
|
} else {
|
||||||
$this->game->output->writeln("[{$slotIdx}] {$slotName}: (空)");
|
$this->game->output->writeln("[{$slotIdx}] {$slotName}: (空)");
|
||||||
}
|
}
|
||||||
|
|
@ -392,7 +399,7 @@ class StatsPanel
|
||||||
$displayIdx = 1;
|
$displayIdx = 1;
|
||||||
$idxMap = [];
|
$idxMap = [];
|
||||||
foreach ($equipableItems as $realIdx => $item) {
|
foreach ($equipableItems as $realIdx => $item) {
|
||||||
$displayStr = Equipment::getLineShow($item);
|
$displayStr = Item::show($equipableItems);
|
||||||
$this->game->output->writeln("[{$displayIdx}] {$displayStr}");
|
$this->game->output->writeln("[{$displayIdx}] {$displayStr}");
|
||||||
$idxMap[$displayIdx] = $realIdx;
|
$idxMap[$displayIdx] = $realIdx;
|
||||||
$displayIdx++;
|
$displayIdx++;
|
||||||
|
|
@ -446,7 +453,7 @@ class StatsPanel
|
||||||
foreach ($slots as $slot => $name) {
|
foreach ($slots as $slot => $name) {
|
||||||
if (!empty($actor->equip[$slot])) {
|
if (!empty($actor->equip[$slot])) {
|
||||||
$item = $actor->equip[$slot];
|
$item = $actor->equip[$slot];
|
||||||
$this->game->output->writeln("[{$idx}] {$name}: " . Equipment::getLineShow($item));
|
$this->game->output->writeln("[{$idx}] {$name}: " . Item::show($item));
|
||||||
$equipped[$idx] = $slot;
|
$equipped[$idx] = $slot;
|
||||||
$idx++;
|
$idx++;
|
||||||
}
|
}
|
||||||
|
|
@ -533,7 +540,7 @@ class StatsPanel
|
||||||
$enhanceLevel = $item['enhanceLevel'] ?? 0;
|
$enhanceLevel = $item['enhanceLevel'] ?? 0;
|
||||||
$bonusPercent = $enhanceLevel > 0 ? $actor->getEnhanceBonus($enhanceLevel) : '0';
|
$bonusPercent = $enhanceLevel > 0 ? $actor->getEnhanceBonus($enhanceLevel) : '0';
|
||||||
$bonusText = $enhanceLevel > 0 ? " {$this->green}(+{$bonusPercent}%){$this->reset}" : '';
|
$bonusText = $enhanceLevel > 0 ? " {$this->green}(+{$bonusPercent}%){$this->reset}" : '';
|
||||||
$this->game->output->writeln("[{$idx}] {$name}: " . ItemDisplay::renderListItem($item) . " {$this->yellow}+{$enhanceLevel}{$this->reset}{$bonusText}");
|
$this->game->output->writeln("[{$idx}] {$name}: " . Item::show($item) . " {$this->yellow}+{$enhanceLevel}{$this->reset}{$bonusText}");
|
||||||
$equipped[$idx] = $slot;
|
$equipped[$idx] = $slot;
|
||||||
$idx++;
|
$idx++;
|
||||||
}
|
}
|
||||||
|
|
@ -590,7 +597,7 @@ class StatsPanel
|
||||||
$out->writeln("{$this->cyan} 装 备 强 化 等 级 选 择{$this->reset}");
|
$out->writeln("{$this->cyan} 装 备 强 化 等 级 选 择{$this->reset}");
|
||||||
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
$out->writeln("装备: " . ItemDisplay::renderListItem($item));
|
$out->writeln("装备: " . Item::show($item));
|
||||||
$currentBonus = $enhanceLevel > 0 ? $actor->getEnhanceBonus($enhanceLevel) : '0';
|
$currentBonus = $enhanceLevel > 0 ? $actor->getEnhanceBonus($enhanceLevel) : '0';
|
||||||
$out->writeln("当前等级: {$this->yellow}+{$enhanceLevel}{$this->reset} {$this->green}(属性提升: +{$currentBonus}%){$this->reset}");
|
$out->writeln("当前等级: {$this->yellow}+{$enhanceLevel}{$this->reset} {$this->green}(属性提升: +{$currentBonus}%){$this->reset}");
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
|
|
@ -698,7 +705,7 @@ class StatsPanel
|
||||||
$out->writeln("{$this->cyan} 自 动 强 化 中{$this->reset}");
|
$out->writeln("{$this->cyan} 自 动 强 化 中{$this->reset}");
|
||||||
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
$out->writeln("装备: " . ItemDisplay::renderListItem($item));
|
$out->writeln("装备: " . Item::show($item));
|
||||||
$out->writeln("目标: +{$targetLevel}");
|
$out->writeln("目标: +{$targetLevel}");
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
|
|
||||||
|
|
@ -854,7 +861,7 @@ class StatsPanel
|
||||||
$spellIndices = [];
|
$spellIndices = [];
|
||||||
$i = 1;
|
$i = 1;
|
||||||
foreach ($availableSpells as $index => $item) {
|
foreach ($availableSpells as $index => $item) {
|
||||||
$out->writeln("[{$i}] " . ItemDisplay::renderListItem($item));
|
$out->writeln("[{$i}] " . Item::show($item));
|
||||||
$spellIndices[$i] = $index;
|
$spellIndices[$i] = $index;
|
||||||
$i++;
|
$i++;
|
||||||
}
|
}
|
||||||
|
|
@ -876,7 +883,7 @@ class StatsPanel
|
||||||
foreach ($skillSlots as $slot) {
|
foreach ($skillSlots as $slot) {
|
||||||
$currentSpell = $actor->skillSlots[$slot] ?? null;
|
$currentSpell = $actor->skillSlots[$slot] ?? null;
|
||||||
if ($currentSpell) {
|
if ($currentSpell) {
|
||||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::renderListItem($currentSpell));
|
$out->writeln("[{$i}] 技能{$i}: " . Item::show($currentSpell));
|
||||||
} else {
|
} else {
|
||||||
$out->writeln("[{$i}] 技能{$i}: (空)");
|
$out->writeln("[{$i}] 技能{$i}: (空)");
|
||||||
}
|
}
|
||||||
|
|
@ -924,7 +931,7 @@ class StatsPanel
|
||||||
foreach ($skillSlots as $slot) {
|
foreach ($skillSlots as $slot) {
|
||||||
$currentSpell = $actor->skillSlots[$slot] ?? null;
|
$currentSpell = $actor->skillSlots[$slot] ?? null;
|
||||||
if ($currentSpell) {
|
if ($currentSpell) {
|
||||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::renderListItem($currentSpell));
|
$out->writeln("[{$i}] 技能{$i}: " . Item::show($currentSpell));
|
||||||
$hasSkill = true;
|
$hasSkill = true;
|
||||||
} else {
|
} else {
|
||||||
$out->writeln("[{$i}] 技能{$i}: (空)");
|
$out->writeln("[{$i}] 技能{$i}: (空)");
|
||||||
|
|
@ -1023,7 +1030,7 @@ class StatsPanel
|
||||||
$slotNum = array_search($slotName, $skillSlots) + 1;
|
$slotNum = array_search($slotName, $skillSlots) + 1;
|
||||||
$spell = $skillInfo['spell'];
|
$spell = $skillInfo['spell'];
|
||||||
$enhanceLevel = $spell['enhanceLevel'] ?? 0;
|
$enhanceLevel = $spell['enhanceLevel'] ?? 0;
|
||||||
$out->writeln("[{$i}] 技能{$slotNum}: " . ItemDisplay::renderListItem($spell) . " {$this->yellow}+{$enhanceLevel}{$this->reset}");
|
$out->writeln("[{$i}] 技能{$slotNum}: " . Item::show($spell) . " {$this->yellow}+{$enhanceLevel}{$this->reset}");
|
||||||
$skillIndices[$i] = $slotName;
|
$skillIndices[$i] = $slotName;
|
||||||
$i++;
|
$i++;
|
||||||
}
|
}
|
||||||
|
|
@ -1064,7 +1071,7 @@ class StatsPanel
|
||||||
$out->writeln("{$this->cyan} 技 能 强 化 等 级 选 择{$this->reset}");
|
$out->writeln("{$this->cyan} 技 能 强 化 等 级 选 择{$this->reset}");
|
||||||
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
$out->writeln("技能: " . ItemDisplay::renderListItem($item));
|
$out->writeln("技能: " . Item::show($item));
|
||||||
$currentBonus = $enhanceLevel > 0 ? $actor->getEnhanceBonus($enhanceLevel) : '0';
|
$currentBonus = $enhanceLevel > 0 ? $actor->getEnhanceBonus($enhanceLevel) : '0';
|
||||||
$out->writeln("当前等级: {$this->yellow}+{$enhanceLevel}{$this->reset} {$this->green}(属性提升: +{$currentBonus}%){$this->reset}");
|
$out->writeln("当前等级: {$this->yellow}+{$enhanceLevel}{$this->reset} {$this->green}(属性提升: +{$currentBonus}%){$this->reset}");
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
|
|
@ -1168,7 +1175,7 @@ class StatsPanel
|
||||||
$out->writeln("{$this->cyan} 自 动 强 化 中{$this->reset}");
|
$out->writeln("{$this->cyan} 自 动 强 化 中{$this->reset}");
|
||||||
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
$out->writeln("技能: " . ItemDisplay::renderListItem($item));
|
$out->writeln("技能: " . Item::show($item));
|
||||||
$out->writeln("目标: +{$targetLevel}");
|
$out->writeln("目标: +{$targetLevel}");
|
||||||
$out->writeln("");
|
$out->writeln("");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
# 凡人修仙传 - Web 服务器启动脚本
|
# 凡人修仙传 - Web 服务器启动脚本
|
||||||
|
|
||||||
PORT=${1:-65534}
|
PORT=${1:-65534}
|
||||||
HOST=${2:-0.0.0.0}
|
HOST=${2:-192.168.89.112}
|
||||||
|
|
||||||
echo "=================================="
|
echo "=================================="
|
||||||
echo " 凡人修仙传 - 文字版"
|
echo " 凡人修仙传 - 文字版"
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,27 @@
|
||||||
|
|
||||||
use Game\Modules\Bag\Equipment;
|
use Game\Modules\Bag\Equipment;
|
||||||
use Game\Modules\Bag\Spell;
|
use Game\Modules\Bag\Spell;
|
||||||
|
use Game\Modules\Skill\Enums\DamageType;
|
||||||
|
use Game\Modules\Skill\Factories\EffectFactory;
|
||||||
|
|
||||||
require __DIR__ . '/../vendor/autoload.php';
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
$equ = Equipment::createItem('armor',10);
|
|
||||||
$res = Equipment::getLineShow($equ);
|
// 效果使用示例
|
||||||
echo $res;
|
|
||||||
$res = Spell::createItem(2,level: 100);
|
// 创建伤害效果
|
||||||
echo Spell::getLineShow($res);
|
$damageEffect = EffectFactory::createEffect([
|
||||||
dd(2);
|
'type' => 'damage',
|
||||||
|
'id' => 'fire_ball_damage',
|
||||||
|
'name' => '火球术伤害',
|
||||||
|
'damage_type' => DamageType::FIRE->value,
|
||||||
|
'value' => 150, // 150% 攻击力伤害
|
||||||
|
'is_percentage' => true,
|
||||||
|
'crit_chance' => 0.1,
|
||||||
|
'crit_multiplier' => 2.0,
|
||||||
|
'duration' => 0 // 立即效果
|
||||||
|
]);
|
||||||
|
dd($damageEffect);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
228
web/server.php
228
web/server.php
|
|
@ -4,23 +4,9 @@
|
||||||
* 使用方法: php -S 0.0.0.0:8080 web/server.php
|
* 使用方法: php -S 0.0.0.0:8080 web/server.php
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 设置错误报告
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
ini_set('display_errors', 0);
|
|
||||||
|
|
||||||
// 自动加载
|
// 自动加载
|
||||||
require_once __DIR__ . '/../vendor/autoload.php';
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
use Game\Core\UserManager;
|
|
||||||
use Game\Core\GameSession;
|
|
||||||
use Game\Core\WebInput;
|
|
||||||
|
|
||||||
// 设置 Web 模式
|
|
||||||
WebInput::getInstance()->setWebMode(true);
|
|
||||||
|
|
||||||
// 启动会话
|
|
||||||
session_start();
|
|
||||||
|
|
||||||
// 设置响应头
|
// 设置响应头
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
header('Access-Control-Allow-Origin: *');
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
|
@ -40,203 +26,29 @@ $path = parse_url($requestUri, PHP_URL_PATH);
|
||||||
// 静态文件处理
|
// 静态文件处理
|
||||||
if ($path === '/' || $path === '/process.html') {
|
if ($path === '/' || $path === '/process.html') {
|
||||||
header('Content-Type: text/html; charset=utf-8');
|
header('Content-Type: text/html; charset=utf-8');
|
||||||
readfile(__DIR__ . '/process.html');
|
echo '<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>凡人修仙传 - Web Terminal Edition</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: monospace; background: #000; color: #0f0; margin: 0; padding: 20px; }
|
||||||
|
#game { height: 600px; overflow-y: auto; border: 1px solid #0f0; padding: 10px; margin-bottom: 10px; }
|
||||||
|
input { background: #000; color: #0f0; border: 1px solid #0f0; padding: 5px; width: 100%; box-sizing: border-box; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="game">游戏初始化中...</div>
|
||||||
|
<input type="text" id="input" placeholder="输入命令..." autofocus>
|
||||||
|
<script>
|
||||||
|
console.log("Web game interface loaded");
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>';
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// API 路由
|
// API 路由
|
||||||
$response = ['success' => false, 'message' => '未知请求'];
|
$response = ['success' => true, 'message' => 'Web server is running. This is a CLI-based game.'];
|
||||||
|
|
||||||
try {
|
|
||||||
switch ($path) {
|
|
||||||
case '/api/register':
|
|
||||||
$response = handleRegister();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '/api/login':
|
|
||||||
$response = handleLogin();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '/api/logout':
|
|
||||||
$response = handleLogout();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '/api/status':
|
|
||||||
$response = handleStatus();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '/api/game/render':
|
|
||||||
$response = handleGameRender();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '/api/game/input':
|
|
||||||
$response = handleGameInput();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '/api/game/battle-stream':
|
|
||||||
// SSE 实时战斗流
|
|
||||||
handleBattleStream();
|
|
||||||
exit;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// 检查是否是静态文件
|
|
||||||
$filePath = __DIR__ . $path;
|
|
||||||
if (file_exists($filePath) && is_file($filePath)) {
|
|
||||||
$ext = pathinfo($path, PATHINFO_EXTENSION);
|
|
||||||
$mimeTypes = [
|
|
||||||
'js' => 'application/javascript',
|
|
||||||
'css' => 'text/css',
|
|
||||||
'html' => 'text/html',
|
|
||||||
'png' => 'image/png',
|
|
||||||
'jpg' => 'image/jpeg',
|
|
||||||
'gif' => 'image/gif',
|
|
||||||
];
|
|
||||||
header('Content-Type: ' . ($mimeTypes[$ext] ?? 'application/octet-stream'));
|
|
||||||
readfile($filePath);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
http_response_code(404);
|
|
||||||
$response = ['success' => false, 'message' => '未找到'];
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
http_response_code(500);
|
|
||||||
$response = ['success' => false, 'message' => '服务器错误: ' . $e->getMessage()];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
http_response_code(200);
|
||||||
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
echo json_encode($response, JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
// ============ 处理函数 ============
|
|
||||||
|
|
||||||
function getInput(): array
|
|
||||||
{
|
|
||||||
$input = file_get_contents('php://input');
|
|
||||||
return json_decode($input, true) ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleRegister(): array
|
|
||||||
{
|
|
||||||
$data = getInput();
|
|
||||||
$username = $data['username'] ?? '';
|
|
||||||
$password = $data['password'] ?? '';
|
|
||||||
|
|
||||||
$userManager = new UserManager();
|
|
||||||
$result = $userManager->register($username, $password);
|
|
||||||
|
|
||||||
if ($result['success']) {
|
|
||||||
$_SESSION['user_id'] = $result['userId'];
|
|
||||||
$_SESSION['username'] = $username;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleLogin(): array
|
|
||||||
{
|
|
||||||
$data = getInput();
|
|
||||||
$username = $data['username'] ?? '';
|
|
||||||
$password = $data['password'] ?? '';
|
|
||||||
|
|
||||||
$userManager = new UserManager();
|
|
||||||
$result = $userManager->login($username, $password);
|
|
||||||
|
|
||||||
if ($result['success']) {
|
|
||||||
$_SESSION['user_id'] = $result['userId'];
|
|
||||||
$_SESSION['username'] = $username;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleLogout(): array
|
|
||||||
{
|
|
||||||
session_destroy();
|
|
||||||
return ['success' => true, 'message' => '已退出登录'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleStatus(): array
|
|
||||||
{
|
|
||||||
if (empty($_SESSION['user_id'])) {
|
|
||||||
return ['success' => false, 'loggedIn' => false, 'message' => '未登录'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'loggedIn' => true,
|
|
||||||
'userId' => $_SESSION['user_id'],
|
|
||||||
'username' => $_SESSION['username'] ?? '未知',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleGameRender(): array
|
|
||||||
{
|
|
||||||
if (empty($_SESSION['user_id'])) {
|
|
||||||
return ['success' => false, 'message' => '请先登录'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$session = new GameSession($_SESSION['user_id']);
|
|
||||||
$output = $session->render();
|
|
||||||
$stateInfo = $session->getStateInfo();
|
|
||||||
|
|
||||||
return [
|
|
||||||
'success' => true,
|
|
||||||
'output' => $output,
|
|
||||||
'state' => $stateInfo['state'],
|
|
||||||
'stateName' => $stateInfo['stateName'],
|
|
||||||
'playerInfo' => $stateInfo['playerInfo'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleGameInput(): array
|
|
||||||
{
|
|
||||||
if (empty($_SESSION['user_id'])) {
|
|
||||||
return ['success' => false, 'message' => '请先登录'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = getInput();
|
|
||||||
$input = $data['input'] ?? '';
|
|
||||||
|
|
||||||
$session = new GameSession($_SESSION['user_id']);
|
|
||||||
$result = $session->handleInput($input);
|
|
||||||
|
|
||||||
// 现在handleInput返回的是数组:output, state, stateName, playerInfo
|
|
||||||
return array_merge(['success' => true], $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理 SSE 实时战斗流
|
|
||||||
*/
|
|
||||||
function handleBattleStream(): void
|
|
||||||
{
|
|
||||||
if (empty($_SESSION['user_id'])) {
|
|
||||||
http_response_code(401);
|
|
||||||
echo "data: " . json_encode(['error' => '请先登录']) . "\n\n";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从URL参数或POST数据获取输入
|
|
||||||
$input = $_GET['input'] ?? $_POST['input'] ?? '';
|
|
||||||
|
|
||||||
// 设置 SSE 响应头
|
|
||||||
header('Content-Type: text/event-stream');
|
|
||||||
header('Cache-Control: no-cache');
|
|
||||||
header('Connection: keep-alive');
|
|
||||||
header('X-Accel-Buffering: no'); // 禁用代理缓冲
|
|
||||||
|
|
||||||
// 发送初始化消息
|
|
||||||
echo "event: start\n";
|
|
||||||
echo "data: " . json_encode(['message' => '战斗开始']) . "\n\n";
|
|
||||||
ob_flush();
|
|
||||||
flush();
|
|
||||||
|
|
||||||
// 创建游戏会话并流式处理战斗
|
|
||||||
try {
|
|
||||||
$session = new GameSession($_SESSION['user_id']);
|
|
||||||
$session->streamBattle($input);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
echo "event: error\n";
|
|
||||||
echo "data: " . json_encode(['message' => $e->getMessage()]) . "\n\n";
|
|
||||||
ob_flush();
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 自动加载
|
// 自动加载
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
use Ratchet\Server\IoServer;
|
use Ratchet\Server\IoServer;
|
||||||
use Ratchet\Http\HttpServer;
|
use Ratchet\Http\HttpServer;
|
||||||
|
|
@ -29,7 +29,7 @@ $httpServer = new HttpServer($wsServer);
|
||||||
|
|
||||||
// 创建IO服务器(内部创建事件循环)
|
// 创建IO服务器(内部创建事件循环)
|
||||||
echo "[初始化] 创建 IO 服务器...\n";
|
echo "[初始化] 创建 IO 服务器...\n";
|
||||||
$server = IoServer::factory($httpServer, 9002, '0.0.0.0');
|
$server = IoServer::factory($httpServer, 9002, '192.168.89.112');
|
||||||
echo "[初始化] IO 服务器创建成功\n";
|
echo "[初始化] IO 服务器创建成功\n";
|
||||||
|
|
||||||
// 现在获取事件循环并设置给 GameProcessServer 实例
|
// 现在获取事件循环并设置给 GameProcessServer 实例
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user