Skip to content

Commit

Permalink
Merge branch 'master' into pettingzoo
Browse files Browse the repository at this point in the history
  • Loading branch information
zyr17 committed Mar 29, 2024
2 parents f24d60e + ef73d48 commit 9cc946a
Show file tree
Hide file tree
Showing 22 changed files with 2,587 additions and 88 deletions.
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ of the game, and the last number is the patch version of this project.

## [Unreleased]

## [0.4.5.1] - 2024-03-25

### Changed
- #104 `CreateRandomObjectAction` have new `replace` attribute, when it is true,
an object can be selected multiple times.

### Fixed
- Golden Troupe's Reward decrease cost of all skill types.
- When generate card to hand with full hand will raise an error.
- `HTTPRoomServer` info wrong in `/version`.
- #106 `Blessing of the Divine Relic's Installation` will reset counter of
`Veteran's Visage`.
- #82 team status have higher event trigger priority than standby characters.

## [0.4.5.0] - 2024-03-19

### Added
Expand Down Expand Up @@ -542,7 +556,8 @@ Happy New Year 2024! New year comes in coding time!
### Added
- Test version to ensure release pipeline is working

[Unreleased]: https://github.com/LPSim/backend/compare/v0.4.5.0...HEAD
[Unreleased]: https://github.com/LPSim/backend/compare/v0.4.5.1...HEAD
[0.4.5.1]: https://github.com/LPSim/backend/releases/tag/v0.4.5.1
[0.4.5.0]: https://github.com/LPSim/backend/releases/tag/v0.4.5.0
[0.4.4.1]: https://github.com/LPSim/backend/releases/tag/v0.4.4.1
[0.4.4.0]: https://github.com/LPSim/backend/releases/tag/v0.4.4.0
Expand Down
2 changes: 1 addition & 1 deletion src/lpsim/network/http_room_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ async def get_version():
"version_tuple": __version_tuple__,
"support_version": __frontend_version__,
"info": {
"class": "HTTPServer",
"class": "HTTPRoomServer",
},
}

