diff --git a/.gitignore b/.gitignore index bb04ebb..df22054 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ __pycache__ .coverage coverage.xml *.egg-info -tools/*.txt \ No newline at end of file +tools/*.txt +.vscode \ No newline at end of file diff --git a/gisim/cards/characters/Anemo/Sucrose.py b/gisim/cards/characters/Anemo/Sucrose.py index 36cabf7..05f6785 100644 --- a/gisim/cards/characters/Anemo/Sucrose.py +++ b/gisim/cards/characters/Anemo/Sucrose.py @@ -1,6 +1,22 @@ """Sucrose""" +from queue import PriorityQueue +from typing import cast + from gisim.cards.characters.base import CharacterCard, CharacterSkill, GenericSkill -from gisim.classes.enums import ElementType, Nation, SkillType, WeaponType +from gisim.classes.enums import ( + AttackType, + CharPos, + ElementalReactionType, + ElementType, + Nation, + SkillType, + WeaponType, +) +from gisim.classes.message import ( + DealDamageMsg, + ElementalReactionTriggeredMsg, + RoundEndMsg, +) from gisim.classes.summon import AttackSummon @@ -39,7 +55,7 @@ class ForbiddenCreationIsomer75TypeII(GenericSkill): Deals 1 Anemo DMG, summons 1 Large Wind Spirit.""" id: int = 15013 - name: str = "Forbidden Creation Isomer 75 Type II" + name: str = "Forbidden Creation Isomer 75 Type II" text: str = """ Deals 1 Anemo DMG, summons 1 Large Wind Spirit. """ @@ -58,6 +74,52 @@ class LargeWindSpirit(AttackSummon): """ name: str = "Large Wind Spirit" + usages: int = 3 + damage_element: ElementType = ElementType.ANEMO + damage_value: int = 2 + element_type_change: bool = False + + def msg_handler(self, msg_queue: PriorityQueue): + msg = msg_queue.queue[0] + if self._uuid in msg.responded_entities: + return False + updated = False + + if isinstance(msg, ElementalReactionTriggeredMsg): + msg = cast(ElementalReactionTriggeredMsg, msg) + source_play_id, source_pos = msg.source + if ( + source_play_id == self.player_id + and not self.element_type_change + and msg.elemental_reaction_type == ElementalReactionType.SWIRL + ): + self.element_type_change = True + self.damage_element = msg.reaction_tuple[1] + updated = True + + if isinstance(msg, RoundEndMsg): + msg = cast(RoundEndMsg, msg) + new_msg = DealDamageMsg( + attack_type=AttackType.SUMMON, + sender_id=self.player_id, + attacker=(self.player_id, CharPos.NONE), + targets=[ + ( + ~self.player_id, + CharPos.ACTIVE, + self.damage_element, + self.damage_value, + ) + ], + ) + msg_queue.put(new_msg) + self.usages -= 1 + if self.usages == 0: + self.active = False + updated = True + if updated: + msg.responded_entities.append(self._uuid) + return updated class Sucrose(CharacterCard): diff --git a/gisim/classes/character.py b/gisim/classes/character.py index 05ad451..8dca672 100644 --- a/gisim/classes/character.py +++ b/gisim/classes/character.py @@ -158,10 +158,14 @@ def msg_handler(self, msg_queue: PriorityQueue): if skill.self_element_attachment is not ElementType.NONE: """Some skills will add elemental attachments to themselves, such as Xingqiu.""" - self.elemental_attachment, reaction_effect = element_reaction( + ( + self.elemental_attachment, + reaction_effect, + reaction_tuple, + ) = element_reaction( self.elemental_attachment, skill.self_element_attachment ) - reaction_effect.to_reaction(msg_queue, parent=self) + reaction_effect.to_reaction(msg_queue, self, reaction_tuple) if skill.accumulate_power and skill.type != SkillType.ELEMENTAL_BURST: self.power = min( @@ -193,11 +197,15 @@ def msg_handler(self, msg_queue: PriorityQueue): and target_pos == CharPos.ACTIVE ): # msg_queue.get() - self.elemental_attachment, reaction_effect = element_reaction( - self.elemental_attachment, element_type - ) + ( + self.elemental_attachment, + reaction_effect, + reaction_tuple, + ) = element_reaction(self.elemental_attachment, element_type) # enumerate 方法迭代出来的值不是实时的数值,请注意 - reaction_effect.to_reaction(msg_queue, parent=self) + reaction_effect.to_reaction( + msg_queue, parent=self, reaction_tuple=reaction_tuple + ) dmg_val = msg.targets[idx][3] # 标志伤害已经计算完毕了,这个消息不应该继续被处理了 msg.damage_calculation_ended = True diff --git a/gisim/classes/message.py b/gisim/classes/message.py index f780386..b1a2501 100644 --- a/gisim/classes/message.py +++ b/gisim/classes/message.py @@ -418,6 +418,7 @@ class ElementalReactionTriggeredMsg(Message): """发生反应的场所""" source: Tuple[PlayerID, CharPos] """来源""" + reaction_tuple: Tuple[ElementType, ElementType] class CharacterDiedMsg(Message): diff --git a/gisim/classes/reaction.py b/gisim/classes/reaction.py index c1e3ee2..19387bf 100644 --- a/gisim/classes/reaction.py +++ b/gisim/classes/reaction.py @@ -1,6 +1,6 @@ import copy from queue import PriorityQueue -from typing import TYPE_CHECKING, List, cast +from typing import TYPE_CHECKING, List, Tuple, cast import numpy as np from pydantic import BaseModel @@ -134,7 +134,12 @@ class Reaction(BaseModel): summon_name: str = "" """生成的召唤物""" - def to_reaction(self, msg_queue: PriorityQueue, parent: "CharacterEntity"): + def to_reaction( + self, + msg_queue: PriorityQueue, + parent: "CharacterEntity", + reaction_tuple: Tuple[ElementType], + ): # sourcery skip: low-code-quality top_msg = msg_queue.queue[0] @@ -149,6 +154,7 @@ def to_reaction(self, msg_queue: PriorityQueue, parent: "CharacterEntity"): elemental_reaction_type=self.reaction_type, target=(parent.player_id, parent.position), source=(parent.player_id, parent.position), + reaction_tuple=reaction_tuple, ) typmsg = "By Self" msg_queue.put(new_msg) @@ -170,6 +176,7 @@ def to_reaction(self, msg_queue: PriorityQueue, parent: "CharacterEntity"): elemental_reaction_type=self.reaction_type, target=(player_id, parent_pos), source=top_msg.attacker, + reaction_tuple=reaction_tuple, ) msg_queue.put(new_msg) typmsg = "From Attack" @@ -376,7 +383,7 @@ def can_attachable(element: ElementType) -> bool: def sum_element_reaction( ElementalAttachment: List[ElementType], AddElement: ElementType -) -> tuple[ElementalReactionType, int]: +) -> Tuple[ElementalReactionType, int]: """计算发生的元素反应""" reaction = RTE[np.ix_(ElementalAttachment, [AddElement])] multiple_reaction = dict(enumerate(np.nditer(reaction), start=0)) @@ -392,12 +399,12 @@ def sum_element_reaction( def element_reaction( ElementalAttachment: List[ElementType], AddElement: ElementType -) -> tuple[list, Reaction]: +) -> Tuple[list, Reaction, Tuple[ElementType]]: """进行元素反应""" ElementalAttachment = copy.deepcopy(ElementalAttachment) cannot_reaction = get_reaction_system_by_type(ElementalReactionType.NONE) if AddElement.value <= 0 or AddElement.value >= 8: - return ElementalAttachment, cannot_reaction + return ElementalAttachment, cannot_reaction, () if ( ElementType.GEO in ElementalAttachment or ElementType.ANEMO in ElementalAttachment @@ -405,27 +412,28 @@ def element_reaction( raise ValueError("There are non attachable elements in the attachment list") if AddElement in ElementalAttachment: # 挂已经附着的元素没有效果 - return ElementalAttachment, cannot_reaction + return ElementalAttachment, cannot_reaction, () attachable = can_attachable(AddElement) if not ElementalAttachment and attachable: # 如果角色没有元素附着,且新挂的元素是可附着元素 ElementalAttachment.append(AddElement) - return ElementalAttachment, cannot_reaction + return ElementalAttachment, cannot_reaction, () if not ElementalAttachment: # 如果角色没有元素附着,且新挂的元素是不可附着元素 - return ElementalAttachment, cannot_reaction + return ElementalAttachment, cannot_reaction, () # 判断元素反应 reaction_type, index = sum_element_reaction(ElementalAttachment, AddElement) + # 获取反应的元素 + reaction_tuple = (AddElement, ElementalAttachment[index]) if reaction_type == ElementalReactionType.NONE: if not attachable: - return ElementalAttachment, cannot_reaction + return ElementalAttachment, cannot_reaction, () ElementalAttachment.append(AddElement) - return ElementalAttachment, cannot_reaction - # 发生了元素反应产生效果 获取效果 + return ElementalAttachment, cannot_reaction, () ElementalAttachment.pop(index) reaction_effect = get_reaction_system_by_type(reaction_type) - return ElementalAttachment, reaction_effect + return ElementalAttachment, reaction_effect, reaction_tuple def get_reaction_system(reaction_name: str): diff --git a/gisim/classes/summon.py b/gisim/classes/summon.py index 2cb5f02..400ff8e 100644 --- a/gisim/classes/summon.py +++ b/gisim/classes/summon.py @@ -65,7 +65,6 @@ def msg_handler(self, msg_queue): self.usages -= 1 if self.usages == 0: self.active = False - msg.responded_entities.append(self._uuid) updated = True if isinstance(msg, RoundEndMsg): @@ -87,6 +86,7 @@ def msg_handler(self, msg_queue): self.usages -= 1 if self.usages == 0: self.active = False - msg.responded_entities.append(self._uuid) updated = True + if updated: + msg.responded_entities.append(self._uuid) return updated diff --git a/gisim/player_area.py b/gisim/player_area.py index a5aaf4a..6f2a150 100644 --- a/gisim/player_area.py +++ b/gisim/player_area.py @@ -21,6 +21,7 @@ ChangeCardsMsg, ChangeDiceMsg, DealDamageMsg, + ElementalReactionTriggeredMsg, GenerateCharacterStatusMsg, GenerateCombatStatusMsg, GenerateEquipmentMsg, @@ -95,8 +96,7 @@ def active_character(self): @property def background_characters(self): - active_pos_val = self.get_active_character_position().value - if active_pos_val: + if active_pos_val := self.get_active_character_position().value: return [ self.character_zones[(active_pos_val + 1) % 3], self.character_zones[(active_pos_val + 2) % 3], @@ -318,12 +318,19 @@ def msg_handler(self, msg_queue: PriorityQueue) -> bool: updated = True msg.responded_entities.append(self._uuid) + # 请求触发召唤物效果 if isinstance(msg, TriggerSummonEffectMsg): for summon in self.summons: if summon.name in msg.summon_list: updated = summon.msg_handler(msg_queue) msg.responded_entities.append(self._uuid) + # 触发元素反应 + if isinstance(msg, ElementalReactionTriggeredMsg): + for summon in self.summons: + updated = summon.msg_handler(msg_queue) + msg.responded_entities.append(self._uuid) + if isinstance(msg, RoundEndMsg): for idx, summon in enumerate(self.summons): updated = summon.msg_handler(msg_queue) @@ -394,9 +401,7 @@ def remove_dice(self, dice_idx: List[int]): def encode(self, viewer_id): return { "length": len(self._dice), - "items": self._dice - if viewer_id == self._parent.player_id or viewer_id == 0 - else None, + "items": self._dice if viewer_id in [self._parent.player_id, 0] else None, } def msg_handler(self, msg_queue: PriorityQueue) -> bool: diff --git a/tests/test_framework_inclube_reaction.py b/tests/test_framework_inclube_reaction.py index f151f99..a194634 100644 --- a/tests/test_framework_inclube_reaction.py +++ b/tests/test_framework_inclube_reaction.py @@ -11,12 +11,12 @@ # } # player2_deck = {"characters": ["Fischl", "Collei", "Xiangling"], "cards": []} player1_deck = { - "characters": ["Kamisato Ayaka", "MaguuKenki", "Xingqiu"], + "characters": ["Kamisato Ayaka", "Sucrose", "Xingqiu"], # "cards": ["Kanten Senmyou Blessing", "Traveler's Handy Sword"], "cards": [], } player2_deck = { - "characters": ["MaguuKenki", "MaguuKenki", "Xingqiu"], + "characters": ["Sucrose", "Sucrose", "Xingqiu"], "cards": [], } game = Game(player1_deck, player2_deck, seed=10)