This commit is contained in:
hantao 2025-12-11 18:03:25 +08:00
parent 96d90889cb
commit b2037e2eed
16 changed files with 4396 additions and 273 deletions

View File

@ -3,7 +3,7 @@
"description": "A PHP terminal game using Symfony src/Console",
"type": "project",
"require": {
"php": ">=8.0",
"php": ">=8.1",
"symfony/console": "^6.4",
"symfony/var-dumper": "^6.4",
"cboden/ratchet": "^0.4"

View File

@ -1 +1,84 @@
{"player":{"hp":100,"maxHp":100,"patk":10,"matk":10,"pdef":10,"mdef":10,"crit":0,"critdmg":110,"level":1,"exp":0,"potionPool":900,"maxExp":100,"inventory":[],"equip":[],"spiritStones":0,"npcFlags":[],"talentPoints":0,"talents":{"hp":0,"patk":0,"matk":0,"pdef":0,"mdef":0,"crit":0,"critdmg":0},"mana":60,"maxMana":60,"skillSlots":{"skill1":null,"skill2":null,"skill3":null,"skill4":null},"partners":[{"id":"li_feiyu","name":"厉飞雨","level":1,"exp":0,"maxExp":201,"equip":[],"talents":{"hp":0,"patk":0,"matk":0,"pdef":0,"mdef":0,"crit":0,"critdmg":0},"talentWeights":{"hp":1,"patk":3,"matk":1,"pdef":1,"mdef":1,"crit":3,"critdmg":2},"mana":60,"maxMana":100,"skillSlots":{"skill1":null,"skill2":null,"skill3":null,"skill4":null},"hp":100,"maxHp":100,"patk":15,"matk":5,"pdef":5,"mdef":3,"crit":10,"critdmg":130}]},"dungeonId":1,"state":4}
{
"player": {
"hp": 100,
"maxHp": 100,
"patk": 10,
"matk": 10,
"pdef": 10,
"mdef": 10,
"crit": 0,
"critdmg": 110,
"level": 1,
"exp": 0,
"potionPool": 900,
"maxExp": 100,
"inventory": [],
"equip": [],
"spiritStones": 0,
"npcFlags": [],
"talentPoints": 0,
"talents": {
"hp": 0,
"patk": 0,
"matk": 0,
"pdef": 0,
"mdef": 0,
"crit": 0,
"critdmg": 0
},
"mana": 60,
"maxMana": 60,
"skillSlots": {
"skill1": null,
"skill2": null,
"skill3": null,
"skill4": null
},
"partners": [
{
"id": "li_feiyu",
"name": "厉飞雨",
"level": 1,
"exp": 0,
"maxExp": 201,
"equip": [],
"talents": {
"hp": 0,
"patk": 0,
"matk": 0,
"pdef": 0,
"mdef": 0,
"crit": 0,
"critdmg": 0
},
"talentWeights": {
"hp": 1,
"patk": 3,
"matk": 1,
"pdef": 1,
"mdef": 1,
"crit": 3,
"critdmg": 2
},
"mana": 60,
"maxMana": 100,
"skillSlots": {
"skill1": null,
"skill2": null,
"skill3": null,
"skill4": null
},
"hp": 100,
"maxHp": 100,
"patk": 15,
"matk": 5,
"pdef": 5,
"mdef": 3,
"crit": 10,
"critdmg": 130
}
]
},
"dungeonId": 1,
"state": 4
}

62
src/Data/map.json Normal file
View 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": "在“破碎虚空”阶段,与冰凤合作稳固空间节点后,由天地法则凝聚而成的一次性通行符箓。使用它即可触发最终飞升剧情,并进入传承安排阶段。"
}
]

View File