Expand Down
31 changes: 22 additions & 9 deletions src/lpsim/server/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,8 @@ class CreateRandomObjectAction(ActionBase):
"""
Action for creating random objects. A list of names are provided, and
`number` of them will be created by uniformly selecting from the list and doing
`CreateObjectAction` for each of them.
`CreateObjectAction` for each of them. Replace means whether the sample is with or
without replacement. True meaning that a value of a can be selected multiple times.
NOTE: some actions will not generate existing objects, e.g. elemental skill of
Rhodeia will not summoning existing objects unless full. This action will not deal
with such situation, you need to write logic to filter out existing objects before
Expand All @@ -324,15 +325,16 @@ class CreateRandomObjectAction(ActionBase):
object_names: List[str]
object_arguments: dict
number: int
replace: bool

def select_by_idx(
self, idx: int
) -> Tuple[CreateObjectAction, "CreateRandomObjectAction"]:
"""
Select an object name by index and create a `CreateObjectAction` for it.
It will also return a new `CreateRandomObjectAction` with the same
arguments but with the `number` reduced by 1 and the selected object
name removed from the list.
It will also return a new `CreateRandomObjectAction`. If replace is False, with
the same arguments but with the `number` reduced by 1 and the selected object
name removed from the list. If replace is True, only number is decreased by 1.
Returns:
CreateObjectAction: The action for creating the object.
Expand All @@ -341,18 +343,29 @@ def select_by_idx(
"""
selected = self.object_names[idx]
others = self.object_names[:idx] + self.object_names[idx + 1 :]
return (
CreateObjectAction(
if self.replace:
ret_action = CreateRandomObjectAction(
object_position=self.object_position,
object_name=selected,
object_names=self.object_names,
object_arguments=self.object_arguments,
),
CreateRandomObjectAction(
number=self.number - 1,
replace=True,
)
else:
ret_action = CreateRandomObjectAction(
object_position=self.object_position,
object_names=others,
object_arguments=self.object_arguments,
number=self.number - 1,
replace=False,
)
return (
CreateObjectAction(
object_position=self.object_position,
object_name=selected,
object_arguments=self.object_arguments,
),
ret_action,
)


Expand Down
2 changes: 2 additions & 0 deletions src/lpsim/server/card/event/resonance.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ def get_actions(
),
object_arguments={},
number=1,
replace=False,
)
]

Expand Down Expand Up @@ -609,6 +610,7 @@ def get_actions(
),
object_arguments={},
number=1,
replace=False,
)
]

Expand Down
6 changes: 3 additions & 3 deletions src/lpsim/server/character/character_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,9 +751,9 @@ def damage_taken(self) -> int:

def get_object_lists(self) -> List[ObjectBase]:
"""
Get all objects of the character, order is passive skill, weapon,
artifact, talent, status. For status, order is their index in status
list, i.e. generated time.
Get all objects of the character, order is character itself, skills, and other
objects. Order of other objects, including equipment and status, are based on
when they are attached, regardless of their types.
"""
result: List[ObjectBase] = [self]
for skill in self.skills:
Expand Down
4 changes: 2 additions & 2 deletions src/lpsim/server/character/electro/keqing_3_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ def get_actions(self, match: Any) -> List[Actions]:
if card.name == "Lightning Stiletto":
found_stiletto = True
break
if not found_stiletto and len(hands) < match.config.max_hand_size:
# not found stiletto and hand not full
if not found_stiletto:
# not found stiletto
position = ObjectPosition(
player_idx=self.position.player_idx, area=ObjectPositionType.HAND, id=-1
)
Expand Down
4 changes: 4 additions & 0 deletions src/lpsim/server/character/hydro/rhodeia_3_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def get_actions(self, match: Any) -> List[ChargeAction | CreateRandomObjectActio
object_position=target_position,
object_arguments={"version": self.version},
number=self._summon_number,
replace=False,
)
)
elif len(unexist_names) >= self._summon_number:
Expand All @@ -138,6 +139,7 @@ def get_actions(self, match: Any) -> List[ChargeAction | CreateRandomObjectActio
object_position=target_position,
object_arguments={"version": self.version},
number=self._summon_number,
replace=False,
)
)
else:
Expand All @@ -148,6 +150,7 @@ def get_actions(self, match: Any) -> List[ChargeAction | CreateRandomObjectActio
object_position=target_position,
object_arguments={"version": self.version},
number=len(unexist_names),
replace=False,
)
)
ret.append(
Expand All @@ -156,6 +159,7 @@ def get_actions(self, match: Any) -> List[ChargeAction | CreateRandomObjectActio
object_position=target_position,
object_arguments={"version": self.version},
number=self._summon_number - len(unexist_names),
replace=False,
)
)
ret.append(self.charge_self(1))
Expand Down
2 changes: 1 addition & 1 deletion src/lpsim/server/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ class CreateObjectEventArguments(EventArgumentsBase):

type: Literal[ActionTypes.CREATE_OBJECT] = ActionTypes.CREATE_OBJECT
action: CreateObjectAction
create_result: Literal["NEW", "RENEW"]
create_result: Literal["NEW", "RENEW", "FAIL"]


# 20
Expand Down
63 changes: 42 additions & 21 deletions src/lpsim/server/match.py
Original file line number Diff line number Diff line change
Expand Up @@ -1094,11 +1094,15 @@ def get_object_list(self) -> List[ObjectBase]:
Get all objects in the match by `self.table.get_object_lists`.
The order of objects should follow the game rule. The rules are:
1. objects of `self.current_player` goes first
2. objects belongs to character goes first
2.1. active character first, otherwise the default order.
2.2. for one character, order is weapon, artifact, talent, status.
2.3. for status, order is their index in status list, i.e.
generated time.
2. objects belongs to character and team status goes first
2.1. active character first, then next, next ... until all alive
characters are included.
2.2. for one character, order is character self, its skills, other objects
attached to the character.
2.3. status and equipment have no priority, just based on the time when they
are attached to the character.
2.4. specifically, team status has lower priority of active character and
its attached objects, but higher priority of other characters.
3. for other objects, order is: summon, support, hand, dice, deck.
3.1. all other objects in same region are sorted by their index in
the list.
Expand Down Expand Up @@ -1491,20 +1495,28 @@ def _respond_switch_character(self, response: SwitchCharacterResponse):
)
cost_value = self._modify_cost_value(cost_value, "REAL")
actions.append(SwitchCharacterAction.from_response(response))
actions.append(
ActionEndAction(
action_label=PlayerActionLabels.SWITCH.value,
position=ObjectPosition(
player_idx=response.player_idx,
character_idx=response.request.active_character_idx,
area=ObjectPositionType.CHARACTER,
id=self.player_tables[response.player_idx]
.characters[response.request.active_character_idx]
.id,
),
do_combat_action=True,
)
end_action = ActionEndAction(
action_label=PlayerActionLabels.SWITCH.value,
position=ObjectPosition(
player_idx=response.player_idx,
character_idx=response.request.active_character_idx,
area=ObjectPositionType.CHARACTER,
id=self.player_tables[response.player_idx]
.characters[response.request.active_character_idx]
.id,
),
do_combat_action=True,
)
# as after switching character, the active character may change, so
# we need to update combat action value before switching.
combat_action_value = CombatActionValue(
position=end_action.position,
action_label=end_action.action_label,
do_combat_action=end_action.do_combat_action,
)
self._modify_value(value=combat_action_value, mode="REAL")
end_action.do_combat_action = combat_action_value.do_combat_action
actions.append(end_action)
self.event_controller.stack_actions(actions)
self.requests = [
x for x in self.requests if x.player_idx != response.player_idx
Expand Down Expand Up @@ -2390,9 +2402,18 @@ def _action_create_object(
target_list = renew_target_list = table.summons
target_name = "summon"
elif action.object_position.area == ObjectPositionType.HAND:
assert (
len(table.hands) < self.config.max_hand_size
), "Cannot create hand card when hand is full."
if len(table.hands) >= self.config.max_hand_size:
logging.warning(
f"Player {player_idx} "
f"tried to create new hand {action.object_name}, "
"but hand size is already maximum."
)
return [
CreateObjectEventArguments(
action=action,
create_result="FAIL",
)
]
target_class = CardBase
target_list = renew_target_list = table.hands
target_name = "hand"
Expand Down
6 changes: 1 addition & 5 deletions src/lpsim/server/object_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,7 @@ def _check_value_self_skill_or_talent(
self,
value: CostValue,
match: Any,
skill_cost_label: int = (
CostLabels.ELEMENTAL_SKILL.value
| CostLabels.NORMAL_ATTACK.value
| CostLabels.ELEMENTAL_BURST.value
),
skill_cost_label: int = CostLabels.SKILLS.value,
) -> bool:
"""
Check whether this object is located on a character, and value is self use
Expand Down
4 changes: 4 additions & 0 deletions src/lpsim/server/patch/v43/balance/rhodeia_of_loch_4_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def get_actions(
object_position=target_position,
object_arguments={"version": self.version},
number=self._summon_number,
replace=False,
)
)
elif (
Expand All @@ -85,6 +86,7 @@ def get_actions(
object_position=target_position,
object_arguments={"version": self.version},
number=self._summon_number,
replace=False,
)
)
else:
Expand All @@ -95,6 +97,7 @@ def get_actions(
object_position=target_position,
object_arguments={"version": self.version},
number=len(unexist_names) - 1,
replace=False,
)
)
ret.append(
Expand All @@ -103,6 +106,7 @@ def get_actions(
object_position=target_position,
object_arguments={"version": self.version},
number=self._summon_number - len(unexist_names) + 1,
replace=False,
)
)
ret.append(self.charge_self(1))
Expand Down
1 change: 1 addition & 0 deletions src/lpsim/server/patch/v43/cards/mamere_4_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ def event_handler_USE_CARD(
object_position=position,
object_arguments=args,
number=1,
replace=True,
)
)
res += self.check_should_remove()
Expand Down
44 changes: 25 additions & 19 deletions src/lpsim/server/patch/v44/cards/sunyata_flower_4_4.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
from typing import Any, Dict, List, Literal, Type
from pydantic import PrivateAttr

