@@ -145,6 +145,62 @@ PLAYER_POOL = (
_card ( " maj3r " , " MAJ3R " , 2 , " 指挥 " , " 土耳其 " , " Eternal Fire " , ( " Space Soldiers " , ) , 68 , 74 , 71 , 93 ) ,
_card ( " wicadia " , " Wicadia " , 2 , " 步枪手 " , " 土耳其 " , " Eternal Fire " , ( ) , 76 , 70 , 68 ) ,
_card ( " jottaaa " , " jottAAA " , 1 , " 步枪手 " , " 土耳其 " , " Eternal Fire " , ( " Sangal " , ) , 67 , 64 , 63 ) ,
_card ( " teses_heroic " , " TeSeS " , 2 , " 步枪手 " , " 丹麦 " , " HEROIC " , ( " Falcons " , ) , 76 , 73 , 72 ) ,
_card ( " sjuush " , " sjuush " , 2 , " 步枪手 " , " 丹麦 " , " HEROIC " , ( ) , 74 , 78 , 77 ) ,
_card ( " degster " , " degster " , 3 , " 狙击手 " , " 俄罗斯 " , " HEROIC " , ( " OG " , " Spirit " ) , 82 , 76 , 82 ) ,
_card ( " kyxsan " , " kyxsan " , 2 , " 指挥 " , " 北马其顿 " , " HEROIC " , ( " Apeks " , ) , 64 , 75 , 70 , 90 ) ,
_card ( " nertz_heroic " , " NertZ " , 3 , " 步枪手 " , " 以色列 " , " HEROIC " , ( " ENCE " , " Liquid " ) , 85 , 79 , 82 ) ,
_card ( " torzi_mouz " , " torzsi " , 2 , " 狙击手 " , " 匈牙利 " , " MOUZ " , ( ) , 78 , 78 , 80 ) ,
_card ( " spinx " , " Spinx " , 4 , " 步枪手 " , " 以色列 " , " Vitality " , ( " ENCE " , ) , 88 , 87 , 86 ) ,
_card ( " electronic " , " electroNic " , 4 , " 步枪手 " , " 俄罗斯 " , " Virtus.pro " , ( " NAVI " , " Cloud9 " ) , 87 , 84 , 84 ) ,
_card ( " fame " , " fame " , 3 , " 步枪手 " , " 俄罗斯 " , " Virtus.pro " , ( ) , 82 , 83 , 82 ) ,
_card ( " fl1t " , " FL1T " , 3 , " 步枪手 " , " 俄罗斯 " , " Virtus.pro " , ( " forZe " , ) , 81 , 82 , 80 ) ,
_card ( " jame " , " Jame " , 3 , " 指挥 " , " 俄罗斯 " , " Virtus.pro " , ( " AVANGAR " , ) , 72 , 87 , 86 , 96 ) ,
_card ( " yekindar_vp " , " YEKINDAR " , 4 , " 步枪手 " , " 拉脱维亚 " , " Virtus.pro " , ( " Liquid " , " FURIA " ) , 90 , 78 , 76 ) ,
_card ( " hallzerk " , " hallzerk " , 2 , " 狙击手 " , " 挪威 " , " Complexity " , ( " Dignitas " , ) , 75 , 72 , 75 ) ,
_card ( " grim " , " Grim " , 2 , " 步枪手 " , " 美国 " , " Complexity " , ( " Liquid " , ) , 74 , 74 , 73 ) ,
_card ( " jt " , " JT " , 2 , " 指挥 " , " 南非 " , " Complexity " , ( " Cloud9 " , ) , 62 , 73 , 69 , 89 ) ,
_card ( " floppy " , " floppy " , 1 , " 步枪手 " , " 美国 " , " Complexity " , ( " Cloud9 " , ) , 68 , 67 , 65 ) ,
_card ( " junior " , " junior " , 1 , " 狙击手 " , " 美国 " , " NRG " , ( " FURIA " , " Complexity " ) , 68 , 64 , 66 ) ,
_card ( " art " , " arT " , 2 , " 指挥 " , " 巴西 " , " Fluxo " , ( " FURIA " , ) , 69 , 68 , 74 , 92 ) ,
_card ( " biguzera " , " biguzera " , 3 , " 指挥 " , " 巴西 " , " paiN " , ( ) , 75 , 80 , 80 , 92 ) ,
_card ( " nqz " , " nqz " , 2 , " 狙击手 " , " 巴西 " , " paiN " , ( " 00Nation " , ) , 76 , 72 , 74 ) ,
_card ( " snow " , " snow " , 1 , " 步枪手 " , " 巴西 " , " paiN " , ( ) , 67 , 65 , 63 ) ,
_card ( " brnz4n " , " brnz4n " , 1 , " 步枪手 " , " 巴西 " , " MIBR " , ( ) , 66 , 66 , 64 ) ,
_card ( " exit " , " exit " , 2 , " 指挥 " , " 巴西 " , " MIBR " , ( ) , 67 , 75 , 71 , 88 ) ,
_card ( " insani " , " insani " , 3 , " 步枪手 " , " 巴西 " , " MIBR " , ( ) , 83 , 75 , 77 ) ,
_card ( " saffee " , " saffee " , 2 , " 狙击手 " , " 巴西 " , " MIBR " , ( " FURIA " , ) , 76 , 72 , 74 ) ,
_card ( " slaxz " , " slaxz- " , 2 , " 狙击手 " , " 德国 " , " GamerLegion " , ( " ALTERNATE " , ) , 75 , 72 , 74 ) ,
_card ( " volt " , " volt " , 2 , " 步枪手 " , " 英国 " , " GamerLegion " , ( " Into the Breach " , ) , 74 , 73 , 72 ) ,
_card ( " acoR " , " acoR " , 2 , " 狙击手 " , " 丹麦 " , " GamerLegion " , ( " MOUZ " , ) , 75 , 73 , 76 ) ,
_card ( " keoz " , " Keoz " , 1 , " 步枪手 " , " 比利时 " , " GamerLegion " , ( ) , 65 , 66 , 64 ) ,
_card ( " isak " , " isak " , 1 , " 步枪手 " , " 瑞典 " , " GamerLegion " , ( ) , 66 , 67 , 65 ) ,
_card ( " tabseN " , " tabseN " , 3 , " 指挥 " , " 德国 " , " BIG " , ( ) , 77 , 82 , 79 , 94 ) ,
_card ( " krimbo " , " Krimbo " , 3 , " 步枪手 " , " 德国 " , " BIG " , ( ) , 82 , 82 , 81 ) ,
_card ( " syrson " , " syrsoN " , 2 , " 狙击手 " , " 德国 " , " BIG " , ( ) , 76 , 73 , 76 ) ,
_card ( " mantuu " , " mantuu " , 2 , " 狙击手 " , " 波兰 " , " BIG " , ( " OG " , ) , 76 , 74 , 75 ) ,
_card ( " blamef " , " blameF " , 4 , " 步枪手 " , " 丹麦 " , " fnatic " , ( " Astralis " , " Complexity " ) , 88 , 88 , 84 ) ,
_card ( " bodyy " , " bodyy " , 2 , " 指挥 " , " 法国 " , " fnatic " , ( " G2 " , ) , 66 , 75 , 72 , 89 ) ,
_card ( " matys " , " MATYS " , 2 , " 步枪手 " , " 斯洛伐克 " , " fnatic " , ( ) , 76 , 72 , 72 ) ,
_card ( " afro " , " afro " , 2 , " 狙击手 " , " 法国 " , " fnatic " , ( " LDLC " , ) , 75 , 72 , 74 ) ,
_card ( " kairon " , " KaiR0N- " , 2 , " 步枪手 " , " 俄罗斯 " , " BetBoom " , ( " Virtus.pro " , ) , 76 , 70 , 72 ) ,
_card ( " zorte " , " zorte " , 2 , " 狙击手 " , " 俄罗斯 " , " BetBoom " , ( " forZe " , ) , 77 , 73 , 75 ) ,
_card ( " nafany " , " nafany " , 2 , " 指挥 " , " 俄罗斯 " , " BetBoom " , ( " Cloud9 " , " Gambit " ) , 65 , 74 , 70 , 91 ) ,
_card ( " s1ren " , " s1ren " , 2 , " 步枪手 " , " 俄罗斯 " , " BetBoom " , ( " Spirit " , ) , 75 , 73 , 72 ) ,
_card ( " rigoN " , " rigoN " , 2 , " 步枪手 " , " 瑞士 " , " Bad News Eagles " , ( " Guild Eagles " , ) , 77 , 70 , 72 ) ,
_card ( " juanflatroo " , " juanflatroo " , 2 , " 步枪手 " , " 科索沃 " , " Bad News Eagles " , ( " Guild Eagles " , ) , 76 , 71 , 72 ) ,
_card ( " calyx " , " Calyx " , 2 , " 步枪手 " , " 土耳其 " , " Eternal Fire " , ( " Space Soldiers " , ) , 75 , 73 , 74 ) ,
_card ( " issaa " , " ISSAA " , 1 , " 步枪手 " , " 约旦 " , " JiJieHao " , ( " OG " , ) , 67 , 64 , 66 ) ,
_card ( " boros " , " BOROS " , 2 , " 步枪手 " , " 约旦 " , " Into the Breach " , ( " Falcons " , " Monte " ) , 78 , 68 , 70 ) ,
_card ( " woro2k " , " Woro2k " , 3 , " 狙击手 " , " 乌克兰 " , " Monte " , ( ) , 82 , 76 , 80 ) ,
_card ( " demqq " , " DemQQ " , 2 , " 步枪手 " , " 乌克兰 " , " Monte " , ( ) , 76 , 74 , 73 ) ,
_card ( " dycha " , " dycha " , 2 , " 步枪手 " , " 波兰 " , " ENCE " , ( ) , 75 , 74 , 72 ) ,
_card ( " gla1ve " , " gla1ve " , 3 , " 指挥 " , " 丹麦 " , " ENCE " , ( " Astralis " , ) , 72 , 84 , 79 , 98 ) ,
_card ( " hades " , " hades " , 2 , " 狙击手 " , " 波兰 " , " ENCE " , ( " 9INE " , ) , 76 , 73 , 75 ) ,
_card ( " goofy " , " Goofy " , 1 , " 步枪手 " , " 波兰 " , " ENCE " , ( " 9INE " , ) , 67 , 66 , 64 ) ,
_card ( " sausol " , " sausol " , 1 , " 步枪手 " , " 西班牙 " , " Movistar Riders " , ( ) , 66 , 65 , 63 ) ,
_card ( " alex_movistar " , " alex " , 2 , " 指挥 " , " 西班牙 " , " Movistar Riders " , ( " NiP " , ) , 64 , 74 , 70 , 88 ) ,
_card ( " dav1g " , " dav1g " , 1 , " 步枪手 " , " 西班牙 " , " KOI " , ( " Movistar Riders " , ) , 65 , 65 , 64 ) ,
)
CARD_BY_ID = { card [ " id " ] : card for card in PLAYER_POOL }
@@ -182,16 +238,180 @@ COACH_POOL = (
_coach ( " trace " , " trace " , 2 , " 防守复盘 " , 70 , 86 , 74 , 6 ) ,
_coach ( " ash " , " ash " , 1 , " 新人培养 " , 76 , 74 , 72 , 5 ) ,
_coach ( " mithr " , " mithR " , 1 , " 基础纪律 " , 70 , 78 , 70 , 5 ) ,
_coach ( " kuben " , " kuben " , 4 , " 慢控默认 " , 74 , 88 , 82 , 8 ) ,
_coach ( " groove " , " groove " , 3 , " 学院体系 " , 72 , 84 , 78 , 7 ) ,
_coach ( " guerri " , " guerri " , 4 , " 巴西爆弹 " , 86 , 76 , 80 , 8 ) ,
_coach ( " xizt " , " Xizt " , 4 , " 默认纪律 " , 76 , 86 , 82 , 8 ) ,
_coach ( " sycrone " , " sycrone " , 3 , " 青年提拔 " , 80 , 80 , 78 , 7 ) ,
_coach ( " malek " , " maLeK " , 3 , " 明星平衡 " , 82 , 78 , 80 , 7 ) ,
_coach ( " peacemaker " , " peacemaker " , 2 , " 临时救火 " , 78 , 74 , 76 , 6 ) ,
_coach ( " kassad " , " kassad " , 2 , " 纪律重建 " , 72 , 82 , 74 , 6 ) ,
)
COACH_BY_ID = { coach [ " id " ] : coach for coach in COACH_POOL }
TRAINING_OPPONENTS = (
( " Team Falcons " , 292 ) ,
( " Natus Vincere " , 306 ) ,
( " Team Vitality " , 316 ) ,
( " FaZe Clan " , 300 ) ,
( " G2 Esports " , 286 ) ,
( " The MongolZ " , 278 ) ,
)
TRAINING_WIN_GOLD = 3
TRAINING_LOSS_GOLD = 1
TRAINING_BYE_GOLD = 2
NARRATIVE_FRAGMENTS = {
" openings " : (
" {team} 开局没有急着提速,先把默认控图铺开。 " ,
" {team} 第一波提速很坚决,外场道具落点干净。 " ,
" {team} 在手枪局后选择慢摸,试图逼出对手的前压信息。 " ,
" {team} 的狙击手早早架住第一交火点,节奏被压得很低。 " ,
" {team} 连续用两颗闪光清掉近点,进攻面一下打开。 " ,
" {team} 防守开局站位很深,把回防路线留得很完整。 " ,
" {team} 先拿地图控制权,再把包点压力一点点推上去。 " ,
" {team} 开局没有赌点,靠信息互换慢慢读阵型。 " ,
" {team} 在第一枪位赢下后没有贪,马上回收人数优势。 " ,
" {team} 用一波假打骗出轮转,第二时间才开始真正爆弹。 " ,
" {team} 选择保守开局,宁愿少拿信息也不送首杀。 " ,
" {team} 的指挥把默认拉得很散,让对手很难判断重心。 " ,
" {team} 手枪局道具很少,但站位互补做得很扎实。 " ,
" {team} 没有被前几回合影响,依旧按自己的节奏展开。 " ,
" {team} 的第一套进攻明显针对了对手弱侧。 " ,
" {team} 在经济局也保留了两颗关键闪,准备打后点反清。 " ,
" {team} 上来就抢中路,想用主动权换后续转点空间。 " ,
" {team} 先把狙击枪放在安全位,围绕它慢慢组织推进。 " ,
) ,
" runs " : (
" {team} 连拿 {count} 分之后,现场短暂安静下来。 " ,
" {team} 把经济滚起来后,连续 {count} 个回合没有给对手完整开枪空间。 " ,
" {team} 这一波 {count} 连分不是纯枪法碾压,而是每回合都换到正确信息。 " ,
" {team} 的节奏忽快忽慢, {count} 个回合里每次进点方式都不一样。 " ,
" {team} 靠补枪链吃下 {count} 分,队伍语音应该已经很热。 " ,
" {team} 的中期处理开始成型,连续 {count} 分把对手钱打空。 " ,
" {team} 没有急着结束比赛,而是用 {count} 连分把地图控制牢牢握住。 " ,
" {team} 这段 {count} 连分很关键,尤其是第二个残局稳定了军心。 " ,
" {team} 在暂停之后突然提速,直接打出 {count} 连分。 " ,
" {team} 先抓前压,再抓回防, {count} 个回合把读秒处理做满。 " ,
" {team} 的防守交叉火力越站越舒服, {count} 连分让比分拉开。 " ,
" {team} 把对手的道具节奏读透,连续 {count} 分都避开了主防区。 " ,
" {team} 经济起飞后没有乱买, {count} 连分中枪线一直保持完整。 " ,
" {team} 在被首杀的回合仍然能翻, {count} 连分含金量很高。 " ,
" {team} 的默认推进开始收利息, {count} 个回合每次都能换到便宜。 " ,
" {team} 靠一波小连胜把压力交回去, {count} 分之后对手必须叫停。 " ,
" {team} 的二楼夹击反复奏效, {count} 连分让对手开始犹豫。 " ,
" {team} 连续 {count} 回合把残局拖到优势人数,后点纪律很稳。 " ,
" {team} 打出 {count} 连分后节奏放慢,像是在等对手自己露破绽。 " ,
" {team} 这波 {count} 连分让地图气质变了,所有枪位都开始往前压。 " ,
) ,
" clutches " : (
" {player} 留到最后一个残局,靠一颗烟把拆包时间偷了出来。 " ,
" {player} 没有急着补枪,等脚步声靠近后才横拉收掉残局。 " ,
" {player} 在 1v2 里先断退路,再回头处理包点。 " ,
" {player} 的残局处理很冷,假拆骗出第一枪后直接反清。 " ,
" {player} 被逼到死角,但一颗反弹闪把局面重新打开。 " ,
" {player} 没有追击残血,而是先保住雷包,判断非常老练。 " ,
" {player} 关键回合双杀,把本来要崩的经济线救回来。 " ,
" {player} 在读秒阶段完成补枪,队友的拉扯给得刚刚好。 " ,
" {player} 先拿信息再开枪,这个残局赢在耐心。 " ,
" {player} 硬吃白光完成换人,比分没有被对手咬住。 " ,
" {player} 从连接位摸出,正好打掉回防节奏。 " ,
" {player} 这次不追求集锦,只把枪线封死等队友到位。 " ,
" {player} 的狙击枪没有空枪,关键首杀让整回合变得简单。 " ,
" {player} 残血仍然敢看第一枪位,队伍因此拿到赛点。 " ,
" {player} 拿下残局后没有庆祝,马上提醒队友保枪经济。 " ,
" {player} 靠脚步假信息骗出转点,最后一枪很稳。 " ,
" {player} 在包点里连续换位,对手始终没能定位。 " ,
" {player} 把闪光背身吃完后仍然补到第二枪,这是关键转折。 " ,
" {player} 一个人拖住整条回防路线,为队伍争到拆包窗口。 " ,
" {player} 最后选择不 peek,让时间替自己赢下这一分。 " ,
) ,
" pauses " : (
" 比分被拉开后,双方都慢了半拍,像是在重新读这张图。 " ,
" 这里适合停一下,刚才几回合的经济差已经开始影响下一枪。 " ,
" 暂停回来之后,防守方明显把第一接触点后撤了半格。 " ,
" 连续交火后节奏突然变慢,双方都在等对方先犯错。 " ,
" 这一段没有夸张集锦,但每一颗道具都在改变下一回合。 " ,
" 场面安静了几秒,真正决定胜负的是下一次首杀。 " ,
" 队伍语音应该都在压节奏,没人愿意在这里送掉经济。 " ,
" 这不是简单的比分变化,战术重心已经从爆弹转到控图。 " ,
" 现场进入短暂拉扯,地图控制权一点点被重新分配。 " ,
" 两边都知道下一分很贵,所以这回合开局格外谨慎。 " ,
" 这一小段空白很重要,强队往往就是在这里调整默认。 " ,
" 比赛没有马上进入下一波交火,指挥正在重新排优先级。 " ,
" 刚才的连分让对手不得不换站位,防线开始出现试探。 " ,
" 经济压力让双方都少了一点容错,下一波道具会很关键。 " ,
" 这里没有急躁的前压,只有一点点互相试探。 " ,
" 暂停不是为了拖时间,而是在给下一套进攻换答案。 " ,
" 双方都把节奏压住,地图像被重新归零。 " ,
" 这一分之前的几秒钟很长,所有人都在等第一个脚步声。 " ,
" 场上没有无意义的 peek,每个人都在守自己的触发条件。 " ,
" 这段过渡让比赛更像真正的 BO 系列赛,而不是单纯掷骰子。 " ,
) ,
" economy " : (
" {team} 赢下这一分后可以完整起枪,下一回合选择会更多。 " ,
" {team} 输掉长枪局后经济见底,可能要交出一回合调整空间。 " ,
" {team} 没有强行追梦,选择保枪把下一回合留住。 " ,
" {team} 的连败补偿到位,下一回合有机会靠道具质量反打。 " ,
" {team} 这回合缴下两把枪,经济收益比比分更重要。 " ,
" {team} 虽然丢分,但保下主狙,下一回合还有破局点。 " ,
" {team} 的半甲手枪没有打成,但至少逼出了对手不少道具。 " ,
" {team} 经济优势开始显现,可以更大胆地争夺第一枪位。 " ,
" {team} 如果下一分再丢,就必须进入半起调整。 " ,
" {team} 这一波选择全甲全弹,明显是要在中期一次打穿。 " ,
" {team} 保下来的两把枪让下回合不至于完全被动。 " ,
" {team} 连续换枪成功,把对手的存款一点点磨掉。 " ,
" {team} 用低成本回合换到下包,经济线还没有断。 " ,
" {team} 这波赢的不只是分数,还有下一回合的主动权。 " ,
" {team} 被重置经济后节奏放慢,先求稳住半场尾声。 " ,
" {team} 靠残局胜利拿到额外空间,可以考虑继续 D 牌补强。 " ,
) ,
" closers " : (
" {team} 没有给翻盘窗口,最后一波回防处理得很完整。 " ,
" {team} 在赛点局选择最熟悉的默认,用纪律结束地图。 " ,
" {team} 最后一回合没有贪枪,稳稳把人数优势转成胜势。 " ,
" {team} 的收官很干净,地图尾声没有出现多余失误。 " ,
" {team} 用一波双拉打开包点,最后比分定格。 " ,
" {team} 把比赛拖进自己舒服的节奏,收尾阶段非常稳。 " ,
" {team} 赛点局的道具层次很清楚,对手没有舒服的回防路线。 " ,
" {team} 终于等到对手前压,抓住这个窗口终结地图。 " ,
" {team} 收下最后一分时没有冒进,这就是强队式结尾。 " ,
" {team} 最后一枪来得很快,但前面的信息铺垫已经足够。 " ,
" {team} 在关键经济局之后顺势收掉地图。 " ,
" {team} 先控退路再进包点,结尾没有留下残局悬念。 " ,
" {team} 把上半场积累的优势守到最后。 " ,
" {team} 用一波慢夹收官,比分看起来合理,过程更像真实比赛。 " ,
" {team} 没让对手连追,赛点局直接把节奏抢回来。 " ,
" {team} 的最后一分赢在补枪,枪线没有断。 " ,
" {team} 收官回合把每个角落都清干净,耐心给了回报。 " ,
" {team} 把悬念留到最后一刻,但关键枪没有手软。 " ,
) ,
" training_hooks " : (
" 训练赛不计入小组胜负,但会发金币,方便后续刷新商店。 " ,
" 这轮训练更像实战热身,每队都会遇到同场参赛对手。 " ,
" 训练赛重点不是淘汰谁,而是检验当前阵容能不能打完整张图。 " ,
" 这轮结束后可以 D 牌、锁店或调首发,下一轮再检验成果。 " ,
" AI 战队也会参与训练,对局结果会写入档案。 " ,
" 如果阵容缺少指挥,训练赛的中期回合会更容易失控。 " ,
" 如果你想追羁绊,现在是最适合刷新商店的窗口。 " ,
" 训练赛的金币很重要,别把每次刷新都当成免费试错。 " ,
" 这一阶段没有生命扣除,重点是运营和阵容成型。 " ,
" 训练赛的地图会轮换,不会一直用同一个固定比分模板。 " ,
" 每轮训练结束后,真人都需要发送 /结束回合 才会进入下一轮。 " ,
" 训练赛会按轮次播报,避免一次性刷出一大串结果。 " ,
" 如果出现轮空,会安排本地陪练队补一张 BO1。 " ,
" 训练阶段适合观察战术克制,火力、稳定、残局都会影响回合走向。 " ,
" 连胜会带来金币,但失败也有保底,保证测试局能继续运营。 " ,
" 这部分是纯文字自走棋的节奏层,不再是单条目标数值判定。 " ,
) ,
" challenges " : (
" 媒体日问题很尖锐,队伍需要在镜头前保持状态。 " ,
" 赞助商临时加码,要求队伍用指定战术打出观赏性。 " ,
" 主办方安排了签名会,状态管理成了额外考验。 " ,
" 训练馆网络短暂波动,队伍必须快速适应备用机器。 " ,
" 赛前采访拖长,指挥需要把注意力重新拉回比赛。 " ,
" 赞助商想看激进打法,但稳住节奏的人会拿到更高回报。 " ,
" 媒体日不是战斗回合,却会影响队伍的运营资源。 " ,
" 队伍如果阵容完整,就更容易把这次插曲处理成奖金。 " ,
" 这次挑战只出现一次,处理完就回到小组赛主线。 " ,
" 主舞台彩排占用了热身时间,教练的管理能力开始生效。 " ,
" 赞助商要求展示新战术,选错方向也不会淘汰,只会少拿金币。 " ,
" 现场采访提到了前一场失利,队员心态需要稳住。 " ,
" 这类挑战不扣生命,目的只是给运营阶段增加变数。 " ,
" 处理好这次插曲的队伍,会在下一场前多一点经济余地。 " ,
) ,
}
def _hash_int ( seed : str ) - > int :
@@ -201,6 +421,18 @@ def _hash_int(seed: str) -> int:
)
def _pick_fragment ( category : str , seed : str , * * values : Any ) - > str :
fragments = tuple ( NARRATIVE_FRAGMENTS . get ( category , ( ) ) )
if not fragments :
return " "
template = fragments [ _hash_int ( f " { category } | { seed } " ) % len ( fragments ) ]
return template . format ( * * values )
def _narrative_inventory_size ( ) - > int :
return sum ( len ( items ) for items in NARRATIVE_FRAGMENTS . values ( ) )
def _pack_response_parts ( parts : list [ str ] ) - > str :
cleaned = [ part . strip ( ) for part in parts if part . strip ( ) ]
if len ( cleaned ) < = 1 :
@@ -918,21 +1150,152 @@ def _auto_manage_ai(
return stories
MAP_POOL = ( " Mirage " , " Inferno " , " Nuke " , " Ancient " , " Dust2 " )
MAP_POOL = ( " Mirage " , " Inferno " , " Nuke " , " Ancient " , " Dust2 " , " Anubis " , " Overpass " )
def _map_score ( margin : float , seed : str ) - > tuple [ str , str , int ] :
if margin > = 0.18 :
return " 16-6 " , " 碾压 " , 0
if margin > = 0.11 :
return " 16-9 " , " 明显优势 " , 0
if margin > = 0.055 :
return " 16-12 " , " 小胜 " , 0
if margin > = 0.025 :
return " 16-14 " , " 险胜 " , 0
overtime_count = 1 + _hash_int ( seed + " |overtime " ) % 2
winner_score = 16 + overtime_count * 3
return f " { winner_score } - { winner_score - 2 } " , " 加时险胜 " , overtime_count
def _round_probability (
adjusted_a : float ,
adjusted_b : float ,
seed : str ,
round_index : int ,
score_a : int ,
score_b : int ,
) - > float :
average = max ( 1.0 , ( adjusted_a + adjusted_b ) / 2 )
power_edge = max ( - 0.16 , min ( 0.16 , ( adjusted_a - adjusted_b ) / average * 0.30 ) )
pressure = max ( - 0.04 , min ( 0.04 , ( score_b - score_a ) * 0.004 ) )
side_swing = 0.018 if round_index < = 15 else - 0.012
if round_index > 30 :
side_swing = 0.0
jitter = ( ( _hash_int ( f " { seed } |prob| { round_index } " ) % 701 ) - 350 ) / 10_000
return max ( 0.32 , min ( 0.68 , 0.5 + power_edge + pressure + side_swing + jitter ) )
def _simulate_map (
state : dict [ str , Any ] ,
key_a : str ,
key_b : str ,
seed : str ,
map_index : int ,
adjusted_a : float ,
adjusted_b : float ,
) - > dict [ str , Any ] :
score_a = 0
score_b = 0
rounds : list [ str ] = [ ]
current_key = " "
current_run = 0
best_run_key = " "
best_run = 0
def record_round ( round_index : int ) - > None :
nonlocal score_a , score_b , current_key , current_run
nonlocal best_run_key , best_run
probability_a = _round_probability (
adjusted_a ,
adjusted_b ,
seed ,
round_index ,
score_a ,
score_b ,
)
roll = ( _hash_int ( f " { seed } |round| { round_index } " ) % 10_000 ) / 10_000
winner_key = key_a if roll < probability_a else key_b
if winner_key == key_a :
score_a + = 1
else :
score_b + = 1
rounds . append ( winner_key )
if winner_key == current_key :
current_run + = 1
else :
current_key = winner_key
current_run = 1
if current_run > best_run :
best_run = current_run
best_run_key = winner_key
for round_index in range ( 1 , 31 ) :
record_round ( round_index )
if score_a > = 16 or score_b > = 16 :
break
round_index = len ( rounds )
if score_a == 15 and score_b == 15 :
while max ( score_a , score_b ) < 19 or abs ( score_a - score_b ) < 2 :
round_index + = 1
record_round ( round_index )
if round_index > 72 :
break
winner_key = key_a if score_a > score_b else key_b
loser_key = key_b if winner_key == key_a else key_a
winner_score = score_a if winner_key == key_a else score_b
loser_score = score_b if winner_key == key_a else score_a
overtime_rounds = max ( 0 , len ( rounds ) - 30 )
overtime_count = ( overtime_rounds + 5 ) / / 6
if overtime_count :
band = " 加时险胜 "
elif loser_score < = 6 :
band = " 压制 "
elif loser_score < = 10 :
band = " 优势 "
elif loser_score < = 13 :
band = " 小胜 "
else :
band = " 险胜 "
map_name = MAP_POOL [ ( map_index - 1 ) % len ( MAP_POOL ) ]
winner_team = _team_name ( state [ " players " ] [ winner_key ] )
loser_team = _team_name ( state [ " players " ] [ loser_key ] )
best_run_team = (
_team_name ( state [ " players " ] [ best_run_key ] ) if best_run_key else winner_team
)
opening_team = _team_name (
state [ " players " ] [ key_a if _hash_int ( seed + " |open " ) % 2 == 0 else key_b ]
)
actor = _key_player_name ( state , winner_key , map_index )
pause = _pick_fragment ( " pauses " , f " { seed } |pause " )
run_text = (
_pick_fragment (
" runs " ,
f " { seed } |run " ,
team = best_run_team ,
count = best_run ,
)
if best_run > = 4
else pause
)
overtime_story = " "
if overtime_count :
overtime_story = (
f " { map_name } 常规时间战成 15-15。 { winner_team } 在加时第 "
f " { overtime_count } 段抓住 { loser_team } 的转点空隙,以 "
f " { winner_score } - { loser_score } 收下地图。 "
)
return {
" map_index " : map_index ,
" map_name " : map_name ,
" winner_key " : winner_key ,
" winner " : state [ " players " ] [ winner_key ] [ " display_name " ] ,
" loser_key " : loser_key ,
" loser " : state [ " players " ] [ loser_key ] [ " display_name " ] ,
" score " : f " { winner_score } - { loser_score } " ,
" score_a " : score_a ,
" score_b " : score_b ,
" band " : band ,
" overtime_count " : overtime_count ,
" overtime_story " : overtime_story ,
" rounds_played " : len ( rounds ) ,
" best_run_key " : best_run_key ,
" best_run " : best_run ,
" opening_text " : _pick_fragment ( " openings " , f " { seed } |opening " , team = opening_team ) ,
" run_text " : run_text ,
" pause_text " : pause ,
" clutch_text " : _pick_fragment ( " clutches " , f " { seed } |clutch " , player = actor ) ,
" economy_text " : _pick_fragment ( " economy " , f " { seed } |economy " , team = loser_team ) ,
" closer_text " : _pick_fragment ( " closers " , f " { seed } |closer " , team = winner_team ) ,
" adjusted_a " : round ( adjusted_a , 2 ) ,
" adjusted_b " : round ( adjusted_b , 2 ) ,
}
def _battle (
@@ -969,51 +1332,23 @@ def _battle(
* ( 10_000 + fluct_b )
/ 10_000
)
if adjusted_a == adjusted_b :
adjusted_a + = (
0.01 if _hash_int ( f " { seed } |map| { map_index } |tie " ) % 2 == 0 else - 0.01
)
winner_key = key_a if adjusted_a > adjusted_b else key_b
item = _simulate_map (
state ,
key_a ,
key_b ,
f " { seed } |map| { map_index } " ,
map_index ,
adjusted_a ,
adjusted_b ,
)
winner_key = str ( item [ " winner_key " ] )
if winner_key == key_a :
wins_a + = 1
else :
wins_b + = 1
high = max ( adjusted_a , adjusted_b )
low = min ( adjusted_a , adjusted_b )
margin = ( high - low ) / max ( high , 1 )
score , band , overtime_count = _map_score (
margin ,
f " { seed } |map| { map_index } " ,
)
winner_name = state [ " players " ] [ winner_key ] [ " display_name " ]
loser_key = key_b if winner_key == key_a else key_a
loser_name = state [ " players " ] [ loser_key ] [ " display_name " ]
map_name = MAP_POOL [ map_index - 1 ]
overtime_story = " "
if overtime_count :
overtime_story = (
f " { map_name } 常规时间战成 15-15。 "
f " { winner_name } 在第 { overtime_count } 轮加时抓住 "
f " { loser_name } 的转点空隙,以 { score } 收下地图。 "
)
maps . append (
{
" map_index " : map_index ,
" map_name " : map_name ,
" winner_key " : winner_key ,
" winner " : winner_name ,
" loser_key " : loser_key ,
" loser " : loser_name ,
" score " : score ,
" band " : band ,
" overtime_count " : overtime_count ,
" overtime_story " : overtime_story ,
" adjusted_a " : round ( adjusted_a , 2 ) ,
" adjusted_b " : round ( adjusted_b , 2 ) ,
" fluctuation_a_bps " : fluct_a ,
" fluctuation_b_bps " : fluct_b ,
}
)
item [ " fluctuation_a_bps " ] = fluct_a
item [ " fluctuation_b_bps " ] = fluct_b
maps . append ( item )
if wins_a == target_maps or wins_b == target_maps :
break
winner_key , loser_key = (
@@ -1113,11 +1448,15 @@ def _battle_story(result: dict[str, Any]) -> str:
for item in result [ " maps " ]
)
matchup = result [ " matchup " ]
tactic_note = (
" 战术克制明显 "
if abs ( float ( matchup [ " a_total_percent " ] ) ) > = 5.5
else " 战术拉扯接近 "
)
return _host_text (
f " { result [ ' winner ' ] } 在 { result [ ' series_format ' ] } 以 { result [ ' score ' ] } "
f " 击败 { result [ ' loser ' ] } ( { map_scores } ) ; "
f " 战术 { matchup [ ' tactic_a ' ] } 对 { matchup [ ' tactic_b ' ] } , "
f " { result [ ' player_a ' ] } 克制修正 { matchup [ ' a_total_percent ' ] : +.2f } %。 "
f " 击败 { result [ ' loser ' ] } ( { map_scores } ) 。 "
f " 战术面是 { matchup [ ' tactic_a ' ] } 对 { matchup [ ' tactic_b ' ] } , { tactic_note } 。 "
)
@@ -1684,7 +2023,7 @@ def _draft_candidate_cards(state: dict[str, Any], round_number: int) -> list[str
def _start_draft_round ( state : dict [ str , Any ] , round_number : int ) - > None :
draft_type = " coach " if round_number == 1 else " player "
candidates = (
[ coach [ " id " ] for coach in COACH_POOL ]
[ coach [ " id " ] for coach in COACH_POOL [ : 10 ] ]
if draft_type == " coach "
else _draft_candidate_cards ( state , round_number )
)
@@ -1740,8 +2079,13 @@ def _draft_prompt_part(state: dict[str, Any]) -> str:
return f " 现在轮到 @ { name } 做出选择,使用 { command } 。 "
def _add_unit_to_roster ( player : dict [ str , Any ] , card_id : str ) - > None :
unit = _new_unit ( card_id )
def _add_unit_to_roster (
player : dict [ str , Any ] ,
card_id : str ,
* ,
copies : int = 1 ,
) - > None :
unit = _new_unit ( card_id , copies = copies )
if len ( player [ " board " ] ) < STARTER_COUNT :
player [ " board " ] . append ( unit )
else :
@@ -1771,13 +2115,15 @@ def _apply_draft_choice(
)
else :
card = CARD_BY_ID [ choice_id ]
if int ( state [ " pool " ] . get ( choice_id , 0 ) ) < = 0 :
draft_copies = 3
if int ( state [ " pool " ] . get ( choice_id , 0 ) ) < draft_copies :
raise ValueError ( " 这名选手已经被选走 " )
state [ " pool " ] [ choice_id ] - = 1
_add_unit_to_roster ( player , choice_id )
state [ " pool " ] [ choice_id ] - = draft_copies
_add_unit_to_roster ( player , choice_id , copies = draft_copies )
text = (
f " { player [ ' display_name ' ] } 选择了 { card [ ' name ' ] } ! "
f " { card [ ' cost ' ] } 费 { card [ ' role ' ] } , { card [ ' team ' ] } / { card [ ' nation ' ] } 。 "
f " { card [ ' cost ' ] } 费 { card [ ' role ' ] } , { card [ ' team ' ] } / { card [ ' nation ' ] } , "
" 以 2 星素材加入阵容。 "
)
candidates . pop ( slot - 1 )
draft [ " candidates " ] = candidates
@@ -1876,7 +2222,10 @@ def _ensure_full_rosters(state: dict[str, Any]) -> None:
]
candidates . sort (
key = lambda card_id : (
CARD_BY_ID [ card_id ] [ " cost " ] if player . get ( " is_ai " ) else - CARD_BY_ID [ card_id ] [ " cost " ] ,
CARD_BY_ID [ card_id ] [ " cost " ]
if player . get ( " is_ai " )
else abs ( int ( CARD_BY_ID [ card_id ] [ " cost " ] ) - 2 ) ,
int ( CARD_BY_ID [ card_id ] [ " cost " ] ) ,
_hash_int ( f " { state [ ' run_id ' ] } |fill| { key } | { card_id } " ) ,
)
)
@@ -1954,62 +2303,172 @@ def _shop_status_text(state: dict[str, Any], player: dict[str, Any]) -> str:
) + " \n 操作:/选1~/选6;/刷新;/锁定;/阵容;/战术 火力|稳定|残局;/结束回合 "
def _training_parts ( state : dict [ str , Any ] , event_id : str ) - > tuple [ list [ str ] , list [ dict [ str , Any ] ] , list [ dict [ str , Any ] ] ] :
parts = [
f " 欢迎来到 { state [ ' event_title ' ] } !你们已经到达酒店宾馆,即将开始训练赛任务。每队会连续完成三场训练赛,结束后可以 D 牌、调阵容,再用 /结束回合 进入小组赛。 "
def _training_schedule ( state : dict [ str , Any ] ) - > list [ dict [ str , Any ] ] :
rounds : list [ dict [ str , Any ] ] = [ ]
keys = list ( state [ " seed_order " ] )
for round_index , pairs in enumerate ( _round_robin_rounds ( keys ) , start = 1 ) :
paired = { key for pair in pairs for key in pair }
bye = next ( ( key for key in keys if key not in paired ) , " " )
rounds . append (
{
" round " : round_index ,
" pairs " : [ [ str ( pair [ 0 ] ) , str ( pair [ 1 ] ) ] for pair in pairs ] ,
" bye " : bye ,
}
)
return rounds
def _ensure_training_schedule ( state : dict [ str , Any ] ) - > None :
if state . get ( " training_rounds " ) :
return
state [ " training_rounds " ] = _training_schedule ( state )
state [ " training_round_cursor " ] = 0
def _training_pairing_line ( state : dict [ str , Any ] , round_item : dict [ str , Any ] ) - > str :
rendered = [
f " { _team_name ( state [ ' players ' ] [ pair [ 0 ] ] ) } vs { _team_name ( state [ ' players ' ] [ pair [ 1 ] ] ) } "
for pair in round_item . get ( " pairs " , [ ] )
]
bye = str ( round_item . get ( " bye " ) or " " )
if bye :
rendered . append ( f " { _team_name ( state [ ' players ' ] [ bye ] ) } vs 本地陪练队 " )
return " ; " . join ( rendered )
def _training_match_part (
state : dict [ str , Any ] ,
result : dict [ str , Any ] ,
round_number : int ,
) - > str :
item = result [ " maps " ] [ 0 ]
winner_key = str ( result [ " winner_key " ] )
loser_key = str ( result [ " loser_key " ] )
winner = state [ " players " ] [ winner_key ]
loser = state [ " players " ] [ loser_key ]
return " \n " . join (
[
(
f " 训练赛第 { round_number } 轮: { _team_name ( winner ) } "
f " vs { _team_name ( loser ) } "
) ,
(
f " { item [ ' map_name ' ] } : { item [ ' opening_text ' ] } "
f " { item [ ' run_text ' ] } "
) ,
(
f " { item [ ' clutch_text ' ] } { item [ ' closer_text ' ] } "
f " { _team_name ( winner ) } 以 { item [ ' score ' ] } 拿下本图。 "
) ,
(
f " 金币: { _team_name ( winner ) } + { TRAINING_WIN_GOLD } , "
f " { _team_name ( loser ) } + { TRAINING_LOSS_GOLD } 。 "
) ,
]
)
def _training_bye_part ( state : dict [ str , Any ] , key : str , round_number : int ) - > str :
player = state [ " players " ] [ key ]
player [ " gold " ] = int ( player [ " gold " ] ) + TRAINING_BYE_GOLD
hook = _pick_fragment (
" training_hooks " ,
f " { state [ ' run_id ' ] } |training|bye| { round_number } | { key } " ,
)
return (
f " 训练赛第 { round_number } 轮: { _team_name ( player ) } 与本地陪练队完成热身。 \n "
f " { hook } \n 金币: { _team_name ( player ) } + { TRAINING_BYE_GOLD } 。 "
)
def _settle_training_round (
state : dict [ str , Any ] ,
event_id : str ,
) - > tuple [ list [ str ] , list [ dict [ str , Any ] ] , list [ dict [ str , Any ] ] ] :
_ensure_training_schedule ( state )
rounds = list ( state . get ( " training_rounds " , [ ] ) )
cursor = int ( state . get ( " training_round_cursor " , 0 ) )
if cursor > = len ( rounds ) :
state [ " phase " ] = " training_complete_manage "
_prepare_new_shops ( state , " training-complete " )
_reset_ready ( state , _human_keys ( state ) )
return [
" 训练赛已经全部结束。可以最后 D 牌、调阵容;所有真人发送 /结束回合 后进入小组赛。 "
] , [ ] , [ ]
round_item = rounds [ cursor ]
round_number = int ( round_item [ " round " ] )
state [ " round_number " ] = round_number
parts : list [ str ] = [ ]
stories : list [ dict [ str , Any ] ] = [ ]
battles : list [ dict [ str , Any ] ] = [ ]
for key in state [ " join_order " ] :
player = state [ " players " ] [ key ]
power = lineup_power ( player ) [ " power " ]
lines = [ f " { _team_name ( player ) } 训练赛: " ]
total_reward = 0
for index in range ( 3 ) :
opponent , target = TRAINING_OPPONENTS [
( _hash_int ( f " { state [ ' run_id ' ] } |train| { key } | { index } " ) + index )
% len ( TRAINING_OPPONENTS )
]
fluctuation = _hash_int (
f " { state [ ' run_id ' ] } | { event_id } |train| { key } | { index } "
) % 401 - 200
adjusted = power * ( 10_000 + fluctuation ) / 10_000
won = adjusted > = target
reward = 3 if won else 1
total_reward + = reward
score = " 16-12 " if won else " 13-16 "
if abs ( adjusted - target ) < 12 :
score = " 19-17 " if won else " 17-19 "
lines . append (
f " { index + 1 } . vs { opponent } (目标 { target } ,发挥 { adjusted : .1f } ): "
f " { ' 胜 ' if won else ' 负 ' } { score } ,获得 { reward } 金币 "
)
result = {
" opponent " : opponent ,
" target_power " : target ,
" player_power " : round ( adjusted , 1 ) ,
" score " : score ,
" won " : won ,
" reward " : reward ,
}
battles . append (
_battle_record (
state ,
battle_type = " training " ,
player_a = key ,
player_b = " training " ,
result = result ,
)
)
player [ " gold " ] = int ( player [ " gold " ] ) + total_reward
lines . append ( f " 训练赛合计收入 { total_reward } 金币;当前金币 { player [ ' gold ' ] } 。 " )
text = " \n " . join ( lines )
if cursor == 0 :
intro = (
f " 欢迎来到 { state [ ' event_title ' ] } !你们已经到达酒店宾馆, "
f " 训练赛共 { len ( rounds ) } 轮。 训练赛不扣生命,只产出运营金币。 "
)
parts . append ( intro )
stories . append ( _story ( state , " training_open " , intro ) )
hook = _pick_fragment (
" training_hooks " ,
f " { state [ ' run_id ' ] } |training|hook| { round_number } " ,
)
parts . append (
f " 训练赛第 { round_number } / { len ( rounds ) } 轮对阵: "
f " { _training_pairing_line ( state , round_item ) } \n { hook } "
)
for match_index , pair in enumerate ( round_item . get ( " pairs " , [ ] ) , start = 1 ) :
key_a , key_b = str ( pair [ 0 ] ) , str ( pair [ 1 ] )
result = _battle (
state ,
key_a ,
key_b ,
f " { state [ ' run_id ' ] } |training| { round_number } | { match_index } | { event_id } " ,
best_of = 1 ,
)
winner_key = str ( result [ " winner_key " ] )
loser_key = str ( result [ " loser_key " ] )
state [ " players " ] [ winner_key ] [ " gold " ] = (
int ( state [ " players " ] [ winner_key ] [ " gold " ] ) + TRAINING_WIN_GOLD
)
state [ " players " ] [ loser_key ] [ " gold " ] = (
int ( state [ " players " ] [ loser_key ] [ " gold " ] ) + TRAINING_LOSS_GOLD
)
result [ " training_rewards " ] = {
winner_key : TRAINING_WIN_GOLD ,
loser_key : TRAINING_LOSS_GOLD ,
}
battle = _battle_record (
state ,
battle_type = " training_bo1 " ,
player_a = key_a ,
player_b = key_b ,
result = result ,
)
text = _training_match_part ( state , result , round_number )
parts . append ( text )
stories . append ( _story ( state , " training_result " , text , { " person_key " : key } ) )
state [ " phase " ] = " training_manage "
_prepare_new_shops ( state , " training " )
stories . append ( _story ( state , " training_result " , text , result ) )
battles . append ( battle )
bye = str ( round_item . get ( " bye " ) or " " )
if bye :
text = _training_bye_part ( state , bye , round_number )
parts . append ( text )
stories . append ( _story ( state , " training_bye " , text , { " person_key " : bye } ) )
state [ " training_round_cursor " ] = cursor + 1
_prepare_new_shops ( state , f " training- { round_number } " )
_reset_ready ( state , _human_keys ( state ) )
parts . append ( " 训练赛阶段结束。可以 /刷新、/选1~/选6、/上阵、/战术;所有真人发送 /结束回合 后进入小组赛。 " )
if cursor + 1 < len ( rounds ) :
state [ " phase " ] = " training_manage "
parts . append (
" 本轮训练结束。现在是运营窗口:/刷新 是 D 牌,/选1~/选6 是买店内选手, "
" /锁定 可保留商店,/上阵 可调整首发;所有真人发送 /结束回合 后进入下一轮训练。 "
)
else :
state [ " phase " ] = " training_complete_manage "
parts . append (
" 训练赛全部结束。可以最后 D 牌、调阵容;所有真人发送 /结束回合 后进入小组赛。 "
)
return parts , stories , battles
@@ -2120,38 +2579,42 @@ def _series_report_parts(
result : dict [ str , Any ] ,
) - > list [ str ] :
key_a , key_b = match [ " pair " ]
team_a = _team_name ( state [ " players " ] [ key_a ] )
team_b = _team_name ( state [ " players " ] [ key_b ] )
matchup = result [ " matchup " ]
parts = [
f " { _match_title ( state , match ) } 开始。 { team_a } 战力 { result [ ' power_a ' ] : .1f } , { team_b } 战力 { result [ ' power_b ' ] : .1f } 。 "
(
f " { _match_title ( state , match ) } 开始。 "
f " 战术面是 { matchup [ ' tactic_a ' ] } 对 { matchup [ ' tactic_b ' ] } , "
" 本场按逐回合模拟推进。 "
)
]
wins : dict [ str , int ] = { key_a : 0 , key_b : 0 }
for index , item in enumerate ( result [ " maps " ] , start = 1 ) :
winner_key = str ( item [ " winner_key " ] )
loser_key = str ( item [ " loser_key " ] )
wins [ winner_key ] + = 1
actor = _key_player_name ( state , winner_key , index )
closer = _key_player_name ( state , loser_key , index + 2 )
lead_text = (
f " { _team_name ( state [ ' players ' ] [ winner_key ] ) } 在系列赛中 "
f " { wins [ winner_key ] } - { wins [ loser_key ] } { ' 领先 ' if wins [ winner_key ] > wins [ loser_key ] else ' 追平 ' } "
)
overtime = " 常规时间 15-15 后进入加时, " if int ( item [ " overtime_count " ] ) else " "
parts . append (
f " 图 { index } { item [ ' map_name ' ] } : { overtime } { actor } 在关键回合打开局面, "
f " { closer } 的残局反扑没能改写结果。 "
f " 图 { index } { item [ ' map_name ' ] } : { item [ ' opening_text ' ] } "
f " { item [ ' run_text ' ] } { item [ ' clutch_text ' ] } "
f " { item [ ' economy_text ' ] } { item [ ' closer_text ' ] } "
f " { _team_name ( state [ ' players ' ] [ winner_key ] ) } 以 { item [ ' score ' ] } 拿下地图; { lead_text } 。 "
)
winner_key = str ( result [ " winner_key " ] )
loser_key = str ( result [ " loser_key " ] )
reward = 5 if match [ " stage " ] == " group " else 6
state [ " players " ] [ winner_key ] [ " gold " ] = int ( state [ " players " ] [ winner_key ] [ " gold " ] ) + reward
parts . append (
final_text = (
f " 系列赛结束, { _team_name ( state [ ' players ' ] [ winner_key ] ) } 以 { result [ ' score ' ] } 击败 "
f " { _team_name ( state [ ' players ' ] [ loser_key ] ) } ,获得 { reward } 金币。 "
" 可 用于 /刷新 和 /选1~ /选6。"
" 这笔钱可以马上 用于 /刷新、 /选1~ /选6 或锁店等下一轮运营 。"
)
return parts [ : 5 ]
parts [ - 1 ] = parts [ - 1 ] + " \n " + final_text
if len ( parts ) < = 5 :
return parts
return [ parts [ 0 ] , parts [ 1 ] , parts [ 2 ] , " \n " . join ( parts [ 3 : - 1 ] ) , parts [ - 1 ] ]
def _settle_current_match (
@@ -2214,9 +2677,13 @@ def _special_event_parts(
won = power > = target
reward = 4 if won else 1
player [ " gold " ] = int ( player [ " gold " ] ) + reward
hook = _pick_fragment (
" challenges " ,
f " { state [ ' run_id ' ] } | { event_kind } | { event_id } | { key } " ,
)
text = (
f " { _team_name ( player ) } { ' 完成 ' if won else ' 勉强应付 ' } { event_kind } : "
f " 表现 { power : .1f } / { target } , 获得 { reward } 金币。 "
f " { hook } 获得 { reward } 金币。 "
)
parts . append ( text )
stories . append ( _story ( state , " special_event " , text , { " person_key " : key } ) )
@@ -2289,7 +2756,8 @@ class CsMajorAutochess:
" 选秀完成后每队 5 名首发、1 名教练、3 个备战席。 \n "
" /选1~/选6;/阵容;/刷新;/锁定;/上阵 备战编号 [首发位];/出售 备战编号。 "
" /战术 火力|稳定|残局,稳定克火力、火力克残局、残局克稳定。 \n "
" 三场 训练赛后进入 BO3 单循环小组赛;半决赛/决赛 BO5。 "
" 训练赛按参赛人数生成多轮真实对阵,每轮结束后可 D 牌和调阵容; "
" 随后进入 BO3 单循环小组赛,半决赛/决赛 BO5。 "
" 所有需要推进的阶段由相关真人发送 /结束回合,满足条件后自动进入下一步。 "
)
@@ -2373,7 +2841,7 @@ class CsMajorAutochess:
)
season = next_season
state = {
" schema_version " : 2 ,
" schema_version " : 3 ,
" run_id " : f " major_ { season : 04d } " ,
" season_number " : season ,
" event_title " : f " 第 { season } 届 IEM 盐田 Major " ,
@@ -2403,6 +2871,8 @@ class CsMajorAutochess:
" story_sequence " : 0 ,
" battle_sequence " : 0 ,
" special_event_done " : False ,
" training_rounds " : [ ] ,
" training_round_cursor " : 0 ,
}
text = (
f " { state [ ' event_title ' ] } 开放报名;主持人 { display_name } 已自动加入。 "
@@ -2485,7 +2955,7 @@ class CsMajorAutochess:
f " 参赛者: { names } 。 " ,
(
f " 种子顺序: { _seed_lines ( state ) } 。本届采用 { mode } : "
" 先进行三轮选秀,随后训练赛、小组赛 BO3 单循环, "
" 先进行三轮选秀,随后按轮次 训练赛、小组赛 BO3 单循环, "
" 淘汰赛 BO5;单图 MR15,常规时间 15-15 进入加时。 "
) ,
_draft_board_part ( state ) ,
@@ -2667,10 +3137,10 @@ class CsMajorAutochess:
phase = str ( state . get ( " phase " ) )
stories : list [ dict [ str , Any ] ] = [ ]
battles : list [ dict [ str , Any ] ] = [ ]
if phase == " lineup_prep " :
parts , stories , battles = _training_parts ( state , event_id )
if phase in { " lineup_prep " , " training_manage " } :
parts , stories , battles = _settle_training_round ( state , event_id )
return respond ( state , parts , stories = stories , battles = battles )
if phase == " training_manage " :
if phase == " training_complete_manage " :
parts = _start_group_parts ( state )
return respond ( state , parts )
if phase == " group_ready " :