@ -16,7 +16,6 @@ return [
// ============================================================
1 => [
'name' => '七玄门',
'min_level' => 1,
'key_item' => null, // 初始区域,无需钥匙
'desc' => '镜州边境的江湖门派,韩立在此获得神秘小瓶,开启修仙之路。',
'monsters' => [
@ -29,11 +28,7 @@ return [
'pdef' => 1,
'mdef' => 0,
'exp' => 10,
'spirit_stones' => 0, // 凡人没有灵石,改为银两(逻辑上)
'drops' => [],
'spells' => [
['id' => 11, 'name' => '罗烟步(伪)', 'rate' => 20],
],
'spirit_stones' => 0,
'weight' => 50,
],
[

3698
src/Data/monster.json Normal file

File diff suppressed because it is too large Load Diff

View 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
View 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);
}
}
}

View File

@ -18,4 +18,30 @@ abstract class Item
public abstract static function getDetailShow($item): array;
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);
}
}
}

View File

@ -36,12 +36,12 @@ class Quest extends Item
return 1;
}
public function getLineShow(): string
public static function getLineShow($item): string
{
// TODO: Implement getLineShow() method.
}
public function getDetailShow(): array
public static function getDetailShow($item): array
{
// TODO: Implement getDetailShow() method.
}

View File

