From 5e168f63ba7fe675e970c5c743835c1c1b46df43 Mon Sep 17 00:00:00 2001 From: Flick <79459207+flick-ai@users.noreply.github.com> Date: Sun, 1 Sep 2024 06:52:55 +0800 Subject: [PATCH] try 5.1, not fix --- README.md | 4 +- .../artifact/artifacts/ConductorsTopHat.py | 85 +++++++ .../specialskill/skills/Tepetlisaurus.py | 48 ++++ .../specialskill/skills/Yumkasaurus.py | 2 +- .../card/action/equipment/talent/base.py | 10 +- .../talent/talents/EngulfingStorm.py | 14 + .../talent/talents/InFiveColorsDyed.py | 14 + .../weapon/weapons/PortablePowerSaw.py | 74 ++++++ .../weapon/weapons/ProspectorsDrill.py | 1 + .../country_resonance/Nature_and_Wisdom.py | 6 +- .../card/action/event/events/Blessing.py | 1 + .../action/event/events/EremiteTeatime.py | 99 ++++++++ .../action/event/events/Master_of_Weaponry.py | 1 + .../action/event/events/Sunyata_Flower.py | 6 +- .../card/action/event/events/Toss_up.py | 1 + .../card/action/event/foods/SaurusCrackers.py | 57 +++++ .../card/action/support/companion/Atea.py | 2 +- .../location/Knights_of_Favonius_Library.py | 3 +- .../action/support/location/StageTepetl.py | 66 +++++ .../characters/AbyssHeraldWickedTorrents.py | 7 + .../characters/AbyssLectorFathomlessFlames.py | 37 +-- .../characters/AbyssLectorVioletLightning.py | 143 +++++++++++ .../card/character/characters/Chevreuse.py | 12 +- .../card/character/characters/Chiori.py | 239 ++++++++++++++++++ .../characters/EremiteFloralRingDancer.py | 191 ++++++++++++++ .../characters/EremiteScorchingLoremaster.py | 219 +++++++--------- .../characters/HydroHilichurlRogue.py | 4 +- .../card/character/characters/Xianyun.py | 3 +- genius_invocation/entity/status.py | 2 +- genius_invocation/game/action.py | 32 ++- genius_invocation/game/game.py | 45 ++-- genius_invocation/game/player.py | 22 +- genius_invocation/game/zone.py | 8 +- genius_invocation/main.py | 4 +- genius_invocation/user_layout.py | 41 ++- genius_invocation/utils.py | 13 +- genius_invocation/web/game/game.py | 2 +- 37 files changed, 1320 insertions(+), 198 deletions(-) create mode 100644 genius_invocation/card/action/equipment/artifact/artifacts/ConductorsTopHat.py create mode 100644 genius_invocation/card/action/equipment/specialskill/skills/Tepetlisaurus.py create mode 100644 genius_invocation/card/action/equipment/talent/talents/EngulfingStorm.py create mode 100644 genius_invocation/card/action/equipment/talent/talents/InFiveColorsDyed.py create mode 100644 genius_invocation/card/action/equipment/weapon/weapons/PortablePowerSaw.py create mode 100644 genius_invocation/card/action/event/events/EremiteTeatime.py create mode 100644 genius_invocation/card/action/event/foods/SaurusCrackers.py create mode 100644 genius_invocation/card/action/support/location/StageTepetl.py create mode 100644 genius_invocation/card/character/characters/AbyssLectorVioletLightning.py create mode 100644 genius_invocation/card/character/characters/Chiori.py create mode 100644 genius_invocation/card/character/characters/EremiteFloralRingDancer.py diff --git a/README.md b/README.md index 0f290813..c0b7dee2 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,8 @@ A simulator of the Genius Invokation TCG in Genshin impact - [ ] 完成了强化学习算法的实习和训练 ## 版本更新说明 -4.8版本:为所有内容维护了id信息编码。 -5.0版本:增加特技效果的维护;优化切换角色的代码实现;新增了预览功能 + 4.8版本:为所有内容维护了id信息编码。 + 5.0版本:增加特技效果的维护;优化切换角色的代码实现;新增了预览功能 ## 本地运行 diff --git a/genius_invocation/card/action/equipment/artifact/artifacts/ConductorsTopHat.py b/genius_invocation/card/action/equipment/artifact/artifacts/ConductorsTopHat.py new file mode 100644 index 00000000..5195cce8 --- /dev/null +++ b/genius_invocation/card/action/equipment/artifact/artifacts/ConductorsTopHat.py @@ -0,0 +1,85 @@ +from genius_invocation.utils import * +from genius_invocation.card.action.equipment.artifact.base import ArtifactCard +from genius_invocation.entity.status import Artifact +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from genius_invocation.game.game import GeniusGame + from genius_invocation.game.player import GeniusPlayer + + +class ConductorsTopHatEntity(Artifact): + name: str = "Conductor's Top Hat" + name_ch = "指挥的礼帽" + id = 31203091 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character = None, artifact_card = None): + super().__init__(game, from_player, from_character, artifact_card) + self.round_usage = 1 + self.is_excute = False + + def on_change_character(self, game: 'GeniusGame'): + if game.current_switch.to_character == self.from_character: + if self.round_usage > 0: + self.is_excute = True + player = self.from_character.from_player + max_id = max_count_card(player.hand_zone.card) + player.hand_zone.discard_card(max_id) + num = self.from_player.dice_zone.num() + self.from_player.dice_zone.remove([num-2, num-1]) + self.from_player.dice_zone.add([DiceType.OMNI for _ in range(2)]) + self.round_usage -= 1 + + def on_calculate_dice(self, game: 'GeniusGame'): + if game.active_player_index == self.from_player.index: + if self.is_excute: + if game.current_dice.use_type in SkillType: + if self.round_usage > 0: + if game.current_dice.cost[0]['cost_num'] > 0: + game.current_dice.cost[0]['cost_num'] -= 1 + return True + if len(game.current_dice.cost)>1: + if game.current_dice.cost[1]['cost_num'] > 0: + game.current_dice.cost[1]['cost_num'] -= 1 + return True + if game.current_dice.use_type == ActionCardType.EQUIPMENT_TALENT: + if self.round_usage > 0: + if game.current_dice.cost[0]['cost_num'] > 0: + game.current_dice.cost[0]['cost_num'] -= 1 + return True + if game.current_dice.cost[1]['cost_num'] > 0: + game.current_dice.cost[1]['cost_num'] -= 1 + return True + return False + + def on_use_skill(self, game: 'GeniusGame'): + if self.on_calculate_dice(game): + self.is_excute = False + + def on_play_card(self, game: 'GeniusGame'): + if self.on_calculate_dice(game): + self.is_excute = False + + def update_listener_list(self): + self.listeners = [ + (EventType.ON_PLAY_CARD, ZoneType.CHARACTER_ZONE, self.on_change_character), + (EventType.ON_USE_SKILL, ZoneType.CHARACTER_ZONE, self.on_use_skill), + (EventType.CALCULATE_DICE, ZoneType.CHARACTER_ZONE, self.on_calculate_dice), + (EventType.AFTER_CHANGE_CHARACTER, ZoneType.CHARACTER_ZONE, self.on_change_character), + ] + + +class ConductorsTopHat(ArtifactCard): + id: int = 312030 + name: str = "Marechaussee Hunter" + name_ch = "指挥的礼帽" + time = 5.1 + cost_num: int = 1 + cost_type: CostType = CostType.WHITE + + def __init__(self) -> None: + super().__init__() + self.artifact_entity = ConductorsTopHatEntity + + def on_played(self, game: 'GeniusGame') -> None: + super().on_played(game) + diff --git a/genius_invocation/card/action/equipment/specialskill/skills/Tepetlisaurus.py b/genius_invocation/card/action/equipment/specialskill/skills/Tepetlisaurus.py new file mode 100644 index 00000000..1a2c5d3b --- /dev/null +++ b/genius_invocation/card/action/equipment/specialskill/skills/Tepetlisaurus.py @@ -0,0 +1,48 @@ +from genius_invocation.utils import * +from genius_invocation.card.action.equipment.specialskill.base import SpecialSkillCard +from genius_invocation.entity.status import SpecialSkill, Shield +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from genius_invocation.game.game import GeniusGame + from genius_invocation.game.player import GeniusPlayer + + +class TepetlisaurusEntity(SpecialSkill): + name: str = "Tepetlisaurus" + name_ch = "特佩利龙" + id = "313004s1" + cost = [{'cost_num': 2, 'cost_type': CostType.BLACK}] + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character = None): + super().__init__(game, from_player, from_character) + self.usage = 2 + + def on_call(self, game: 'GeniusGame') -> None: + super().on_call(game) + self.from_player.get_card(num=2) + num = 0 + for card in self.from_player.hand_zone.card: + card_name = card.name + if card_name not in self.from_player.card_zone.card_name: + num += 1 + self.from_character.character_zone.add_entity(Shield( + game, self.from_player, self.from_character, usage=num, + )) + game.manager.invoke(EventType.AFTER_USE_SPECIAL, game) + + + +class Tepetlisaurus(SpecialSkillCard): + id: int = 313004 + name: str = "Koholasaurus" + name_ch = "嵴锋龙" + time: float = 5.1 + cost_num: int = 2 + cost_type: CostType = CostType.WHITE + + def __init__(self) -> None: + super().__init__() + self.equipment_entity = TepetlisaurusEntity + + def on_played(self, game: 'GeniusGame') -> None: + super().on_played(game) diff --git a/genius_invocation/card/action/equipment/specialskill/skills/Yumkasaurus.py b/genius_invocation/card/action/equipment/specialskill/skills/Yumkasaurus.py index f924494d..76cc5af1 100644 --- a/genius_invocation/card/action/equipment/specialskill/skills/Yumkasaurus.py +++ b/genius_invocation/card/action/equipment/specialskill/skills/Yumkasaurus.py @@ -40,7 +40,7 @@ def on_call(self, game: 'GeniusGame'): def on_calculate_dice(self, game: 'GeniusGame'): if game.active_player_index == self.from_player.index: if game.current_dice.from_character == self.from_character: - if game.current_dice.use_type == SkillType.SPECIAL_SKILL: + if game.current_dice.use_type == SpecialSkillType.SPECIAL_SKILL: if len(self.from_player.hand_zone.card) <= 2: if game.current_dice.cost[0]['cost_num'] > 0: game.current_dice.cost[0]['cost_num'] -= 1 diff --git a/genius_invocation/card/action/equipment/talent/base.py b/genius_invocation/card/action/equipment/talent/base.py index 37c6398a..cb5c2963 100644 --- a/genius_invocation/card/action/equipment/talent/base.py +++ b/genius_invocation/card/action/equipment/talent/base.py @@ -20,12 +20,14 @@ class TalentCard(EquipmentCard): cost: list[dict] def __init__(self) -> None: super().__init__() + self.from_character = None for i in range(len(self.cost)): self.cost[i]['cost_type'] = CostType(self.cost[i]['cost_type']) def on_played(self, game: 'GeniusGame') -> None: target_character = game.active_player.character_list[game.current_action.target_idx] target_character.equip_talent(game, self.is_action, self) + self.from_character = target_character def find_target(self, game: 'GeniusGame'): if not self.is_action: @@ -43,4 +45,10 @@ def count_cost(self): if self.is_equip: return sum([cost['cost_num'] for cost in self.cost]) else: - return 0 \ No newline at end of file + return 0 + + def on_destroy(self, game): + game.current_remove_from = self.from_character + game.manager.invoke(EventType.ON_EQUIP_REMOVE, game) + game.current_remove_from = None + self.from_character.character_zone.talent_card = None \ No newline at end of file diff --git a/genius_invocation/card/action/equipment/talent/talents/EngulfingStorm.py b/genius_invocation/card/action/equipment/talent/talents/EngulfingStorm.py new file mode 100644 index 00000000..f1ab96ce --- /dev/null +++ b/genius_invocation/card/action/equipment/talent/talents/EngulfingStorm.py @@ -0,0 +1,14 @@ +from genius_invocation.card.action.equipment.talent.import_head import * +from genius_invocation.card.character.characters.AbyssLectorVioletLightning import * + +class EngulfingStorm(TalentCard): + id: int = 224061 + name: str = "Surging Undercurrent" + name_ch = "侵雷重闪" + time = 5.1 + is_action = False + cost = [{'cost_num': 1, 'cost_type': CostType.ELECTRO.value}] + cost_power = 0 + character = AbyssLectorVioletLightning + def __init__(self) -> None: + super().__init__() diff --git a/genius_invocation/card/action/equipment/talent/talents/InFiveColorsDyed.py b/genius_invocation/card/action/equipment/talent/talents/InFiveColorsDyed.py new file mode 100644 index 00000000..09267998 --- /dev/null +++ b/genius_invocation/card/action/equipment/talent/talents/InFiveColorsDyed.py @@ -0,0 +1,14 @@ +from genius_invocation.card.action.equipment.talent.import_head import * +from genius_invocation.card.character.characters.Chiori import * + +class InFiveColorsDyed(TalentCard): + id: int = 216091 + name: str = "In Five Colors Dyed" + name_ch = "落染五色" + time = 5.1 + is_action = True + cost = [{'cost_num': 3, 'cost_type': CostType.GEO.value}] + cost_power = 0 + character = Chiori + def __init__(self) -> None: + super().__init__() diff --git a/genius_invocation/card/action/equipment/weapon/weapons/PortablePowerSaw.py b/genius_invocation/card/action/equipment/weapon/weapons/PortablePowerSaw.py new file mode 100644 index 00000000..573759b9 --- /dev/null +++ b/genius_invocation/card/action/equipment/weapon/weapons/PortablePowerSaw.py @@ -0,0 +1,74 @@ +from genius_invocation.utils import * +from genius_invocation.card.action.equipment.weapon.base import WeaponCard +from typing import TYPE_CHECKING +from genius_invocation.entity.status import Weapon + +if TYPE_CHECKING: + from genius_invocation.game.game import GeniusGame + + +class PortablePowerSawWeapon(Weapon): + id: int = 31130981 + name: str = "Portable Power Saw" + name_ch = "便携动力锯" + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character = None, weapon_card = None): + super().__init__(game, from_player, from_character, weapon_card) + self.round_usage = 1 + self.stoicssymbol = 0 + self.add_damage = False + + def on_damage_execute(self, game: 'GeniusGame'): + if self.round_usage <=0 : + return + if game.current_damage.damage_to == self.from_character: + if game.current_damage.main_damage_element == ElementType.PIERCING: + return + if game.current_damage.main_damage > 0: + max_id = max_count_card() + if max_id == None: + return + else: + game.current_damage.main_damage -= 1 + self.stoicssymbol += 1 + self.round_usage -= 1 + + def on_add_damage(self, game:'GeniusGame'): + if self.add_damage: + game.current_damage.main_damage += 1 + self.add_damage = False + + def after_use_skill(self, game: 'GeniusGame'): + if self.add_damage: + self.add_damage = False + + def on_use_skill(self, game: 'GeniusGame'): + if game.current_skill.from_character == self.from_character: + if self.stoicssymbol > 0: + self.add_damage = True + self.from_player.get_card(num=self.stoicssymbol) + self.stoicssymbol = 0 + + def update_listener_list(self): + self.listeners = [ + (EventType.FINAL_EXECUTE, ZoneType.CHARACTER_ZONE, self.on_damage_execute), + (EventType.DAMAGE_ADD, ZoneType.CHARACTER_ZONE, self.on_add_damage), + (EventType.AFTER_USE_SKILL, ZoneType.CHARACTER_ZONE, self.after_use_skill), + (EventType.ON_USE_SKILL, ZoneType.CHARACTER_ZONE, self.on_use_skill) + ] + + +class PortablePowerSaw(WeaponCard): + id: int = 311309 + name: str = "Portable Power Saw" + name_ch = "便携动力锯" + time = 5.1 + weapon_type: WeaponType = WeaponType.CLAYMORE + cost_num: int = 2 + cost_type: CostType = CostType.WHITE + + def __init__(self) -> None: + super().__init__() + self.equipment_entity = PortablePowerSawWeapon + + def on_played(self, game: 'GeniusGame') -> None: + super().on_played(game) \ No newline at end of file diff --git a/genius_invocation/card/action/equipment/weapon/weapons/ProspectorsDrill.py b/genius_invocation/card/action/equipment/weapon/weapons/ProspectorsDrill.py index 55efff67..a95998a4 100644 --- a/genius_invocation/card/action/equipment/weapon/weapons/ProspectorsDrill.py +++ b/genius_invocation/card/action/equipment/weapon/weapons/ProspectorsDrill.py @@ -29,6 +29,7 @@ def on_damage_execute(self, game: 'GeniusGame'): return else: game.current_damage.main_damage -= 1 + self.solidarity += 1 self.round_usage -= 1 def on_add_damage(self, game:'GeniusGame'): diff --git a/genius_invocation/card/action/event/country_resonance/Nature_and_Wisdom.py b/genius_invocation/card/action/event/country_resonance/Nature_and_Wisdom.py index bdb952b0..cb95cd42 100644 --- a/genius_invocation/card/action/event/country_resonance/Nature_and_Wisdom.py +++ b/genius_invocation/card/action/event/country_resonance/Nature_and_Wisdom.py @@ -19,11 +19,13 @@ def __init__(self) -> None: self.now_phase: GamePhase def on_played(self, game: 'GeniusGame'): + game.active_player.get_card(num=1) self.now_phase = game.game_phase game.game_phase = GamePhase.SET_CARD game.special_phase = self - game.active_player.get_card(num=1) + def on_finished(self, game: 'GeniusGame'): game.game_phase = self.now_phase - game.special_phase = None \ No newline at end of file + game.special_phase = None + game.resolve_action(None) \ No newline at end of file diff --git a/genius_invocation/card/action/event/events/Blessing.py b/genius_invocation/card/action/event/events/Blessing.py index 7e3b4f5d..6f0e0891 100644 --- a/genius_invocation/card/action/event/events/Blessing.py +++ b/genius_invocation/card/action/event/events/Blessing.py @@ -36,6 +36,7 @@ def on_finished(self, game: 'GeniusGame'): game.game_phase = self.now_phase game.special_phase = None + game.resolve_action(None) def find_target(self, game:'GeniusGame'): target = [] diff --git a/genius_invocation/card/action/event/events/EremiteTeatime.py b/genius_invocation/card/action/event/events/EremiteTeatime.py new file mode 100644 index 00000000..f78151a9 --- /dev/null +++ b/genius_invocation/card/action/event/events/EremiteTeatime.py @@ -0,0 +1,99 @@ +from genius_invocation.card.action.base import ActionCard +from genius_invocation.utils import * +import os + +if TYPE_CHECKING: + from genius_invocation.game.game import GeniusGame + + +class EremiteTeatime(ActionCard): + id = 332040 + name: str = "Eremite Teatime" + name_ch = '镀金旅团的茶歇' + time = 5.1 + cost_num = 2 + cost_type = CostType.WHITE + card_type = ActionCardType.EVENT + + def __init__(self) -> None: + super().__init__() + cards_dirs = ["./card/action/support/item", + "./card/action/support/location", + "./card/action/event/foods"] + self.items = [] + available_name = [f[:-3] for f in os.listdir(cards_dirs[0]) if f.endswith(".py") and f != "__init__.py" and f != "import_head.py"] + self.items.extend(available_name) + + self.locations = [] + available_name = [f[:-3] for f in os.listdir(cards_dirs[1]) if f.endswith(".py") and f != "__init__.py" and f != "import_head.py"] + self.locations.extend(available_name) + + self.foods = [] + available_name = [f[:-3] for f in os.listdir(cards_dirs[2]) if f.endswith(".py") and f != "__init__.py" and f != "import_head.py"] + self.foods.extend(available_name) + + def on_played(self, game: 'GeniusGame'): + same_element = {} + same_country = {} + same_weapon = {} + for character in game.active_player.character_list: + same_element[character.element] = same_element.get(character.element, 0) + 1 + same_weapon[character.weapon_type] = same_weapon.get(character.weapon_type, 0) + 1 + if hasattr(character, 'country_list'): + for country in character.country_list: + same_country[country] = same_country.get(country, 0) + 1 + else: + same_country[character.country] = same_country.get(character.country, 0) + 1 + + self.location = [] + for element in same_element.keys(): + if same_element[element] >= 2: + for i in range(3): + self.location.append(random.choice(self.locations)) + break + + self.item = [] + for country in same_country.keys(): + if same_country[country] >= 2: + for i in range(3): + self.item.append(random.choice(self.items)) + break + + self.food = [] + for weapon in same_weapon.keys(): + if same_weapon[weapon] >= 2: + for i in range(3): + self.food.append(random.choice(self.foods)) + break + + self.get_list = [] + if self.location != []: + self.get_list.append(self.location) + if self.item != []: + self.get_list.append(self.item) + if self.food != []: + self.get_list.append(self.food) + if self.get_list == []: + return + + self.now_phase = game.game_phase + game.game_phase = GamePhase.SELECT + game.special_phase = self + game.active_player.select_list = self.get_list.pop(0) + game.active_player.select_num = 1 + + def find_target(self, game:'GeniusGame'): + return [1] + + def on_finished(self, game: 'GeniusGame'): + select_list = game.active_player.select_list + result = game.active_player.select_result + game.active_player.hand_zone.add_card_by_name([select_list[result[0]]]) + if len(self.get_list) > 0: + game.active_player.select_list = self.get_list.pop(0) + else: + game.game_phase = self.now_phase + game.special_phase = None + game.active_player.select_list = None + game.active_player.select_num = 0 + game.active_player.select_result = None diff --git a/genius_invocation/card/action/event/events/Master_of_Weaponry.py b/genius_invocation/card/action/event/events/Master_of_Weaponry.py index d4e9f0f5..cae54ce7 100644 --- a/genius_invocation/card/action/event/events/Master_of_Weaponry.py +++ b/genius_invocation/card/action/event/events/Master_of_Weaponry.py @@ -36,6 +36,7 @@ def on_finished(self, game: 'GeniusGame'): game.game_phase = self.now_phase game.special_phase = None + game.resolve_action(None) def find_target(self, game:'GeniusGame'): target = [] diff --git a/genius_invocation/card/action/event/events/Sunyata_Flower.py b/genius_invocation/card/action/event/events/Sunyata_Flower.py index 72a2bc7d..07cde036 100644 --- a/genius_invocation/card/action/event/events/Sunyata_Flower.py +++ b/genius_invocation/card/action/event/events/Sunyata_Flower.py @@ -62,8 +62,10 @@ def __init__(self) -> None: def on_played(self, game: 'GeniusGame'): target = game.current_action.target_idx game.active_player.support_zone.destroy_by_idx(target) - card_name = random.choice(self.cards) - game.active_player.hand_zone.add_card_by_name(card_name) + cards = [] + for i in range(2): + cards.append(random.choice(self.cards)) + game.active_player.hand_zone.add_card_by_name(cards) def find_target(self, game: 'GeniusGame'): if game.active_player.support_zone.num() > 0: diff --git a/genius_invocation/card/action/event/events/Toss_up.py b/genius_invocation/card/action/event/events/Toss_up.py index 3760a3b9..277ad9d9 100644 --- a/genius_invocation/card/action/event/events/Toss_up.py +++ b/genius_invocation/card/action/event/events/Toss_up.py @@ -25,6 +25,7 @@ def on_played(self, game: 'GeniusGame'): def on_finished(self, game: 'GeniusGame'): game.game_phase = self.now_phase game.special_phase = None + game.resolve_action(None) def find_target(self, game:'GeniusGame'): if game.active_player.dice_zone.num()>0: diff --git a/genius_invocation/card/action/event/foods/SaurusCrackers.py b/genius_invocation/card/action/event/foods/SaurusCrackers.py new file mode 100644 index 00000000..9b865879 --- /dev/null +++ b/genius_invocation/card/action/event/foods/SaurusCrackers.py @@ -0,0 +1,57 @@ +from genius_invocation.card.action.base import ActionCard +from genius_invocation.utils import * +from genius_invocation.card.action.event.base import FoodCard +from genius_invocation.entity.status import Status +if TYPE_CHECKING: + from genius_invocation.game.game import GeniusGame + from genius_invocation.game.player import GeniusPlayer + +class SaurusCrackersEntity(Status): + id: int = 33301621 + name: str = "Saurus Crackers" + name_ch = "龙龙饼干" + + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + self.current_usage = 1 + + def on_calculate(self, game: 'GeniusGame'): + if game.active_player_index == self.from_player.index: + if game.current_dice.from_character == self.from_character: + if game.current_dice.use_type == SpecialSkillType.SPECIAL_SKILL: + if game.current_dice.cost[0]['cost_num'] > 0: + game.current_dice.cost[0]['cost_num'] -= 1 + return True + return False + + def on_special_skill(self, game: 'GeniusGame'): + if self.on_calculate(game): + self.current_usage -= 1 + if self.current_usage <= 0: + self.on_destroy(game) + + def on_end(self, game: 'GeniusGame'): + self.on_destroy(game) + + def update_listener_list(self): + self.listeners = [ + (EventType.ON_USE_SPECIAL, ZoneType.CHARACTER_ZONE, self.on_special_skill), + (EventType.CALCULATE_DICE, ZoneType.CHARACTER_ZONE, self.on_calculate), + (EventType.FINAL_END, ZoneType.CHARACTER_ZONE, self.on_end) + ] + +class SaurusCrackers(FoodCard): + id: int = 333016 + name: str = "Saurus Crackers" + name_ch = "龙龙饼干" + time = 5.1 + cost_num = 0 + cost_type = None + + def __init__(self) -> None: + super().__init__() + self.food_entity = SaurusCrackersEntity + + def on_played(self, game: 'GeniusGame'): + super().on_played(game) + diff --git a/genius_invocation/card/action/support/companion/Atea.py b/genius_invocation/card/action/support/companion/Atea.py index 2e10f701..6294a17a 100644 --- a/genius_invocation/card/action/support/companion/Atea.py +++ b/genius_invocation/card/action/support/companion/Atea.py @@ -23,7 +23,7 @@ def on_begin(self, game:'GeniusGame'): def on_calculate(self, game:'GeniusGame'): if game.active_player_index == self.from_player.index: - if game.current_dice.use_type == SkillType.SPECIAL_SKILL: + if game.current_dice.use_type == SpecialSkillType.SPECIAL_SKILL: if self.usage > 0: if game.current_dice.cost[0]['cost_num'] > 0: game.current_dice.cost[0]['cost_num'] -= 1 diff --git a/genius_invocation/card/action/support/location/Knights_of_Favonius_Library.py b/genius_invocation/card/action/support/location/Knights_of_Favonius_Library.py index 61c2ee4c..34dd301d 100644 --- a/genius_invocation/card/action/support/location/Knights_of_Favonius_Library.py +++ b/genius_invocation/card/action/support/location/Knights_of_Favonius_Library.py @@ -55,7 +55,8 @@ def on_played(self, game: 'GeniusGame') -> None: def on_finished(self, game: 'GeniusGame'): game.game_phase = self.now_phase game.special_phase = None - + game.resolve_action(None) + @staticmethod def balance_adjustment(): log = {} diff --git a/genius_invocation/card/action/support/location/StageTepetl.py b/genius_invocation/card/action/support/location/StageTepetl.py new file mode 100644 index 00000000..568e2bee --- /dev/null +++ b/genius_invocation/card/action/support/location/StageTepetl.py @@ -0,0 +1,66 @@ +from genius_invocation.utils import * +from genius_invocation.card.action.support.base import SupportCard +from typing import TYPE_CHECKING +from genius_invocation.entity.support import Support + +if TYPE_CHECKING: + from genius_invocation.game.game import GeniusGame + from genius_invocation.game.player import GeniusPlayer + + +class StageTepetlEntity(Support): + name = 'Stage Tepetl' + name_ch = '特佩利舞台' + id = 32102361 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + self.attention = 0 + + def after_use_skill(self, game: 'GeniusGame'): + if game.active_player_index == self.from_player.index: + self.attention += 1 + else: + self.attention -= 1 + + def after_use_special(self, game: 'GeniusGame'): + if game.active_player_index == self.from_player.index: + self.attention += 1 + else: + self.attention -= 1 + + def on_begin(self, game: 'GeniusGame'): + if game.active_player_index == self.from_player.index: + if self.attention >= 1: + num = self.from_player.dice_zone.num() + self.from_player.dice_zone.remove([num-1]) + self.from_player.dice_zone.add([DiceType.OMNI for _ in range(1)]) + if self.attention >= 3: + dice = self.from_player.roll_dice(num=1, is_basic=True) + self.from_player.dice_zone.add(dice) + + def update_listener_list(self): + self.listeners = [ + (EventType.BEGIN_ACTION_PHASE, ZoneType.SUPPORT_ZONE, self.on_begin), + (EventType.AFTER_USE_SKILL, ZoneType.SUPPORT_ZONE, self.after_use_skill), + (EventType.AFTER_USE_SPECIAL, ZoneType.SUPPORT_ZONE, self.after_use_special), + ] + + def show(self): + return str(self.attention) + +class StageTepetl(SupportCard): + id: int = 321023 + name = 'Stage Tepetl' + name_ch = '特佩利舞台' + time = 5.1 + cost_num = 0 + cost_type = None + card_type = ActionCardType.SUPPORT_LOCATION + + def __init__(self) -> None: + super().__init__() + self.entity = None + + def on_played(self, game: 'GeniusGame') -> None: + self.entity = StageTepetlEntity(game, from_player=game.active_player) + super().on_played(game) \ No newline at end of file diff --git a/genius_invocation/card/character/characters/AbyssHeraldWickedTorrents.py b/genius_invocation/card/character/characters/AbyssHeraldWickedTorrents.py index 985d06d1..133976fa 100644 --- a/genius_invocation/card/character/characters/AbyssHeraldWickedTorrents.py +++ b/genius_invocation/card/character/characters/AbyssHeraldWickedTorrents.py @@ -250,3 +250,10 @@ def equip_talent(self, game:'GeniusGame', is_action=True, talent_card=None): if self.character_zone.has_entity(WateryRebirth) is None: target_zone = get_opponent(game).team_combat_status target_zone.add_entity(CurseoftheUndercurrent(game, from_player=get_opponent(game), from_character=None)) + self.listen_event(game, EventType.CHARACTER_WILL_DIE, ZoneType.CHARACTER_ZONE, self.on_character_die) + + def on_character_die(self, game: 'GeniusGame'): + if not self.from_character.is_alive: + if self.from_character.talent: + target_zone = get_opponent(game).team_combat_status + target_zone.add_entity(CurseoftheUndercurrent(game, from_player=get_opponent(game), from_character=None)) \ No newline at end of file diff --git a/genius_invocation/card/character/characters/AbyssLectorFathomlessFlames.py b/genius_invocation/card/character/characters/AbyssLectorFathomlessFlames.py index 1018255b..be6d798a 100644 --- a/genius_invocation/card/character/characters/AbyssLectorFathomlessFlames.py +++ b/genius_invocation/card/character/characters/AbyssLectorFathomlessFlames.py @@ -113,6 +113,8 @@ def on_character_die(self, game: 'GeniusGame'): if self.set_talent: shield = Aegis_of_Abyssal_Flame(game, self.from_player, self.from_character) self.from_character.character_zone.add_entity(shield) + self.from_character.talent = False + self.from_character.character_zone.talent_card.on_destroy(game) self.on_destroy(game) def update_listener_list(self): @@ -126,15 +128,24 @@ class Aegis_of_Abyssal_Flame(Shield): id = 230241 def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character: 'Character'): super().__init__(game, from_player, from_character) - self.current_usage = 3 - self.usage = 3 - def dmg_add(self, game:'GeniusGame'): - if game.current_damage.damage_from == self.from_character: - assert game.current_damage.main_damage_element == ElementType.PYRO - game.current_damage.main_damage += 1 - def update_listener_list(self): - super().update_listener_list() - self.listeners.append((EventType.DAMAGE_ADD, ZoneType.CHARACTER_ZONE, self.dmg_add)) + self.current_usage = 2 + self.usage = 2 + + def on_destroy(self, game: 'GeniusGame'): + oppenent = game.players[1-self.from_player.index] + for character in oppenent.character_list: + if character.is_alive: + dmg = Damage.create_damage( + game, + damage_type=SkillType.OTHER, + main_damage_element=ElementType.PIERCING, + main_damage=1, + piercing_damage=0, + damage_from=self, + damage_to=character) + game.add_damage(dmg) + game.resolve_damage() + super().on_destroy(game) class AbyssLectorFathomlessFlames(Character): id: int = 2302 @@ -168,17 +179,11 @@ def __init__(self, game: 'GeniusGame', zone: 'CharacterZone', from_player: 'Geni def on_dmg_add(self, game: 'GeniusGame'): if game.current_damage.damage_from == self: - if game.current_damage.main_damage_element == ElementType.HYDRO: + if game.current_damage.main_damage_element == ElementType.PYRO: game.current_damage.main_damage += 1 - def infusion(self, game:'GeniusGame'): - if self.from_character == game.current_damage.damage_from: - if game.current_damage.main_damage_element == ElementType.PHYSICAL: - game.current_damage.main_damage_element = ElementType.HYDRO - def revive_event(self, game: 'GeniusGame'): self.listen_event(game, EventType.DAMAGE_ADD, ZoneType.CHARACTER_ZONE, self.on_dmg_add) - self.listen_event(game, EventType.INFUSION, ZoneType.CHARACTER_ZONE, self.infusion) def equip_talent(self, game:'GeniusGame', is_action=True, talent_card=None): self.talent = True diff --git a/genius_invocation/card/character/characters/AbyssLectorVioletLightning.py b/genius_invocation/card/character/characters/AbyssLectorVioletLightning.py new file mode 100644 index 00000000..2e3f014c --- /dev/null +++ b/genius_invocation/card/character/characters/AbyssLectorVioletLightning.py @@ -0,0 +1,143 @@ +from genius_invocation.card.character.import_head import * + +class DenofThunder(NormalAttack): + name = "Den of Thunder" + name_ch = "渊薮落雷" + id = 240601 + type: SkillType = SkillType.NORMAL_ATTACK + damage_type: SkillType = SkillType.NORMAL_ATTACK + main_damage_element: ElementType = ElementType.ELECTRO + main_damage: int = 1 + piercing_damage: int = 0 + cost = [{'cost_num':1, 'cost_type':CostType.ELECTRO}, {'cost_num':2, "cost_type":CostType.BLACK}] + energy_cost: int = 0 + energy_gain: int = 1 + + def on_call(self, game: 'GeniusGame'): + super().on_call(game) + self.resolve_damage(game) + self.gain_energy(game) + game.manager.invoke(EventType.AFTER_USE_SKILL, game) + + +class ShockoftheEnigmaticAbyss(ElementalSkill): + name = "Shock of the Enigmatic Abyss" + name_ch = "秘渊虚霆" + id = 240602 + type: SkillType = SkillType.ELEMENTAL_SKILL + damage_type: SkillType = SkillType.ELEMENTAL_SKILL + main_damage_element: ElementType = ElementType.ELECTRO + main_damage: int = 3 + piercing_damage: int = 0 + cost = [{'cost_num':3, 'cost_type':CostType.ELECTRO}] + energy_cost: int = 0 + energy_gain: int = 1 + + def on_call(self, game: 'GeniusGame'): + super().on_call(game) + target = get_opponent_active_character(game) + self.resolve_damage(game) + self.gain_energy(game) + if target.elemental_application == ElementType.ELECTRO: + target.loose_power(1) + self.from_character.get_power(1) + game.manager.invoke(EventType.AFTER_USE_SKILL, game) + +class WildThunderburst(ElementalBurst): + name = "Wild Thunderburst" + name_ch = "狂迸骇雷" + id = 240603 + type: SkillType = SkillType.ELEMENTAL_BURST + damage_type: SkillType = SkillType.ELEMENTAL_BURST + main_damage_element: ElementType = ElementType.ELECTRO + main_damage: int = 3 + piercing_damage: int = 0 + cost = [{'cost_num':3, 'cost_type':CostType.ELECTRO}] + energy_cost: int = 2 + energy_gain: int = 0 + + def on_call(self, game: 'GeniusGame'): + super().on_call(game) + self.consume_energy(game) + add_damage = 0 + if get_opponent_active_character(game).power <= 1: + add_damage = 2 + self.resolve_damage(game, add_main_damage=add_damage) + game.manager.invoke(EventType.AFTER_USE_SKILL, game) + +class ElectricRebirth(Status): + name = "Electric Rebirth" + name_ch = "雷之新生" + id = 240621 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character: 'Character'): + super().__init__(game, from_player, from_character) + self.current_usage = 1 + self.set_talent = False + + def on_character_die(self, game: 'GeniusGame'): + ''' + 角色死亡时 + ''' + if not self.from_character.is_alive: + self.from_character.is_alive = True + self.from_character.health_point = 0 + self.from_character.heal(4, game, heal_type=HealType.REVIVE) + self.from_character.revive_event(game) + if self.set_talent: + target = get_active_character(game, 1-self.from_player.index) + target.loose_power(1) + self.from_character.talent = False + self.from_character.character_zone.talent_card.on_destroy(game) + self.on_destroy(game) + + def update_listener_list(self): + self.listeners = [ + (EventType.CHARACTER_WILL_DIE, ZoneType.CHARACTER_ZONE, self.on_character_die) + ] + +class AbyssLectorVioletLightning(Character): + id: int = 2406 + name: str = "Abyss Lector - Violet Lightning" + name_ch = "深渊咏者·紫电" + time = 5.1 + element: ElementType = ElementType.ELECTRO + weapon_type: WeaponType = WeaponType.OTHER + country: CountryType = CountryType.MONSTER + init_health_point: int = 6 + max_health_point: int = 6 + skill_list: List = [DenofThunder, ShockoftheEnigmaticAbyss, WildThunderburst] + max_power: int = 2 + + def init_state(self, game: 'GeniusGame'): + rebirth = ElectricRebirth(game, self.from_player, self) + self.character_zone.add_entity(rebirth) + + def __init__(self, game: 'GeniusGame', zone: 'CharacterZone', from_player: 'GeniusPlayer', index: int, from_character=None, talent=False): + super().__init__(game, zone, from_player, index, from_character) + self.power = 0 + self.talent = talent + if self.talent: + self.character_zone.has_entity(ElectricRebirth).set_talent = True + + def on_dmg_add(self, game: 'GeniusGame'): + if game.current_damage.damage_from == self: + if game.current_damage.main_damage_element == ElementType.ELECTRO: + game.current_damage.main_damage += 1 + + def revive_event(self, game: 'GeniusGame'): + self.listen_event(game, EventType.DAMAGE_ADD, ZoneType.CHARACTER_ZONE, self.on_dmg_add) + + def equip_talent(self, game:'GeniusGame', is_action=True, talent_card=None): + self.talent = True + self.character_zone.talent_card = talent_card + if self.character_zone.has_entity(ElectricRebirth) is None: + target = get_opponent_active_character(game) + target.loose_power(1) + self.listen_event(game, EventType.CHARACTER_WILL_DIE, ZoneType.CHARACTER_ZONE, self.on_character_die) + + def on_character_die(self, game: 'GeniusGame'): + if not self.from_character.is_alive: + if self.from_character.talent: + target = get_opponent_active_character(game) + target.loose_power(1) + diff --git a/genius_invocation/card/character/characters/Chevreuse.py b/genius_invocation/card/character/characters/Chevreuse.py index 01ba74f8..6dbe25c0 100644 --- a/genius_invocation/card/character/characters/Chevreuse.py +++ b/genius_invocation/card/character/characters/Chevreuse.py @@ -46,7 +46,9 @@ class VerticalForceCoordination(CharacterSkill): name = 'Vertical Force Coordination' name_ch = '纵阵武力统筹' def on_call(self, game:'GeniusGame'): - self.from_character.from_player.hand_zone.add([OverchargedBall()]) + if self.passive_round_usage > 0: + self.passive_round_usage -= 1 + self.from_character.from_player.hand_zone.add([OverchargedBall()]) class LineBayonetThrustEX(NormalAttack): id: int = 131301 @@ -231,6 +233,7 @@ class Chevreuse(Character): max_power: int = 2 def init_state(self, game: 'GeniusGame'): + self.listen_event(game, EventType.BEGIN_ACTION_PHASE, ZoneType.CHARACTER_ZONE, self.on_begin) self.listen_event(game, EventType.DAMAGE_ADD_AFTER_REACTION, ZoneType.CHARACTER_ZONE, self.on_reaction) def __init__(self, game: 'GeniusGame', zone: 'CharacterZone', from_player: 'GeniusPlayer', index:int, from_character = None, talent = False): @@ -239,11 +242,16 @@ def __init__(self, game: 'GeniusGame', zone: 'CharacterZone', from_player: 'Geni self.power = 0 self.talent_skill = None self.passive_skill = VerticalForceCoordination(self) + self.passive_round_usage = 1 + + def on_begin(self, game: 'GeniusGame'): + if game.active_player_index == self.from_player.index: + self.passive_round_usage = 1 def on_reaction(self, game: 'GeniusGame'): if game.current_damage.damage_to.from_player.index == 1 - self.from_player.index: if game.current_damage.reaction == ElementalReactionType.Overloaded: - self.passive_skill.on_reaction(game) + self.passive_skill.on_call(game) if self.talent: self.from_player.team_combat_status.add_entity(PyroQuill(game, from_player=self.from_player, from_character=self)) diff --git a/genius_invocation/card/character/characters/Chiori.py b/genius_invocation/card/character/characters/Chiori.py new file mode 100644 index 00000000..92aef240 --- /dev/null +++ b/genius_invocation/card/character/characters/Chiori.py @@ -0,0 +1,239 @@ +from genius_invocation.card.character.import_head import * + +class WeavingBlade(NormalAttack): + name = "Weaving Blade" + name_ch = "心织刀流" + id = 160901 + type: SkillType = SkillType.NORMAL_ATTACK + damage_type: SkillType = SkillType.NORMAL_ATTACK + main_damage_element: ElementType = ElementType.PHYSICAL + main_damage: int = 2 + piercing_damage: int = 0 + cost = [{'cost_num':1, 'cost_type':CostType.GEO}, {'cost_num':2, "cost_type":CostType.BLACK}] + energy_cost: int = 0 + energy_gain: int = 1 + + def on_call(self, game: 'GeniusGame'): + super().on_call(game) + self.resolve_damage(game) + self.gain_energy(game) + game.manager.invoke(EventType.AFTER_USE_SKILL, game) + + +class FlutteringHasode(ElementalSkill): + name = "Fluttering Hasode" + name_ch = "羽袖一触" + id = 160902 + type: SkillType = SkillType.ELEMENTAL_SKILL + damage_type: SkillType = SkillType.ELEMENTAL_SKILL + main_damage_element: ElementType = ElementType.GEO + main_damage: int = 0 + piercing_damage: int = 0 + cost = [{'cost_num':3, 'cost_type':CostType.GEO}] + energy_cost: int = 0 + energy_gain: int = 1 + + def on_call(self, game: 'GeniusGame'): + super().on_call(game) + num = 4 if self.from_character.talent else 3 + self.from_character.from_player.select_list = random.sample([Doll1, Doll2, Doll3, Doll4, Doll5, Doll6], num) + self.from_character.from_player.select_num = 1 + + self.now_phase = game.game_phase + game.game_phase = GamePhase.SELECT + game.special_phase = self + + def on_finished(self, game: 'GeniusGame'): + game.game_phase = self.now_phase + game.special_phase = None + select_list = game.active_player.select_list + game.active_player.select_list = None + game.active_player.select_num = 0 + result = game.active_player.select_result + game.active_player.select_result = None + + self.generate_summon(game, select_list[result[0]]) + if self.from_character.talent: + self.generate_summon(game, Doll3) + self.gain_energy(game) + game.manager.invoke(EventType.AFTER_USE_SKILL, game) + game.current_skill = None + game.resolve_action(None) + +class HiyokuTwinBlades(ElementalBurst): + name = "Hiyoku: Twin Blades" + name_ch = "二刀之形·比翼" + id = 160903 + type: SkillType = SkillType.ELEMENTAL_BURST + damage_type: SkillType = SkillType.ELEMENTAL_BURST + main_damage_element: ElementType = ElementType.GEO + main_damage: int = 5 + piercing_damage: int = 0 + cost = [{'cost_num':3, 'cost_type':CostType.GEO}] + energy_cost: int = 2 + energy_gain: int = 0 + + def on_call(self, game: 'GeniusGame'): + super().on_call(game) + self.consume_energy(game) + self.resolve_damage(game) + game.manager.invoke(EventType.AFTER_USE_SKILL, game) + +class ChiorisAutomatonDolls(Summon): + name: str = "Chiori's Automaton Dolls" + name_ch = "千织的自动制御人形" + element: ElementType = ElementType.GEO + removable = True + id = 160910 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + self.usage: int = 2 + self.current_usage: int = 2 + + def on_end_phase(self, game: 'GeniusGame'): + if game.active_player == self.from_player: + dmg = Damage.create_damage( + game, + damage_type=SkillType.SUMMON, + main_damage_element=ElementType.GEO, + main_damage=1, + piercing_damage=0, + damage_from=self, + damage_to=get_opponent_active_character(game), + ) + game.add_damage(dmg) + game.resolve_damage() + + def update_listener_list(self): + self.listeners = [ + (EventType.END_PHASE, ZoneType.SUMMON_ZONE, self.on_end_phase), + ] + +class Doll1(ChiorisAutomatonDolls): + name: str = "Doll 1" + name_ch = "不悦挥刀之袖" + id = 160911 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + self.listen_event(game, EventType.INFUSION, ZoneType.SUMMON_ZONE, self.on_infusion) + + def on_infusion(self, game: 'GeniusGame'): + if game.current_damage.damage_from == self.from_character: + if game.current_damage.main_damage_element == ElementType.PHYSICAL: + game.current_damage.main_damage_element = ElementType.GEO + +class Doll2(ChiorisAutomatonDolls): + name: str = "Doll 2" + name_ch = "无事发生之袖" + id = 160912 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + self.round_usage = 1 + self.listen_event(game, EventType.AFTER_USE_SKILL, ZoneType.SUMMON_ZONE, self.after_use_skill) + + def after_use_skill(self, game: 'GeniusGame'): + if self.round_usage <= 0: + return + if game.active_player == self.from_player: + self.from_player.change_to_next_character() + self.round_usage -= 1 + +class Doll3(ChiorisAutomatonDolls): + name: str = "Doll 3" + name_ch = "平静养神之袖" + id = 160913 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + +class Doll4(ChiorisAutomatonDolls): + name: str = "Doll 4" + name_ch = "轻松迎敌之袖" + id = 160914 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + self.round_usage = 1 + self.listen_event(game, EventType.AFTER_USE_SKILL, ZoneType.SUMMON_ZONE, self.after_use_skill) + + def after_use_skill(self, game: 'GeniusGame'): + if self.round_usage <= 0: + return + if game.active_player == self.from_player: + if game.current_skill.from_character != self.from_character: + dmg = Damage.create_damage( + game, + damage_type=SkillType.SUMMON, + main_damage_element=ElementType.GEO, + main_damage=1, + piercing_damage=0, + damage_from=self, + damage_to=get_opponent_active_character(game), + ) + game.add_damage(dmg) + game.resolve_damage() + self.round_usage -= 1 + +class Doll5(ChiorisAutomatonDolls): + name: str = "Doll 5" + name_ch = "闭目战斗之袖" + id = 160915 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + self.round_usage = 2 + self.listen_event(game, EventType.DAMAGE_ADD, ZoneType.SUMMON_ZONE, self.on_add_damage) + + def on_add_damage(self, game: 'GeniusGame'): + if self.round_usage <= 0: + return + if game.current_damage.damage_from == self.from_character: + if game.current_damage.main_damage_element == ElementType.GEO: + game.current_damage.main_damage += 1 + self.round_usage -= 1 + if isinstance(game.current_damage.damage_from, ChiorisAutomatonDolls): + if game.current_damage.main_damage_element == ElementType.GEO: + game.current_damage.main_damage += 1 + self.round_usage -= 1 + +class Doll6(ChiorisAutomatonDolls): + name: str = "Doll 6" + name_ch = "侧目睥睨之袖" + id = 160916 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + self.round_usage = 1 + self.listen_event(game, EventType.CALCULATE_DICE, ZoneType.SUMMON_ZONE, self.on_calculate_dice) + self.listen_event(game, EventType.ON_USE_SKILL, ZoneType.SUMMON_ZONE, self.on_use_skill) + + def on_calculate_dice(self, game: 'GeniusGame'): + if game.current_dice.from_character == self.from_character: + if game.current_dice.use_type == SkillType.NORMAL_ATTACK: + if game.current_dice.cost[0]['cost_num'] > 0: + game.current_dice.cost[0]['cost_num'] -= 1 + return True + elif game.current_dice.cost[1]['cost_num'] > 0: + game.current_dice.cost[1]['cost_num'] -= 1 + return True + return False + + def on_use_skill(self, game: 'GeniusGame'): + if self.on_calculate_dice(game): + self.round_usage -= 1 + +class Chiori(Character): + id: int = 1609 + name: str = "Chiori" + name_ch = "千织" + time = 5.1 + element: ElementType = ElementType.GEO + weapon_type: WeaponType = WeaponType.SWORD + country: CountryType = CountryType.INAZUMA + init_health_point: int = 10 + max_health_point: int = 10 + skill_list: List = [WeavingBlade, FlutteringHasode, HiyokuTwinBlades] + max_power: int = 2 + + def __init__(self, game: 'GeniusGame', zone: 'CharacterZone', from_player: 'GeniusPlayer', index: int, from_character=None, talent=False): + super().__init__(game, zone, from_player, index, from_character) + self.power = 0 + self.talent = talent + self.talent_skill = self.skills[1] + diff --git a/genius_invocation/card/character/characters/EremiteFloralRingDancer.py b/genius_invocation/card/character/characters/EremiteFloralRingDancer.py new file mode 100644 index 00000000..f990adb2 --- /dev/null +++ b/genius_invocation/card/character/characters/EremiteFloralRingDancer.py @@ -0,0 +1,191 @@ +from genius_invocation.card.character.import_head import * +from genius_invocation.card.character.characters.EremiteScorchingLoremaster import SpiritofOmensPower +from genius_invocation.card.action.equipment.specialskill import SpecialSkillCard +from genius_invocation.entity.status import Frozen_Status, SpecialSkill + + +class SpiritSerpentsBlessing(Combat_Status): + name = "Spirit Serpent's Blessing" + name_ch = "灵蛇祝福" + id = 270331 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + self.usage = 1 + + def update(self, usage:int = 1): + self.usage += usage + + def on_add_damage(self, game: 'GeniusGame'): + if game.current_skill.from_character == self.from_character: + if game.current_skill.name == "Viny Razorscale": + game.current_damage.main_damage += 1 + self.usage -= 1 + if self.usage <= 0: + self.on_destroy(game) + + def update_listener_list(self): + self.listeners = [ + (EventType.DAMAGE_ADD, ZoneType.ACTIVE_ZONE, self.on_add_damage), + ] + +class VinyRazorscale(SpecialSkill): + name: str = "Viny Razorscale" + name_ch = "藤蔓锋鳞" + id = "2703s1" + cost = [{'cost_num': 1,'cost_type': CostType.WHITE}] + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character = None): + super().__init__(game, from_player, from_character) + self.usage = 2 + + def on_call(self, game: 'GeniusGame'): + if self.from_player.team_combat_status.has_status(SpiritSerpentsBlessing): + not_use = True + damage = Damage(damage_type=None, + main_damage_element=ElementType.DENDRO, + main_damage=1, + piercing_damage=0, + damage_from=self, + damage_to=get_opponent_active_character(game)) + game.add_damage(damage) + game.resolve_damage() + if not not_use: + self.check_usage(game) + game.manager.invoke(EventType.AFTER_USE_SPECIAL, game) + + +class SpiritofOmenDendroSpiritSerpent(SpecialSkillCard): + id: int = 270371 + name: str = "Spirit of Omen: Dendro Spirit Serpent" + name_ch = "厄灵·草之灵蛇" + time: float = 5.1 + cost_num: int = 0 + cost_type: CostType = None + def __init__(self) -> None: + super().__init__() + self.equipment_entity = VinyRazorscale + + def on_played(self, game: 'GeniusGame') -> None: + super().on_played(game) + +class FloralRingCaress(NormalAttack): + name = "Floral Ring Caress" + name_ch = "叶轮轻扫" + id = 270301 + type: SkillType = SkillType.NORMAL_ATTACK + damage_type: SkillType = SkillType.NORMAL_ATTACK + main_damage_element: ElementType = ElementType.PHYSICAL + main_damage: int = 2 + piercing_damage: int = 0 + cost = [{'cost_num':1, 'cost_type':CostType.DENDRO}, {'cost_num':2, "cost_type":CostType.BLACK}] + energy_cost: int = 0 + energy_gain: int = 1 + + def on_call(self, game: 'GeniusGame'): + super().on_call(game) + self.resolve_damage(game) + self.gain_energy(game) + game.manager.invoke(EventType.AFTER_USE_SKILL, game) + + +class SpiralingWhirl(ElementalSkill): + name = "Spiraling Whirl" + name_ch = "蔓延旋舞" + id = 270302 + type: SkillType = SkillType.ELEMENTAL_SKILL + damage_type: SkillType = SkillType.ELEMENTAL_SKILL + main_damage_element: ElementType = ElementType.DENDRO + main_damage: int = 3 + piercing_damage: int = 0 + cost = [{'cost_num':3, 'cost_type':CostType.DENDRO}] + energy_cost: int = 0 + energy_gain: int = 1 + + def on_call(self, game: 'GeniusGame'): + super().on_call(game) + self.resolve_damage(game) + self.gain_energy(game) + self.add_combat_status(game, SpiritSerpentsBlessing) + game.manager.invoke(EventType.AFTER_USE_SKILL, game) + +class SpiritofOmensAwakeningDendroSpiritSerpent(ElementalBurst): + name = "Spirit of Omen's Awakening: Dendro Spirit Serpent" + name_ch = "厄灵苏醒·草之灵蛇" + id = 270304 + type: SkillType = SkillType.ELEMENTAL_BURST + damage_type: SkillType = SkillType.ELEMENTAL_BURST + main_damage_element: ElementType = ElementType.DENDRO + main_damage: int = 4 + piercing_damage: int = 0 + cost = [{'cost_num':3, 'cost_type':CostType.DENDRO}] + energy_cost: int = 2 + energy_gain: int = 0 + + def on_call(self, game: 'GeniusGame'): + super().on_call(game) + self.consume_energy(game) + self.resolve_damage(game) + if not self.from_character.has_brust: + self.from_character.from_player.hand_zone.add([SpiritofOmenDendroSpiritSerpent()]) + game.manager.invoke(EventType.AFTER_USE_SKILL, game) + + +class EremiteFloralRingDancer(Character): + id: int = 2703 + name: str = "Eremite Floral Ring Dancer" + name_ch = "镀金旅团·叶轮舞者" + time = 5.1 + element: ElementType = ElementType.DENDRO + weapon_type: WeaponType = WeaponType.OTHER + country: CountryType = CountryType.EREMITES + init_health_point: int = 10 + max_health_point: int = 10 + skill_list: List = [FloralRingCaress, SpiralingWhirl, SpiritofOmensAwakeningDendroSpiritSerpent] + max_power: int = 2 + + def init_state(self, game: 'GeniusGame'): + self.listen_event(game, EventType.BEGIN_ACTION_PHASE, ZoneType.CHARACTER_ZONE, self.on_begin) + self.listen_event(game, EventType.FINAL_EXECUTE, ZoneType.CHARACTER_ZONE, self.on_excute_damage) + + def __init__(self, game: 'GeniusGame', zone: 'CharacterZone', from_player: 'GeniusPlayer', index: int, from_character=None, talent=False): + super().__init__(game, zone, from_player, index, from_character) + self.power = 0 + self.talent = talent + self.talent_skill = self.skills[2] + self.talent_usage = 1 + + self.passive_skill = SpiritofOmensPower(self) + self.passive_round_usage = 1 + + self.has_brust = False + + + def on_begin(self, game: 'GeniusGame'): + if game.active_player_index == self.from_player.index: + self.passive_round_usage = 1 + if self.talent: + self.talent_usage = 1 + + def on_excute_damage(self, game: 'GeniusGame'): + if game.current_damage.damage_to == self: + self.passive_skill.on_call(game) + + def listen_talent_events(self, game: 'GeniusGame'): + self.listen_event(game, EventType.AFTER_CHANGE_CHARACTER, ZoneType.CHARACTER_ZONE, self.on_change_character) + + def on_change_character(self, game: 'GeniusGame'): + if game.current_switch.to_character.from_player == self.from_player: + if isinstance(game.current_switch.to_character.character_zone.special_skill, VinyRazorscale): + if self.talent: + if self.talent_usage > 0: + self.talent_usage -= 1 + dmg = Damage( + damage_type=None, + main_damage_element=ElementType.DENDRO, + main_damage=1, + piercing_damage=0, + damage_from=None, + damage_to=get_opponent_active_character(game) + ) + game.add_damage(dmg) + game.resolve_damage() + diff --git a/genius_invocation/card/character/characters/EremiteScorchingLoremaster.py b/genius_invocation/card/character/characters/EremiteScorchingLoremaster.py index 5c9cbab8..e64ba9bf 100644 --- a/genius_invocation/card/character/characters/EremiteScorchingLoremaster.py +++ b/genius_invocation/card/character/characters/EremiteScorchingLoremaster.py @@ -1,4 +1,65 @@ from genius_invocation.card.character.import_head import * +from genius_invocation.card.action.equipment.specialskill import SpecialSkillCard +from genius_invocation.entity.status import Frozen_Status, SpecialSkill + +class ScorpionBlessing(Combat_Status): + name = "Scorpion Blessing" + name_ch = "魔蝎祝福" + id = 230331 + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character=None): + super().__init__(game, from_player, from_character) + self.usage = 1 + + def update(self, usage:int = 1): + self.usage += usage + + def on_add_damage(self, game: 'GeniusGame'): + if game.current_skill.from_character == self.from_character: + if game.current_skill.name == "Viny Razorscale": + game.current_damage.main_damage += 1 + self.usage -= 1 + if self.usage <= 0: + self.on_destroy(game) + + def update_listener_list(self): + self.listeners = [ + (EventType.DAMAGE_ADD, ZoneType.ACTIVE_ZONE, self.on_add_damage), + ] + +class BurningAssault(SpecialSkill): + name: str = "Burning Assault" + name_ch = "炙烧攻势" + id = "2303s1" + cost = [{'cost_num': 2,'cost_type': CostType.WHITE}] + def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character = None): + super().__init__(game, from_player, from_character) + self.usage = 2 + + def on_call(self, game: 'GeniusGame'): + damage = Damage(damage_type=None, + main_damage_element=ElementType.PYRO, + main_damage=2, + piercing_damage=0, + damage_from=self, + damage_to=get_opponent_active_character(game)) + game.add_damage(damage) + game.resolve_damage() + self.check_usage(game) + game.manager.invoke(EventType.AFTER_USE_SPECIAL, game) + +class SpiritofOmenPyroScorpion(SpecialSkillCard): + id: int = 230371 + name: str = "Spirit of Omen: Pyro Scorpion" + name_ch = "厄灵·炎之魔蝎" + time: float = 5.1 + cost_num: int = 0 + cost_type: CostType = None + def __init__(self) -> None: + super().__init__() + self.equipment_entity = BurningAssault + + def on_played(self, game: 'GeniusGame') -> None: + super().on_played(game) class SearingGlare(NormalAttack): name = "Searing Glare" @@ -37,16 +98,17 @@ def on_call(self, game: 'GeniusGame'): super().on_call(game) self.resolve_damage(game) self.gain_energy(game) + self.add_combat_status(game, ScorpionBlessing) game.manager.invoke(EventType.AFTER_USE_SKILL, game) -class Spirit_of_Omens_Awakening_Pyro_Scorpion(ElementalBurst): +class SpiritofOmensAwakeningPyroScorpion(ElementalBurst): name = "Spirit of Omen's Awakening: Pyro Scorpion" name_ch = "厄灵苏醒·炎之魔蝎" id = 230303 type: SkillType = SkillType.ELEMENTAL_BURST damage_type: SkillType = SkillType.ELEMENTAL_BURST main_damage_element: ElementType = ElementType.PYRO - main_damage: int = 2 + main_damage: int = 3 piercing_damage: int = 0 cost = [{'cost_num':3, 'cost_type':CostType.PYRO}] energy_cost: int = 2 @@ -56,136 +118,18 @@ def on_call(self, game: 'GeniusGame'): super().on_call(game) self.consume_energy(game) self.resolve_damage(game) - self.generate_summon(game, Spirit_of_Omen_Pyro_Scorpion) - game.manager.invoke(EventType.AFTER_USE_SKILL, game) - - -class Spirit_of_Omen_Pyro_Scorpion(Summon): - name = " Spirit of Omen: Pyro Scorpion" - name_ch = "厄灵·炎之魔蝎" - removable = True - element = ElementType.PYRO - id = 230311 - def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character: 'Character'): - super().__init__(game, from_player, from_character) - self.current_usage = 2 - self.usage = 2 - status = self.from_character.character_zone.has_entity(Pyro_Scorpion_Guardian_Stance) - if status is None: - status = Pyro_Scorpion_Guardian_Stance(game, self.from_player, self.from_character, self) - self.from_character.character_zone.add_entity(status) - else: - status.update() - - def on_end_phase(self, game: 'GeniusGame'): - if game.active_player == self.from_player: - add_dmg = 0 - if self.from_character.talent and self.from_character.skills[0].usage_this_round + self.from_character.skills[1].usage_this_round >0: - add_dmg = 1 - dmg = Damage.create_damage( - game, - SkillType.SUMMON, - main_damage_element=self.element, - main_damage= 1 + add_dmg, - piercing_damage=0, - damage_from=self, - damage_to = get_opponent_active_character(game), - ) - game.add_damage(dmg) - game.resolve_damage() - self.current_usage -= 1 - if self.current_usage <= 0: - self.on_destroy(game) - - def begin_round(self, game: 'GeniusGame'): - status = self.from_character.character_zone.has_entity(Pyro_Scorpion_Guardian_Stance) - if status is None: - status = Pyro_Scorpion_Guardian_Stance(game, self.from_player, self.from_character, self) - self.from_character.character_zone.add_entity(status) - else: - status.update() - - - def update(self, game:'GeniusGame'): - self.current_usage = max(self.current_usage, self.usage) - status = self.from_character.character_zone.has_entity(Pyro_Scorpion_Guardian_Stance) - if status is None: - status = Pyro_Scorpion_Guardian_Stance(game, self.from_player, self.from_character, self) - self.from_character.character_zone.add_entity(status) - else: - status.update() - - def update_listener_list(self): - self.listeners = [ - (EventType.END_PHASE, ZoneType.SUMMON_ZONE, self.on_end_phase), - (EventType.BEGIN_ACTION_PHASE, ZoneType.SUMMON_ZONE, self.begin_round) - ] - - def on_destroy(self, game: 'GeniusGame'): - status = self.from_character.character_zone.has_entity(Pyro_Scorpion_Guardian_Stance) - if status is not None: - status.on_destroy(game) - super().on_destroy(game) - + game.manager.invoke(EventType.AFTER_USE_SKILL, game) -class Pyro_Scorpion_Guardian_Stance(Status): - name = 'Pyro Scorpion: Guardian Stance' - nane_ch = '炎之魔蝎·守势' - id = 230321 - def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character = None, from_summon:'Summon' = None): - super().__init__(game, from_player, from_character) - # USAGE SHOULD ALWAYS 1 - self.from_summon = from_summon - - if self.from_character.talent: - self.current_usage = 2 - self.usage = 2 - else: - self.current_usage = 1 - self.usage = 1 - - def on_execute_dmg(self, game:"GeniusGame"): - if game.current_damage.damage_to != self.from_character: return - if game.current_damage.main_damage <= 0: return - if game.current_damage.main_damage_element == ElementType.PIERCING: return - game.current_damage.main_damage -= 1 - self.current_usage -= 1 - if self.current_usage <=0: - self.on_destroy(game) - def update_listener_list(self): - self.listeners = [ - (EventType.EXECUTE_DAMAGE, ZoneType.CHARACTER_ZONE, self.on_execute_dmg) - ] - def update(self): - if self.from_character.talent: - self.current_usage = 2 - self.usage = 2 - else: - self.current_usage = 1 - self.usage = 1 - -class Spirit_of_Omens_Power(Status): +class SpiritofOmensPower(CharacterSkill): name = "Spirit of Omen's Power" name_ch = "厄灵之能" - id = 230322 - - def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character = None): - super().__init__(game, from_player, from_character) - # USAGE SHOULD ALWAYS 1 - self.current_usage = 1 - self.usage = 1 - - def on_final_dmg(self, game:'GeniusGame'): - if self.from_character.health_point<=7: + id = 230304 + def on_call(self, game: 'GeniusGame'): + if self.from_character.health_point <= 7 and self.from_character.passive_round_usage > 0: + self.from_character.passive_round_usage -= 1 self.from_character.get_power(1) - self.on_destroy(game) - - def update_listener_list(self): - self.listeners = [ - (EventType.FINAL_EXECUTE, ZoneType.CHARACTER_ZONE, self.on_final_dmg) - ] class EremiteScorchingLoremaster(Character): id: int = 2303 @@ -197,19 +141,34 @@ class EremiteScorchingLoremaster(Character): country: CountryType = CountryType.EREMITES init_health_point: int = 10 max_health_point: int = 10 - skill_list: List = [SearingGlare, BlazingStrike, Spirit_of_Omens_Awakening_Pyro_Scorpion] + skill_list: List = [SearingGlare, BlazingStrike, SpiritofOmensAwakeningPyroScorpion] max_power: int = 2 def init_state(self, game: 'GeniusGame'): - status = Spirit_of_Omens_Power(game, self.from_player, self) - self.character_zone.add_entity(status) - + self.listen_event(game, EventType.BEGIN_ACTION_PHASE, ZoneType.CHARACTER_ZONE, self.on_begin) + self.listen_event(game, EventType.FINAL_EXECUTE, ZoneType.CHARACTER_ZONE, self.on_excute_damage) def __init__(self, game: 'GeniusGame', zone: 'CharacterZone', from_player: 'GeniusPlayer', index: int, from_character=None, talent=False): super().__init__(game, zone, from_player, index, from_character) self.power = 0 self.talent = talent self.talent_skill = self.skills[2] + self.passive_skill = SpiritofOmensPower(self) + self.passive_round_usage = 1 + + def on_begin(self, game: 'GeniusGame'): + if game.active_player_index == self.from_player.index: + self.passive_round_usage = 1 + + def on_excute_damage(self, game: 'GeniusGame'): + if game.current_damage.damage_to == self: + self.passive_skill.on_call(game) + + def balance_adjustment(): + log = {} + log[5.1] = "重做,类似叶轮舞者" + return log + diff --git a/genius_invocation/card/character/characters/HydroHilichurlRogue.py b/genius_invocation/card/character/characters/HydroHilichurlRogue.py index fd897451..8de3b767 100644 --- a/genius_invocation/card/character/characters/HydroHilichurlRogue.py +++ b/genius_invocation/card/character/characters/HydroHilichurlRogue.py @@ -75,7 +75,7 @@ def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_charact def on_call(self, game: 'GeniusGame'): opponent = get_opponent_active_character(game) - damage = Damage(damage_type=SkillType.SPECIAL_SKILL, + damage = Damage(damage_type=SpecialSkillType.SPECIAL_SKILL, main_damage_element=ElementType.HYDRO, main_damage=1, piercing_damage=0, @@ -229,7 +229,7 @@ def listen_talent_events(self, game: 'GeniusGame'): def on_calculate(self, game:'GeniusGame'): if game.active_player_index == self.from_player.index: - if game.current_dice.use_type == SkillType.SPECIAL_SKILL: + if game.current_dice.use_type == SpecialSkillType.SPECIAL_SKILL: if self.talent_round != game.round: if game.current_dice.cost[0]['cost_num'] > 0: game.current_dice.cost[0]['cost_num'] -= 1 diff --git a/genius_invocation/card/character/characters/Xianyun.py b/genius_invocation/card/character/characters/Xianyun.py index 367d3f0d..2ae814c8 100644 --- a/genius_invocation/card/character/characters/Xianyun.py +++ b/genius_invocation/card/character/characters/Xianyun.py @@ -1,6 +1,6 @@ from genius_invocation.card.character.import_head import * from genius_invocation.card.action.equipment.specialskill import SpecialSkillCard -from genius_invocation.entity.status import Frozen_Status, SpecialSkill +from genius_invocation.entity.status import SpecialSkill class DriftcloudWave(Status): name = 'Driftcloud Wave' @@ -69,6 +69,7 @@ class AdeptalAssistance(SpecialSkill): name: str = "Adeptal Assistance" name_ch = "仙力助推" id = "1510s1" + cost = [{'cost_num': 1,'cost_type': CostType.WHITE}] def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character = None): super().__init__(game, from_player, from_character) self.usage = 2 diff --git a/genius_invocation/entity/status.py b/genius_invocation/entity/status.py index fa505215..759629ef 100644 --- a/genius_invocation/entity/status.py +++ b/genius_invocation/entity/status.py @@ -166,7 +166,7 @@ def count_cost(self): return self.artifact_card.cost_num class SpecialSkill(Equipment): - type = SkillType.SPECIAL_SKILL + type = SpecialSkillType.SPECIAL_SKILL cost = [{'cost_num': 0, 'cost_type': CostType.BLACK}] def __init__(self, game: 'GeniusGame', from_player: 'GeniusPlayer', from_character: "Character"= None, card: 'SpecialSkillCard' = None): super().__init__(game, from_player, from_character) diff --git a/genius_invocation/game/action.py b/genius_invocation/game/action.py index 3ec962da..3fe3847e 100644 --- a/genius_invocation/game/action.py +++ b/genius_invocation/game/action.py @@ -17,7 +17,8 @@ 15: pass this turn 16: dice region 17: card region - 18: special action + 18: special skill + 19: select ''' # 16: none (to solve problem caused by "Toss-up" and "Nature and Wisdom" and so on) @@ -30,6 +31,7 @@ 9-12: my support region 13: dice region 14: card region + 15: select region ''' ''' @@ -78,6 +80,9 @@ def set_type(self) -> None: elif self.choice == 18: self.choice_type = ActionChoice.CHARACTER_SKILL self.choice_idx = self.choice + elif self.choice == 19: + self.choice_type = ActionChoice.SELECT + self.choice_idx = self.choice if self.target == 0: self.target_type = ActionTarget.OPPONENT @@ -100,6 +105,9 @@ def set_type(self) -> None: elif self.target == 14: self.target_type = ActionTarget.CARD_REGION self.target_idx = -1 + elif self.target == 15: + self.target_type = ActionTarget.SELECT_REGION + self.target_idx = -1 @staticmethod def from_tuple(action: tuple): @@ -145,7 +153,8 @@ def from_layout(game: 'GeniusGame', layout, jump=True): 15:'结束回合', 16:'选择操作本方骰子', 17:'选择操作本方手牌', - 18:'使用特技'} + 18:'使用特技', + 19:'进行选择'} history.append(f"您是{game.active_player_index}号玩家") choose_prompt = f"以下是你可以选择的行动,请输入一个数字表示你的行动选择(按确认以提交或清空选择):\n" choose_list = [] @@ -183,7 +192,8 @@ def from_layout(game: 'GeniusGame', layout, jump=True): 11:'选择2号支援', 12:'选择3号支援', 13:'选择操作本方骰子', - 14:'选择操作本方手牌'} + 14:'选择操作本方手牌', + 15:'进行选择'} target_prompt = '根据您选择的行动,您可以选择以下目标\n' target_list = [] last_target = -1 @@ -207,6 +217,20 @@ def from_layout(game: 'GeniusGame', layout, jump=True): elif choice == 17: list_prompt = f'您需要选择重新获取的手牌的位置,形式如0 1 2所示,数值应该在{0}-{use_dice[choice][target][0]-1}之间:' dice = user_input.get_special_rng_mul_sel(list_prompt, min=0, max=use_dice[choice][target][0]-1) + elif choice == 19: + list_prompt = f'您需要选择需要的卡牌,形式如0 1 2所示,选择的数量为{use_dice[choice][target][1]}:' + while True: + try: + print_prompt(layout, history, list_prompt) + dice = user_input.get_rng_mul_sel( + '', min=0, max=game.active_player.dice_zone.num()-1, + assert_fn=lambda x: len(x)==use_dice[choice][target][1]) + assert not check_duplicate_dice(dice) + break + except KeyboardInterrupt: + exit() + except: + print(layout, "您选择的骰子包含重复位置,非法,请重新选择") elif use_dice.sum() == 0: dice = [] else: # what is this? @@ -244,7 +268,7 @@ def from_layout(game: 'GeniusGame', layout, jump=True): game.active_player_index = true_active_player_index action = Action(choice, target, dice) - if game.game_phase == GamePhase.ACTION_PHASE: + if not jump and game.game_phase == GamePhase.ACTION_PHASE: copy_game = game.copy_game() copy_game.step(action) old_dict = game.encoder_dict() diff --git a/genius_invocation/game/game.py b/genius_invocation/game/game.py index efd17155..b8a896fd 100644 --- a/genius_invocation/game/game.py +++ b/genius_invocation/game/game.py @@ -167,19 +167,23 @@ def resolve_action(self, action: 'Action'): self.current_action = action opponent_player = self.players[1 - self.active_player_index] active_player = self.active_player - - if action.choice_type == ActionChoice.HAND_CARD: - self.is_change_player = False - active_player.play_card(self) - elif action.choice_type == ActionChoice.CHARACTER_SKILL: - self.is_change_player = True - active_player.use_skill(self) - elif action.choice_type == ActionChoice.CHANGE_CHARACTER: - self.is_change_player = True - active_player.change_character(self) - elif action.choice_type == ActionChoice.PASS: - self.is_change_player = True - active_player.is_pass = True + + if action is not None: + if action.choice_type == ActionChoice.HAND_CARD: + self.is_change_player = False + active_player.play_card(self) + elif action.choice_type == ActionChoice.CHARACTER_SKILL: + self.is_change_player = True + active_player.use_skill(self) + elif action.choice_type == ActionChoice.CHANGE_CHARACTER: + self.is_change_player = True + active_player.change_character(self) + elif action.choice_type == ActionChoice.PASS: + self.is_change_player = True + active_player.is_pass = True + + if self.special_phase is not None and self.game_phase != GamePhase.ACTION_PHASE: + return self.manager.invoke(EventType.AFTER_ANY_ACTION, self) self.reset_current() @@ -316,6 +320,8 @@ def step(self, action: 'Action'): game_for_plan.set_reroll_dice(action) case GamePhase.ACTION_PHASE: game_for_plan.resolve_action(action) + case GamePhase.SELECT: + game_for_plan.select_phase(action) # print(self, game_for_plan) logger.info(self.is_dying) if not self.is_dying: @@ -342,11 +348,18 @@ def step(self, action: 'Action'): self.set_reroll_dice(action) case GamePhase.ACTION_PHASE: self.resolve_action(action) + case GamePhase.SELECT: + self.select_phase(action) # else: - - + def select_phase(self, action): + ''' + 5.1特殊阶段:选择阶段 + ''' + assert self.special_phase is not None + self.active_player.select_action(action) + self.special_phase.on_finished(self) def set_hand_card(self, action): ''' @@ -437,7 +450,7 @@ def encode_message(self, base=None): 新版: 尝试将Game信息编码成table呈现给使用者 ''' return layout(self, base) - + def encoder_dict(self): ''' 旧版: 将Game信息编码成dict diff --git a/genius_invocation/game/player.py b/genius_invocation/game/player.py index d245681b..0d4f7a54 100644 --- a/genius_invocation/game/player.py +++ b/genius_invocation/game/player.py @@ -90,6 +90,11 @@ def init_define(self): self.played_cards = [] self.tune_or_discard_cards = [] + # Select List + self.select_list = None + self.select_num = 0 + self.select_result = None + def update_element_list(self): ''' Only For La Signora right now. Refresh the element list, which may be used by some skills and talents.''' self.element_list = [] @@ -97,6 +102,13 @@ def update_element_list(self): self.element_list.append(char.element) self.element_set = set(self.element_list) + def select_action(self, action: 'Action'): + ''' + 5.1特殊行动:选择行动 + ''' + assert self.select_list is not None + self.select_result = action.choice_list + def choose_card(self, action: 'Action'): ''' 非标准行动: 制衡手牌 @@ -214,7 +226,8 @@ def use_skill(self, game: 'GeniusGame'): use_type=skill.type, cost=deepcopy(skill.cost)) self.character_list[self.active_idx].skill(idx, game) - game.current_skill = None + if game.special_phase is None: + game.current_skill = None def play_card(self, game: 'GeniusGame'): ''' @@ -319,6 +332,13 @@ def generate_mask(self, game: 'GeniusGame'): if character.is_alive and not character.is_active: self.action_mask[14][idx+2][0] = 1 return + if game.game_phase == GamePhase.SELECT: + self.action_mask[19][15][0] = 1 + self.action_mask[19][15][1] = len(self.select_list) + self.action_mask[19][15][2] = self.select_num + # 此处为特例,我们用第三维表征需要选择的数量 + return + # 计算能否打出手牌和烧牌 for idx, action_card in enumerate(self.hand_zone.card): diff --git a/genius_invocation/game/zone.py b/genius_invocation/game/zone.py index 4991a102..f8c620d6 100644 --- a/genius_invocation/game/zone.py +++ b/genius_invocation/game/zone.py @@ -20,8 +20,8 @@ class Switch: def __init__(self, from_character, to_character, swicth_type) -> None: self.from_player = to_character.from_player - self.from_character = from_character - self.to_character = to_character + self.from_character: 'Character' = from_character + self.to_character: 'Character' = to_character self.type = swicth_type class GetCard: @@ -563,7 +563,7 @@ def clear(self, game:'GeniusGame'): self.artifact_card.on_destroy(game) self.artifact_card = None if self.talent_card is not None: - game.manager.invoke(EventType.ON_EQUIP_REMOVE, game) + self.talent_card.on_destroy(game) self.talent_card = None if self.special_skill is not None: self.special_skill.on_destroy(game) @@ -709,7 +709,7 @@ def add_card_by_name(self, card_names): 通过名字获取牌 ''' for card_name in card_names: - self.add(eval(card_name)()) + self.add([eval(card_name)()]) def num(self): return len(self.card) diff --git a/genius_invocation/main.py b/genius_invocation/main.py index 2447fed2..500d70e6 100644 --- a/genius_invocation/main.py +++ b/genius_invocation/main.py @@ -232,8 +232,8 @@ def code_to_deck(code): else: deck1 = code_to_deck('FhHRgm4YFiHxg3YYFzFhhHcYF0FxhXgYF1GBhn4YF2Hhh38YF3HxioAYGKEBfYEXGNAA') deck2 = { - 'character': ['Barbara', 'Beidou', 'KaedeharaKazuha'], - 'action_card': ['Koholasaurus', 'Xenochromatic', 'Yumkasaurus'] + 'character': ['Chiori', 'Arataki_Itto', 'KaedeharaKazuha'], + 'action_card': ['EremiteTeatime', 'Xenochromatic', 'Yumkasaurus'] } # 初始化游戏 diff --git a/genius_invocation/user_layout.py b/genius_invocation/user_layout.py index 42b02068..e947bcb9 100644 --- a/genius_invocation/user_layout.py +++ b/genius_invocation/user_layout.py @@ -111,7 +111,40 @@ def print_prompt(layout, history, string): print(layout) def get_skill(layout, game: 'GeniusGame'): - if not game.active_player.active_idx in range(len(game.active_player.character_list)): + if game.game_phase == GamePhase.SELECT: + message_panel_list = [] + for s in game.active_player.select_list: + sponsor_message = Table.grid() + sponsor_message.add_column(no_wrap=True, justify="medium") + sponsor_message.add_row( + s.name_ch if hasattr(s, 'name_ch') else s, + style=STRONG_COLOR, + ) + message_panel = Panel( + Align.center( + Group(" ",Align.center(sponsor_message)), + vertical="middle", + ), + title="Select"+str(len(message_panel_list)), + style=OTHER_STYLE, + ) + message_panel_list.append(message_panel) + print(s) + num = len(message_panel_list) + if num == 3: + layout.split_row( + Layout(name="Select1", ratio=1), + Layout(name="Select2", ratio=1), + Layout(name="Select3", ratio=1), + ) + layout['Select1'].update(message_panel_list[0]) + layout['Select2'].update(message_panel_list[1]) + layout['Select3'].update(message_panel_list[2]) + else: + print("Select skill number is not 3, 尚不支持显示") + exit() + + elif not game.active_player.active_idx in range(len(game.active_player.character_list)): sponsor_message = Table.grid() sponsor_message.add_column(no_wrap=True, justify="medium") sponsor_message.add_row( @@ -227,8 +260,8 @@ def get_character(player: 'GeniusPlayer', idx: int, base=None): title="Character", style=OTHER_STYLE ) - return message_panel - + return message_panel + if character_list[idx].is_active: color = STRONG_COLOR else: @@ -433,7 +466,7 @@ def get_dice(player: 'GeniusPlayer'): DiceToChinese(DiceType(dice)), style=DiceToColor(DiceType(dice)), ) - + if player.index == player.game.active_player_index: style = ACTIVE_PLAYER_STYLE else: diff --git a/genius_invocation/utils.py b/genius_invocation/utils.py index f7b2180c..8e79eb6a 100644 --- a/genius_invocation/utils.py +++ b/genius_invocation/utils.py @@ -6,8 +6,8 @@ MAX_HANDCARD = 10 MAX_DICE = 16 MAX_ROUND = 15 -CHOICE_NUM = 19 -TARGET_NUM = 15 +CHOICE_NUM = 20 +TARGET_NUM = 16 class CountryType(Enum): MONDSTADT = 0 # 蒙德 @@ -83,7 +83,9 @@ class SkillType(Enum): PASSIVE_SKILL = 3 SUMMON = 4 OTHER = 5 - SPECIAL_SKILL = 6 + +class SpecialSkillType(Enum): + SPECIAL_SKILL = 0 class GamePhase(Enum): SET_CARD = 0 @@ -91,6 +93,7 @@ class GamePhase(Enum): ROLL_PHASE = 2 ACTION_PHASE = 3 END_PHASE = 4 + SELECT = 5 class ActionChoice(Enum): HAND_CARD = 0 @@ -98,6 +101,7 @@ class ActionChoice(Enum): CHANGE_CHARACTER = 2 PASS = 3 NONE = 4 + SELECT = 5 class ActionTarget(Enum): OPPONENT = 0 @@ -107,6 +111,7 @@ class ActionTarget(Enum): MY_SUPPORT_REGION = 4 DICE_REGION = 5 CARD_REGION = 6 + SELECT_REGION = 7 class ActionCardType(Enum): @@ -346,7 +351,7 @@ def SkillToChinese(skill: SkillType): return "元素战技" case SkillType.ELEMENTAL_BURST: return "元素爆发" - case SkillType.SPECIAL_SKILL: + case SpecialSkillType.SPECIAL_SKILL: return "特技" def CostToChinese(cost: CostType): diff --git a/genius_invocation/web/game/game.py b/genius_invocation/web/game/game.py index d2aaed82..c9a9f5c0 100644 --- a/genius_invocation/web/game/game.py +++ b/genius_invocation/web/game/game.py @@ -113,7 +113,7 @@ def resolve_action(self, action: 'Action'): self.first_player = self.active_player_index if self.is_change_player and (not oppenent_player.is_pass): self.change_active_player() - + oppenent_player = self.players[1 - self.active_player_index] self.manager.invoke(EventType.BEFORE_ANY_ACTION, self) while self.active_player.prepared_skill is not None: