From b92ec644363c8def2749e180a0d1af1c1667d8ac Mon Sep 17 00:00:00 2001 From: zyr17 Date: Sun, 3 Sep 2023 16:27:43 +0800 Subject: [PATCH] feat: implement two millelith artifact. --- server/card/equipment/artifact/__init__.py | 4 +- server/card/equipment/artifact/base.py | 34 ++++++- server/card/equipment/artifact/millelith.py | 99 +++++++++++++++++++++ server/card/equipment/weapon/base.py | 16 ++-- server/status/charactor_status/__init__.py | 3 +- server/status/charactor_status/artifacts.py | 13 +++ 6 files changed, 158 insertions(+), 11 deletions(-) create mode 100644 server/card/equipment/artifact/millelith.py create mode 100644 server/status/charactor_status/artifacts.py diff --git a/server/card/equipment/artifact/__init__.py b/server/card/equipment/artifact/__init__.py index f89f92eb..e61024ed 100644 --- a/server/card/equipment/artifact/__init__.py +++ b/server/card/equipment/artifact/__init__.py @@ -1,9 +1,11 @@ from .element_artifacts import ElementArtifacts +from .millelith import MillelithArtifacts from .others import OtherArtifacts + from .old_version import OldVersionArtifacts Artifacts = ( - ElementArtifacts | OtherArtifacts + ElementArtifacts | MillelithArtifacts | OtherArtifacts # finally old versions | OldVersionArtifacts ) diff --git a/server/card/equipment/artifact/base.py b/server/card/equipment/artifact/base.py index 0f79714d..9858f6cd 100644 --- a/server/card/equipment/artifact/base.py +++ b/server/card/equipment/artifact/base.py @@ -1,6 +1,6 @@ from typing import Literal, List, Any -from ....event import MoveObjectEventArguments +from ....event import MoveObjectEventArguments, RoundPrepareEventArguments from ....object_base import CardBase from ....struct import Cost @@ -14,13 +14,14 @@ class ArtifactBase(CardBase): Base class of artifacts. """ name: str - type: Literal[ObjectType.ARTIFACT] = ObjectType.ARTIFACT - cost_label: int = CostLabels.CARD.value | CostLabels.ARTIFACT.value - + desc: str version: str cost: Cost usage: int + type: Literal[ObjectType.ARTIFACT] = ObjectType.ARTIFACT + cost_label: int = CostLabels.CARD.value | CostLabels.ARTIFACT.value + def equip(self, match: Any) -> List[Actions]: """ The artifact is equipped, i.e. from hand to charactor. Set the status @@ -85,3 +86,28 @@ def event_handler_MOVE_OBJECT( # this artifact equipped from hand to charactor return self.equip(match) return [] + + +class RoundEffectArtifactBase(ArtifactBase): + """ + Artifacts that has round effects. Refresh their usage when equipped and + at round preparing stage. + Instead of setting usage, set max_usage_per_round. + """ + name: str + desc: str + version: str + cost: Cost + max_usage_per_round: int + + usage: int = 0 + + def equip(self, match: Any) -> List[Actions]: + self.usage = self.max_usage_per_round + return [] + + def event_handler_ROUND_PREPARE( + self, event: RoundPrepareEventArguments, match: Any + ) -> List[Actions]: + self.usage = self.max_usage_per_round + return [] diff --git a/server/card/equipment/artifact/millelith.py b/server/card/equipment/artifact/millelith.py new file mode 100644 index 00000000..a7a95b0d --- /dev/null +++ b/server/card/equipment/artifact/millelith.py @@ -0,0 +1,99 @@ +from typing import Any, List, Literal + +from ....consts import ELEMENT_TO_DIE_COLOR, ObjectPositionType + +from ....action import CreateDiceAction, CreateObjectAction + +from ....event import ReceiveDamageEventArguments, RoundPrepareEventArguments + +from ....struct import Cost +from .base import ArtifactBase, RoundEffectArtifactBase + + +class GeneralsAncientHelm(ArtifactBase): + name: Literal["General's Ancient Helm"] + desc: str = ( + 'When Action Phase begins: The character to which this is attached ' + 'gains Unmovable Mountain that provides 2 Shield points.' + ) + version: Literal['3.5'] = '3.5' + cost: Cost = Cost(same_dice_number = 2) + usage: int = 0 + + def event_handler_ROUND_PREPARE( + self, event: RoundPrepareEventArguments, match: Any + ) -> List[CreateObjectAction]: + """ + Create Unmovable Mountain for this charactor. + """ + if self.position.area != ObjectPositionType.CHARACTOR: + # not equipped + return [] + position = self.position.set_area(ObjectPositionType.CHARACTOR_STATUS) + return [CreateObjectAction( + object_name = 'Unmovable Mountain', + object_position = position, + object_arguments = {} + )] + + +class TenacityOfTheMillelith(RoundEffectArtifactBase): + name: Literal['Tenacity of the Millelith'] + desc: str = ( + 'When Action Phase begins: The character to which this is attached ' + 'gains Unmovable Mountain that provides 2 Shield points. ' + 'After this character takes DMG: If the character this card is ' + 'attached to is the active character, create 1 Elemental Die matching ' + "this character's Elemental Type. (Once per Round) " + ) + version: Literal['3.7'] = '3.7' + cost: Cost = Cost(same_dice_number = 3) + max_usage_per_round: int = 1 + + def event_handler_ROUND_PREPARE( + self, event: RoundPrepareEventArguments, match: Any + ) -> List[CreateObjectAction]: + if self.position.area != ObjectPositionType.CHARACTOR: + # not equipped + return [] + super().event_handler_ROUND_PREPARE(event, match) + position = self.position.set_area(ObjectPositionType.CHARACTOR_STATUS) + return [CreateObjectAction( + object_name = 'Unmovable Mountain', + object_position = position, + object_arguments = {} + )] + + def event_handler_RECEIVE_DAMAGE( + self, event: ReceiveDamageEventArguments, match: Any + ) -> List[CreateDiceAction]: + """ + When this charactor received damage and is active charactor, create + elemental die. + """ + if self.position.area != ObjectPositionType.CHARACTOR: + # not equipped + return [] + if self.usage == 0: + # no usage + return [] + damage = event.final_damage + if not self.position.check_position_valid( + damage.target_position, match, player_idx_same = True, + charactor_idx_same = True, source_is_active_charactor = True + ): + # damage not attack self, or self not active charactor + return [] + # create die + self.usage -= 1 + charactor = match.player_tables[self.position.player_idx].charactors[ + self.position.charactor_idx + ] + return [CreateDiceAction( + player_idx = self.position.player_idx, + number = 1, + color = ELEMENT_TO_DIE_COLOR[charactor.element] + )] + + +MillelithArtifacts = GeneralsAncientHelm | TenacityOfTheMillelith diff --git a/server/card/equipment/weapon/base.py b/server/card/equipment/weapon/base.py index c35f5c4b..f5445301 100644 --- a/server/card/equipment/weapon/base.py +++ b/server/card/equipment/weapon/base.py @@ -18,12 +18,12 @@ class WeaponBase(CardBase): """ name: str desc: str - type: Literal[ObjectType.WEAPON] = ObjectType.WEAPON + cost: Cost version: str - cost_label: int = CostLabels.CARD.value | CostLabels.WEAPON.value weapon_type: WeaponType + type: Literal[ObjectType.WEAPON] = ObjectType.WEAPON + cost_label: int = CostLabels.CARD.value | CostLabels.WEAPON.value - cost: Cost usage: int = 0 damage_increase: int = 1 # Almost all weapons increase the damage by 1 @@ -124,15 +124,21 @@ class RoundEffectWeaponBase(WeaponBase): at round preparing stage. Instead of setting usage, set max_usage_per_round. """ - usage: int = 0 + name: str + desc: str + cost: Cost + version: str + weapon_type: WeaponType max_usage_per_round: int + usage: int = 0 + def equip(self, match: Any) -> List[Actions]: self.usage = self.max_usage_per_round return [] def event_handler_ROUND_PREPARE( self, event: RoundPrepareEventArguments, match: Any - ): + ) -> List[Actions]: self.usage = self.max_usage_per_round return [] diff --git a/server/status/charactor_status/__init__.py b/server/status/charactor_status/__init__.py index 1d92fbfa..a9a3e810 100644 --- a/server/status/charactor_status/__init__.py +++ b/server/status/charactor_status/__init__.py @@ -2,9 +2,10 @@ from .dendro_charactors import DendroCharactorStatus from .geo_charactors import GeoCharactorStatus from .foods import FoodStatus +from .artifacts import ArtifactCharactorStatus CharactorStatus = ( DendroCharactorStatus | GeoCharactorStatus - | SystemCharactorStatus | FoodStatus + | SystemCharactorStatus | FoodStatus | ArtifactCharactorStatus ) diff --git a/server/status/charactor_status/artifacts.py b/server/status/charactor_status/artifacts.py new file mode 100644 index 00000000..b9c83447 --- /dev/null +++ b/server/status/charactor_status/artifacts.py @@ -0,0 +1,13 @@ +from typing import Literal +from .base import ShieldCharactorStatus + + +class UnmovableMountain(ShieldCharactorStatus): + name: Literal['Unmovable Mountain'] = 'Unmovable Mountain' + desc: str = '''Provides 2 Shield to protect the equipped charactor.''' + version: Literal['3.5'] = '3.5' + usage: int = 2 + max_usage: int = 2 + + +ArtifactCharactorStatus = UnmovableMountain | UnmovableMountain