@ -5,6 +5,7 @@ use Game\Core\Game;
use Game\Core\ItemDisplay;
use Game\Core\Screen;
use Game\Core\Colors;
use Game\Modules\Bag\Item;
use Game\Services\EquipmentEnhancer;
use Game\Entities\Partner;
@ -114,7 +115,7 @@ class EquipmentEnhancePanel
$item = $player->equip[$slot] ?? null;
if ($item) {
$str = ItemDisplay::renderListItem($item);
$str = Item::show($item);
$out->writeln("{$this->cyan}║[$slotIndex]$str");
$equipped[$slotIndex] = ['slot' => $slot, 'item' => $item];
} else {
@ -132,7 +133,7 @@ class EquipmentEnhancePanel
foreach ($player->inventory as $invIndex => $item) {
if (in_array($item['type'] ?? '', ['weapon', 'armor', 'boots', 'ring', 'necklace'])) {
$str = ItemDisplay::renderListItem($item);
$str = Item::show($item);
$out->writeln("{$this->cyan}║[$slotIndex]$str");
$inventoryItems[$slotIndex] = ['invIndex' => $invIndex, 'item' => $item];
$slotIndex++;
@ -224,7 +225,7 @@ class EquipmentEnhancePanel
$item = $partner->equip[$slot] ?? null;
if ($item) {
$str = ItemDisplay::renderListItem($item);
$str = Item::show($item);
$out->writeln("{$this->cyan}║[$slotIndex]$str");
$equipped[$slotIndex] = ['slot' => $slot, 'item' => $item];
} else {

View File

@ -7,6 +7,7 @@ use Game\Core\ItemDisplay;
use Game\Core\SpellDisplay;
use Game\Core\Colors;
use Game\Modules\Bag\Equipment;
use Game\Modules\Bag\Item;
class InventoryPanel
{
@ -99,7 +100,7 @@ class InventoryPanel
if ($item['type'] == 'quest_item'){
}else{
$displayStr = Equipment::getLineShow($item);
$displayStr = Item::show($item);
}
$out->writeln("[{$index}] {$displayStr}");
@ -153,7 +154,7 @@ class InventoryPanel
$out->writeln("╔════════════════════════════════════════╗");
// 使用统一的详细显示
$detailLines = Equipment::getLineShow($item);
$detailLines = Item::show($item);
$out->writeln($detailLines);
if ($quantity > 1) {
$out->writeln("");
@ -305,37 +306,13 @@ class InventoryPanel
$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;
// Remove from inventory
unset($player->inventory[$index]);
$player->inventory = array_values($player->inventory);
$enhanceStr = ($item['enhanceLevel'] ?? 0) > 0 ? "+{$item['enhanceLevel']}" : "";
$out->writeln("你装备了:{$item['name']}{$enhanceStr}");
$out->writeln("你装备了:{$item['name']}");
Screen::sleep(1);
}
@ -362,7 +339,7 @@ class InventoryPanel
$player = $this->game->player;
// 计算售价
$sellPrice = \Game\Entities\Item::calculateSellPrice($item);
$sellPrice = Item::calcPrice($item);
// 消耗品可能有多个,卖一个
$quantity = $item['quantity'] ?? 1;
@ -545,7 +522,7 @@ class InventoryPanel
$isEquipment = in_array($item['type'], ['weapon', 'armor', 'ring', 'necklace', 'boots','spell']);
if ($isEquipment && in_array($quality, $qualitiesToSell)) {
$price = \Game\Entities\Item::calculateSellPrice($item);
$price = Item::calcPrice($item);
$itemsToSell[] = [
'index' => $index,
'item' => $item,
@ -727,7 +704,7 @@ class InventoryPanel
foreach ($slots as $slot => $slotName) {
$item = $player->equip[$slot] ?? null;
if ($item){
$slotLines = Equipment::getLineShow($item);
$slotLines = Item::show($item);
$out->writeln($slotLines);
}
}

View File

@ -7,6 +7,8 @@ use Game\Core\Screen;
use Game\Core\Input;
use Game\Core\ItemDisplay;
use Game\Core\SpellDisplay;
use Game\Modules\Bag\Equipment;
use Game\Modules\Bag\Item;
use Game\Services\EquipmentEnhancer;
use Game\Entities\Actor;
use Game\Entities\Partner;
@ -355,7 +357,7 @@ class StatsPanel
foreach ($slots as $slotKey => $slotName) {
$currentItem = $actor->equip[$slotKey] ?? null;
if ($currentItem) {
$this->game->output->writeln("[{$slotIdx}] {$slotName}: " . ItemDisplay::renderListItem($currentItem));
$this->game->output->writeln("[{$slotIdx}] {$slotName}: " . Item::show($currentItem));
} else {
$this->game->output->writeln("[{$slotIdx}] {$slotName}: (空)");
}
@ -397,7 +399,7 @@ class StatsPanel
$displayIdx = 1;
$idxMap = [];
foreach ($equipableItems as $realIdx => $item) {
$displayStr = ItemDisplay::renderListItem($item, true, false);
$displayStr = Item::show($equipableItems);
$this->game->output->writeln("[{$displayIdx}] {$displayStr}");
$idxMap[$displayIdx] = $realIdx;
$displayIdx++;
@ -451,7 +453,7 @@ class StatsPanel
foreach ($slots as $slot => $name) {
if (!empty($actor->equip[$slot])) {
$item = $actor->equip[$slot];
$this->game->output->writeln("[{$idx}] {$name}: " . ItemDisplay::renderListItem($item));
$this->game->output->writeln("[{$idx}] {$name}: " . Item::show($item));
$equipped[$idx] = $slot;
$idx++;
}
@ -538,7 +540,7 @@ class StatsPanel
$enhanceLevel = $item['enhanceLevel'] ?? 0;
$bonusPercent = $enhanceLevel > 0 ? $actor->getEnhanceBonus($enhanceLevel) : '0';
$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;
$idx++;
}
@ -595,7 +597,7 @@ class StatsPanel
$out->writeln("{$this->cyan} 装 备 强 化 等 级 选 择{$this->reset}");
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
$out->writeln("");
$out->writeln("装备: " . ItemDisplay::renderListItem($item));
$out->writeln("装备: " . Item::show($item));
$currentBonus = $enhanceLevel > 0 ? $actor->getEnhanceBonus($enhanceLevel) : '0';
$out->writeln("当前等级: {$this->yellow}+{$enhanceLevel}{$this->reset} {$this->green}(属性提升: +{$currentBonus}%){$this->reset}");
$out->writeln("");
@ -703,7 +705,7 @@ class StatsPanel
$out->writeln("{$this->cyan} 自 动 强 化 中{$this->reset}");
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
$out->writeln("");
$out->writeln("装备: " . ItemDisplay::renderListItem($item));
$out->writeln("装备: " . Item::show($item));
$out->writeln("目标: +{$targetLevel}");
$out->writeln("");
@ -859,7 +861,7 @@ class StatsPanel
$spellIndices = [];
$i = 1;
foreach ($availableSpells as $index => $item) {
$out->writeln("[{$i}] " . ItemDisplay::renderListItem($item));
$out->writeln("[{$i}] " . Item::show($item));
$spellIndices[$i] = $index;
$i++;
}
@ -881,7 +883,7 @@ class StatsPanel
foreach ($skillSlots as $slot) {
$currentSpell = $actor->skillSlots[$slot] ?? null;
if ($currentSpell) {
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::renderListItem($currentSpell));
$out->writeln("[{$i}] 技能{$i}: " . Item::show($currentSpell));
} else {
$out->writeln("[{$i}] 技能{$i}: (空)");
}
@ -929,7 +931,7 @@ class StatsPanel
foreach ($skillSlots as $slot) {
$currentSpell = $actor->skillSlots[$slot] ?? null;
if ($currentSpell) {
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::renderListItem($currentSpell));
$out->writeln("[{$i}] 技能{$i}: " . Item::show($currentSpell));
$hasSkill = true;
} else {
$out->writeln("[{$i}] 技能{$i}: (空)");
@ -1028,7 +1030,7 @@ class StatsPanel
$slotNum = array_search($slotName, $skillSlots) + 1;
$spell = $skillInfo['spell'];
$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;
$i++;
}
@ -1069,7 +1071,7 @@ class StatsPanel
$out->writeln("{$this->cyan} 技 能 强 化 等 级 选 择{$this->reset}");
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
$out->writeln("");
$out->writeln("技能: " . ItemDisplay::renderListItem($item));
$out->writeln("技能: " . Item::show($item));
$currentBonus = $enhanceLevel > 0 ? $actor->getEnhanceBonus($enhanceLevel) : '0';
$out->writeln("当前等级: {$this->yellow}+{$enhanceLevel}{$this->reset} {$this->green}(属性提升: +{$currentBonus}%){$this->reset}");
$out->writeln("");
@ -1173,7 +1175,7 @@ class StatsPanel
$out->writeln("{$this->cyan} 自 动 强 化 中{$this->reset}");
$out->writeln("{$this->cyan}════════════════════════════════════{$this->reset}");
$out->writeln("");
$out->writeln("技能: " . ItemDisplay::renderListItem($item));
$out->writeln("技能: " . Item::show($item));
$out->writeln("目标: +{$targetLevel}");
$out->writeln("");

View File

@ -2,7 +2,7 @@
# 凡人修仙传 - Web 服务器启动脚本
PORT=${1:-65534}
HOST=${2:-0.0.0.0}
HOST=${2:-192.168.89.112}
echo "=================================="
echo " 凡人修仙传 - 文字版"

View File

@ -2,16 +2,27 @@
use Game\Modules\Bag\Equipment;
use Game\Modules\Bag\Spell;
use Game\Modules\Skill\Enums\DamageType;
use Game\Modules\Skill\Factories\EffectFactory;
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);
dd(2);
// 效果使用示例
// 创建伤害效果
$damageEffect = EffectFactory::createEffect([
'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);

View File

@ -4,23 +4,9 @@
* 使用方法: 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';
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('Access-Control-Allow-Origin: *');
@ -40,203 +26,29 @@ $path = parse_url($requestUri, PHP_URL_PATH);
// 静态文件处理
if ($path === '/' || $path === '/process.html') {
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;
}
// API 路由
$response = ['success' => false, 'message' => '未知请求'];
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()];
}
$response = ['success' => true, 'message' => 'Web server is running. This is a CLI-based game.'];
http_response_code(200);
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();
}
}

View File

@ -10,7 +10,7 @@
*/
// 自动加载
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/../vendor/autoload.php';
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
@ -29,7 +29,7 @@ $httpServer = new HttpServer($wsServer);
// 创建IO服务器内部创建事件循环
echo "[初始化] 创建 IO 服务器...\n";
$server = IoServer::factory($httpServer, 9002, '0.0.0.0');
$server = IoServer::factory($httpServer, 9002, '192.168.89.112');
echo "[初始化] IO 服务器创建成功\n";
// 现在获取事件循环并设置给 GameProcessServer 实例