from ....action import ActionTypes, Actions, CreateObjectAction, RemoveObjectAction
from ....action import (
ActionTypes,
Actions,
CreateObjectAction,
CreateRandomObjectAction,
RemoveObjectAction,
)
from .....utils.class_registry import get_instance, register_class
from ....event import GameStartEventArguments, MoveObjectEventArguments
from ....modifiable_values import CostValue
Expand Down Expand Up @@ -109,30 +115,30 @@ def get_targets(self, match: Match) -> List[ObjectPosition]:

def get_actions(
self, target: ObjectPosition | None, match: Match
) -> List[RemoveObjectAction | CreateObjectAction]:
) -> List[RemoveObjectAction | CreateObjectAction | CreateRandomObjectAction]:
"""
remove target, and generate 2 random support cards, and create status
"""
assert target is not None
ret: List[RemoveObjectAction | CreateObjectAction] = [
RemoveObjectAction(object_position=target)
]
ret: List[
RemoveObjectAction | CreateObjectAction | CreateRandomObjectAction
] = [RemoveObjectAction(object_position=target)]
candidate_list = self._get_candidate_list(match)
for i in range(2):
random_target = candidate_list[int(match._random() * len(candidate_list))]
position = ObjectPosition(
player_idx=self.position.player_idx, area=ObjectPositionType.HAND, id=-1
)
args = {}
if self._accept_version is not None:
args["version"] = self._accept_version
ret.append(
CreateObjectAction(
object_name=random_target,
object_position=position,
object_arguments=args,
)
position = ObjectPosition(
player_idx=self.position.player_idx, area=ObjectPositionType.HAND, id=-1
)
args = {}
if self._accept_version is not None:
args["version"] = self._accept_version
ret.append(
CreateRandomObjectAction(
object_names=candidate_list,
object_position=position,
object_arguments=args,
number=2,
replace=True,
)
)
ret.append(
CreateObjectAction(
object_name=self.name,
Expand Down
Loading

0 comments on commit 9cc946a

Please sign in to comment.