def perform(self, character: "CharacterModel", input_: input_model) -> Description: character_doc = self._character_lib.get_document(character.id) resource, resource_extraction_description, cost_per_unit = self._get_resource_and_cost( character_doc, input_) if input_.quantity is None: unit_name = self._kernel.translation.get(resource.unit) return Description( title=f"Récupérer du {resource.name}", items=[ Part( is_form=True, form_values_in_query=True, form_action=get_character_action_url( character_id=character.id, action_type=ActionType.COLLECT_RESOURCE, action_description_id=self._description.id, query_params=self.input_model_serializer.dump( input_), ), items=[ Part( label= f"Quantité (coût: {cost_per_unit} par {unit_name}) ?", type_=Type.NUMBER, name="quantity", ) ], ) ], ) self._kernel.resource_lib.add_resource_to( character_id=character_doc.id, resource_id=input_.resource_id, quantity=input_.quantity, commit=False, ) cost = self.get_cost(character, input_=input_) if cost is None: raise RollingError("Cost compute should not be None !") self._kernel.character_lib.reduce_action_points(character.id, cost, commit=False) self._kernel.server_db_session.commit() return Description( title= f"{input_.quantity} {self._kernel.translation.get(resource.unit)} récupéré", footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements") ], )
def get_properties_from_config(cls, game_config: "GameConfig", action_config_raw: dict) -> dict: for produce in action_config_raw["produce"]: if "resource" not in produce and "stuff" not in produce: raise RollingError( "Misconfiguration for action SearchFoodAction (production " "must contain stuff or resource key" ) properties = fill_base_action_properties(cls, game_config, {}, action_config_raw) properties.update({"produce": action_config_raw["produce"]}) return properties
def get_properties_from_config(cls, game_config: "GameConfig", action_config_raw: dict) -> dict: for consume in action_config_raw["consume"]: if "resource" not in consume and "stuff" not in consume: raise RollingError(f"Action config is not correct: {action_config_raw}") properties = fill_base_action_properties(cls, game_config, {}, action_config_raw) properties["produce_stuff_id"] = action_config_raw["produce_stuff_id"] properties["consume"] = action_config_raw["consume"] properties["craft_ap"] = action_config_raw["craft_ap"] properties["default_description"] = action_config_raw.get("default_description", "") properties["link_group_name"] = action_config_raw.get("link_group_name", None) return properties
def get_spawn_coordinates(self, world_map_source: "WorldMapSource") -> typing.Tuple[int, int]: available_coordinates: typing.List[typing.Tuple[int, int]] = [] for row_i, rows in enumerate(self._kernel.world_map_source.geography.rows): for col_i, world_tile_type in enumerate(rows): if world_tile_type in self._world_tile_types: available_coordinates.append((row_i, col_i)) if not available_coordinates: raise RollingError("No matching world tile for find spawn coordinate") return random.choice(available_coordinates)
def get_start_zone_coordinates( self, world_row_i: int, world_col_i: int ) -> typing.Tuple[int, int]: available_coordinates: typing.List[typing.Tuple[int, int]] = [] for row_i, row in enumerate(self.geography.rows): for col_i, map_tile_type in enumerate(row): if traversable_properties[map_tile_type].get(TransportType.WALKING.value): available_coordinates.append((row_i, col_i)) if not available_coordinates: raise RollingError(f"No traversable coordinate in zone {world_row_i},{world_col_i}") return random.choice(available_coordinates)
def _get_properties_from_config( cls, game_config: "GameConfig", action_config_raw: dict ) -> dict: for require in action_config_raw["require"]: if "resource" not in require and "stuff" not in require: raise RollingError( "Misconfiguration for action " "CraftStuffWithResourceAction/CraftStuffWithStuffAction (require " "must contain stuff or resource key" ) properties = fill_base_action_properties(cls, game_config, {}, action_config_raw) properties.update( {"produce": action_config_raw["produce"], "require": action_config_raw["require"]} ) return properties
def make_deal( self, offer_id: int, character_id: str, request_item_id: typing.Optional[int] = None, offer_item_id: typing.Optional[int] = None, ) -> None: offer: OfferDocument = self.get_offer_query(offer_id).one() request_items: typing.List[OfferItemDocument] = [] offer_items: typing.List[OfferItemDocument] = [] event_texts: typing.List[str] = ["Vous avez obtenu:"] if not self.character_can_deal(character_id, offer_id): raise RollingError( f"Character {character_id} cannot make deal {offer_id}") if offer.request_operand == OfferOperand.OR.value: if request_item_id: request_items.append( next(i for i in offer.request_items if i.id == request_item_id)) else: request_items.extend(offer.request_items) if offer.offer_operand == OfferOperand.OR.value: if not offer_item_id: raise RollingError( f"Offer {offer_id} require an offer_item_id") offer_items.append( next(i for i in offer.offer_items if i.id == offer_item_id)) else: offer_items.extend(offer.offer_items) def _deal_item(item: OfferItemDocument, giver_id: str, receiver_id: str) -> None: if item.resource_id: self._kernel.resource_lib.reduce_carried_by( character_id=giver_id, resource_id=item.resource_id, quantity=float(item.quantity), commit=False, ) self._kernel.resource_lib.add_resource_to( character_id=receiver_id, resource_id=item.resource_id, quantity=float(item.quantity), commit=False, ) if item.stuff_id: for _ in range(int(item.quantity)): stuff = self._kernel.stuff_lib.get_first_carried_stuff( character_id=giver_id, stuff_id=item.stuff_id) self._kernel.stuff_lib.un_use_stuff( stuff.id) # TODO BS 20200719: test it self._kernel.stuff_lib.set_carried_by( stuff_id=stuff.id, character_id=receiver_id, commit=False) for request_item in request_items: _deal_item(request_item, giver_id=character_id, receiver_id=offer.character_id) event_texts.append( f"- {request_item.get_name(self._kernel, quantity=True)}") event_texts.append("Vous avez donné:") for offer_item in offer_items: _deal_item(offer_item, giver_id=offer.character_id, receiver_id=character_id) event_texts.append( f"- {offer_item.get_name(self._kernel, quantity=True)}") self._kernel.character_lib.add_event( offer.character_id, title=f"Un affaire à été conclu: {offer.title}", story_pages=[StoryPageDocument(text="\n".join(event_texts))], ) self._kernel.server_db_session.commit()
def generate( self, width: int, height: typing.Optional[int] = None, north_west_map: typing.Optional[ZoneMapSource] = None, north_map: typing.Optional[ZoneMapSource] = None, north_est_map: typing.Optional[ZoneMapSource] = None, west_map: typing.Optional[ZoneMapSource] = None, est_map: typing.Optional[ZoneMapSource] = None, south_west_map: typing.Optional[ZoneMapSource] = None, south_map: typing.Optional[ZoneMapSource] = None, south_est_map: typing.Optional[ZoneMapSource] = None, ) -> ZoneMapSource: height = height or width # Must be odd if not width % 2 or not height % 2: raise RollingError( "Width and height must be odd: given values are {}x{}".format( width, height)) self._current_raw_source = "::GEO\n" part_len = width // 3 top_void_part = 0, part_len - 1 bottom_void_part = height - part_len, height - 1 reduce_counter = part_len reduce2_counter = 0 for row_i in range(height): if row_i <= top_void_part[1]: left_void_part = 0, part_len - 1 - row_i right_void_part = width - part_len + row_i, width - 1 elif row_i >= bottom_void_part[0]: left_void_part = 0, part_len - reduce_counter right_void_part = width - reduce2_counter - 1, width - 1 reduce_counter -= 1 reduce2_counter += 1 else: left_void_part = -1, -1 right_void_part = width + 1, width + 1 for col_i in range(width): # Fill with empty # TODO BS 2018-12-28: Idea can be fill other map tiles if col_i <= left_void_part[1] or col_i >= right_void_part[0]: self._current_raw_source += " " # FIXME fom type else: is_border = False distance_from_border = 0 border = None # TODO BS 2019-03-07: give info about border self._current_raw_source += self._filler.get_char( self, is_border=is_border, distance_from_border=distance_from_border, border=border, ) self._current_raw_source += "\n" return ZoneMapSource(self._kernel, self._current_raw_source)
async def manage(self, request: Request, hapic_data: HapicData) -> Description: # TODO BS: only chief (or ability) can display this affinity = self._kernel.affinity_lib.get_affinity( hapic_data.path.affinity_id) relation = self._kernel.affinity_lib.get_character_relation( affinity_id=hapic_data.path.affinity_id, character_id=hapic_data.path.character_id) if hapic_data.query.join_type == affinity_join_str[ AffinityJoinType.ACCEPT_ALL]: request_relations = (self._kernel.server_db_session.query( AffinityRelationDocument).filter( AffinityRelationDocument.request == True, AffinityRelationDocument.accepted == False, AffinityRelationDocument.affinity_id == affinity.id, ).all()) if len(request_relations) and not hapic_data.query.confirm: return Description( title=affinity.name, items=[ Part( text=f'Choisir "{hapic_data.query.join_type}" ' f"acceptera automatiquement {len(request_relations)} demande(s) " f"en attente"), Part( label="Confirmer", is_link=True, form_action= f"/affinity/{hapic_data.path.character_id}" f"/manage/{affinity.id}" f"?join_type={urllib.parse.quote(hapic_data.query.join_type)}" f"&confirm=1", ), Part( label="Annuler", is_link=True, form_action= f"/affinity/{hapic_data.path.character_id}" f"/manage/{affinity.id}", ), ], ) # proceed submited data if hapic_data.query.join_type is not None: join_type = list(affinity_join_str.keys())[list( affinity_join_str.values()).index(hapic_data.query.join_type)] # TODO BS: code it if join_type not in [ AffinityJoinType.ACCEPT_ALL, AffinityJoinType.ONE_CHIEF_ACCEPT ]: raise RollingError( "Cette fonctionnalite n'est pas encore disponible") if join_type == AffinityJoinType.ACCEPT_ALL: self._kernel.server_db_session.query( AffinityRelationDocument).filter( AffinityRelationDocument.request == True, AffinityRelationDocument.accepted == False, AffinityRelationDocument.affinity_id == affinity.id, ).update({ "accepted": True, "request": False }) affinity.join_type = join_type.value self._kernel.server_db_session.add(affinity) self._kernel.server_db_session.commit() return Description( redirect= f"/affinity/{hapic_data.path.character_id}/manage/{affinity.id}" ) join_values = [ affinity_join_str[AffinityJoinType.ACCEPT_ALL], affinity_join_str[AffinityJoinType.ONE_CHIEF_ACCEPT], affinity_join_str[AffinityJoinType.HALF_STATUS_ACCEPT], ] request_count = (self._kernel.server_db_session.query( AffinityRelationDocument).filter( AffinityRelationDocument.affinity_id == affinity.id, AffinityRelationDocument.accepted == False, AffinityRelationDocument.request == True, ).count()) member_count = (self._kernel.server_db_session.query( AffinityRelationDocument).filter( AffinityRelationDocument.affinity_id == affinity.id, AffinityRelationDocument.accepted == True, ).count()) parts = [ Part( label= f"Il y a actuellement {request_count} demande(s) d'adhésion", is_link=bool(request_count), form_action= (f"/affinity/{hapic_data.path.character_id}/manage-requests/{affinity.id}" ), ), Part( label=f"Il y a actuellement {member_count} membre(s)", is_link=True, form_action= (f"/affinity/{hapic_data.path.character_id}/manage-relations/{affinity.id}" ), ), ] return Description( title=f"Administration de {affinity.name}", items=[ Part( is_form=True, form_values_in_query=True, submit_label="Enregistrer", form_action= f"/affinity/{hapic_data.path.character_id}/manage/{affinity.id}", items=[ Part( label="Mode d'admission des nouveaux membres", choices=join_values, name="join_type", value=affinity_join_str[AffinityJoinType( affinity.join_type)], ) ], ) ] + parts, footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements"), Part( is_link=True, label="Voir les affinités", form_action=f"/affinity/{hapic_data.path.character_id}", ), ], can_be_back_url=True, )
def display_object(self) -> "DisplayObject": if self._display_object is None: raise RollingError( "You are trying to use property which is not set") return self._display_object
def get_zone_properties(self, zone_type: ZoneMapTileType) -> ZoneProperties: for zone_properties in self.world.zones_properties: if zone_properties.zone_type == zone_type: return zone_properties raise RollingError(f"No zone properties for zone {zone_type}")