FimmlpS

文章目录

    时机

    时机

    这一节实在是太复杂了,所以单开。
    TIP:作者也是塔二萌新,如有错误还望指正。

    时序逻辑(?)

    在塔二中,卡牌/遗物/Power继承自AbstractModel类,它提供了统一的时机函数。
    它们统一在{MegaCrit.Sts2.Core.Hooks.Hook}中被封装。
    对于所有监听某个时机的AbstractModel而言,具体调用顺序(CombatState.IterateHookListeners)为:
    玩家:(最多触发玩家数*50条例)

    • Power
    • 遗物
    • 药水(药水槽中的药水)
    • 充能球
    • 卡牌(牌堆顺序为:Hand->DrawPile->DiscardPile->ExhaustPile->PlayPile)
      • 卡牌本身
      • 卡牌痛苦(比如女王限制只能3打1)
      • 卡牌附魔
    • Modifier

    怪物

    • Power
    • 怪物本身(MonsterModel)
    [你也可以看看源代码]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    public IEnumerable<AbstractModel> IterateHookListeners()
    {
    List<AbstractModel> list = new List<AbstractModel>(Players.Count * 50);
    for (int i = 0; i < _allies.Count + _enemies.Count; i++)
    {
    Creature creature = ((i < _allies.Count) ? _allies[i] : _enemies[i - _allies.Count]);
    list.AddRange(creature.Powers);
    Player player = creature.Player;
    if (player == null)
    {
    list.Add(creature.Monster);
    }
    else
    {
    if (!player.IsActiveForHooks)
    {
    continue;
    }
    IReadOnlyList<RelicModel> relics = player.Relics;
    for (int j = 0; j < relics.Count; j++)
    {
    if (!relics[j].IsMelted)
    {
    list.Add(relics[j]);
    }
    }
    IReadOnlyList<PotionModel> potionSlots = player.PotionSlots;
    for (int k = 0; k < potionSlots.Count; k++)
    {
    if (potionSlots[k] != null)
    {
    list.Add(potionSlots[k]);
    }
    }
    if (player.PlayerCombatState == null)
    {
    continue;
    }
    list.AddRange(player.PlayerCombatState.OrbQueue.Orbs);
    IReadOnlyList<CardPile> allPiles = player.PlayerCombatState.AllPiles;
    for (int l = 0; l < allPiles.Count; l++)
    {
    CardPile cardPile = allPiles[l];
    IReadOnlyList<CardModel> cards = cardPile.Cards;
    for (int m = 0; m < cards.Count; m++)
    {
    CardModel cardModel = cards[m];
    list.Add(cardModel);
    if (cardModel.Affliction != null)
    {
    list.Add(cardModel.Affliction);
    }
    if (cardModel.Enchantment != null)
    {
    list.Add(cardModel.Enchantment);
    }
    }
    }
    }
    }
    for (int n = 0; n < Modifiers.Count; n++)
    {
    list.Add(Modifiers[n]);
    }
    if (MultiplayerScalingModel != null)
    {
    list.Add(MultiplayerScalingModel);
    }
    foreach (AbstractModel item in list)
    {
    if (Contains(item))
    {
    yield return item;
    }
    }
    }


    玩家回合开始

    这里按时间顺序列出所有回合开始时的时机函数以及生效对象:(Hook为上方给出的AbstractModel通用时机)

    • Creature.BeforeTurnStart
      当前Side的全体生物(例如玩家阵营/敌方阵营)
    • Hook.BeforeSideTurnStart
      全体AbstractModel
      遗物-破碎核心
    • Creature.PrepareForNextTurn
      仅限玩家的非额外回合,全体敌人
    • Creature.AfterTurnStart
      当前Side的全体生物
    • Hook.AfterBlockCleared
      当前Side的全体生物
      遗物-船甲板
    • CombatManager.SetupPlayerTurn (内含多个时机)
      开启回合的全体玩家(涉及额外回合处理)
      • 重置能量(冰淇淋生效时机)
      • Hook.AfterEnergyReset
        遗物-孙子兵法
      • Hook.BeforeHandDraw
        遗物-忍者卷轴
      • Hook.ModifyHandDraw(在这修改回合开始时的抽牌数)
        遗物-准备背包
      • Hook.AfterModifyingHandDraw
        遗物-怀表(闪烁)
      • (第一回合的固有处理)
      • CardPileCmd.Draw(抽牌)
      • Hook.AfterPlayerTurnStart(这里还有多个时机)
        • AfterPlayerTurnStartEarly
          遗物-历史课
        • AfterPlayerTurnStart
          遗物-选择悖论
        • AfterPlayerTurnStartLate
          遗物-小血瓶
    • Hook.AfterSideTurnStart
      全体AbstractModel
      遗物-大帽子
    • OrbQueue.AfterTurnStart(能量球相关,todo)
      开启回合的全体玩家
    • Hook.BeforePlayPhaseStart
      全体存活的且开启回合的玩家
      遗物-低语耳环(恶魔打牌)

    怪物回合开始

    与玩家回合类似,后半部分不同:

    • Creature.BeforeTurnStart
      当前Side的全体生物(例如玩家阵营/敌方阵营)
    • Hook.BeforeSideTurnStart
      全体AbstractModel
    • Creature.AfterTurnStart
      当前Side的全体生物
    • Hook.AfterBlockCleared
      当前Side的全体生物
    • Hook.AfterSideTurnStart
      全体AbstractModel
    • Action.Invoke
      敌方回合开始时的Action
    • CombatManager.CheckWinCondition(判断胜负)
    • CombatManager.ExecuteEnemyTurn(内含多个时机)
      仅限存活的enemy
      • Creature.TakeTurn
      • CombatManager.CheckWinCondition(判断胜负)
    • CombatManager.EndEnemyTurn

    玩家回合结束

    由CombatManager.AfterAllPlayersReadyToEndTurn执行

    [你也可以看看源代码]
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private async Task AfterAllPlayersReadyToEndTurn(Func<Task>? actionDuringEnemyTurn = null)
    {
    EndingPlayerTurnPhaseOne = true;
    RunManager.Instance.ActionQueueSynchronizer.SetCombatState(ActionSynchronizerCombatState.EndTurnPhaseOne);
    await WaitUntilQueueIsEmptyOrWaitingOnNonPlayerDrivenAction();
    await EndPlayerTurnPhaseOneInternal();
    if (IsInProgress && RunManager.Instance.NetService.Type != NetGameType.Replay)
    {
    RunManager.Instance.ActionQueueSynchronizer.RequestEnqueue(new ReadyToBeginEnemyTurnAction(LocalContext.GetMe(_state), actionDuringEnemyTurn));
    }
    EndingPlayerTurnPhaseOne = false;
    }

    执行顺序:

    • Hook.BeforeTurnEnd
    • CombatManager.DoTurnEnd
      • OrbQueue.BeforeTurnEnd
      • CardCmd.Exhaust (虚无卡牌处理)
        须满足拥有Ethereal-Keyword且Hook.ShouldEtherealTrigger
        虚无牌的卡面上一定要有虚无词条
      • CardPileCmd.Add (回合结束有效果的卡牌处理->放入待执行区)
        须满足CardModel.HasTurnEndInHandEffect为true
      • CardModel.OnTurnEndInHand
      • 分支:
        • CardCmd.Exhaust (回合结束有效果的卡牌的虚无处理)
        • CardPileCmd.Add (回合结束有效果的卡牌处理->放入弃牌区)
    • Hook.BeforeFlush
      结束回合的全体玩家
      • BeforeFlush
        附魔-沉眠精华
      • BeforeFlushLate
        Power-计划妥当