def perform( self, character: "CharacterModel", build_id: int, input_: typing.Any ) -> Description: build_doc = self._kernel.build_lib.get_build_doc(build_id) if input_.quantity is None: build_description = self._kernel.game.config.builds[build_doc.build_id] required_resource = next( ( brr for brr in build_description.build_require_resources if brr.resource_id == input_.resource_id ) ) resource_description, left, left_percent = self.get_resource_infos( self._kernel, required_resource, build_doc=build_doc ) left_str = quantity_to_str(left, resource_description.unit, kernel=self._kernel) unit_str = self._kernel.translation.get(resource_description.unit) return Description( title=f"Cette construction nécessite encore {left_str} " f"de {resource_description.name} (soit {round(left_percent)}%)", can_be_back_url=True, items=[ Part( is_form=True, form_values_in_query=True, form_action=get_with_build_action_url( character_id=character.id, build_id=build_id, action_type=ActionType.BRING_RESOURCE_ON_BUILD, action_description_id=self._description.id, query_params=self.input_model_serializer.dump(input_), ), items=[ Part( label=f"Quantité ({unit_str}) ?", type_=Type.NUMBER, name="quantity" ) ], ) ], ) resource_description = self._kernel.game.config.resources[input_.resource_id] try: self._kernel.resource_lib.reduce_carried_by( character.id, resource_id=input_.resource_id, quantity=input_.quantity, commit=False ) except (NotEnoughResource, NoCarriedResource): raise ImpossibleAction( f"{character.name} ne possède pas assez de {resource_description.name}" ) self._kernel.resource_lib.add_resource_to( build_id=build_doc.id, resource_id=input_.resource_id, quantity=input_.quantity, commit=False, ) self._kernel.server_db_session.commit() build_description = self._kernel.game.config.builds[build_doc.build_id] quantity_str = quantity_to_str( input_.quantity, resource_description.unit, kernel=self._kernel ) return Description( title=f"{quantity_str} {resource_description.name} déposé pour {build_description.name}", items=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements"), Part( label="Voir le batiment", is_link=True, form_action=DESCRIBE_BUILD.format( build_id=build_doc.id, character_id=character.id ), ), ], force_back_url=f"/_describe/character/{character.id}/build_actions", )
async def manage_requests(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) requests: typing.List[ AffinityRelationDocument] = self._kernel.server_db_session.query( AffinityRelationDocument).filter( AffinityRelationDocument.affinity_id == affinity.id, AffinityRelationDocument.accepted == False, AffinityRelationDocument.request == True, ).all() data = {} try: data = await request.json() except JSONDecodeError: pass request: AffinityRelationDocument if data: for request in list(requests): choose = data.get(request.character_id) if choose: if choose == "Accepter": request.accepted = True request.request = False request.status_id = affinity.default_status_id elif choose == "Refuser": request.accepted = False request.request = False request.disallowed = True self._kernel.server_db_session.add(request) self._kernel.server_db_session.commit() requests.remove(request) form_parts = [] for request in requests: character = self._kernel.character_lib.get_document( request.character_id) form_parts.append( Part( label=f"{character.name}", name=character.id, choices=["Ne rien décider", "Accepter", "Refuser"], value="Ne rien décider", )) return Description( title=f"Demande(s) d'adhésion pour {affinity.name}", items=[ Part( is_form=True, submit_label="Enregistrer", form_action=(f"/affinity/{hapic_data.path.character_id}" f"/manage-requests/{affinity.id}"), items=form_parts, ) ], footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements"), Part( is_link=True, label=f"Voir la fiche de {affinity.name}", form_action= f"/affinity/{hapic_data.path.character_id}/see/{affinity.id}", classes=["primary"], ), Part( is_link=True, label="Voir les affinités", form_action=f"/affinity/{hapic_data.path.character_id}", ), ], can_be_back_url=True, )
async def manage_relation(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) displayer_relation = self._kernel.affinity_lib.get_character_relation( affinity_id=hapic_data.path.affinity_id, character_id=hapic_data.path.character_id) relation = self._kernel.affinity_lib.get_character_relation( affinity_id=hapic_data.path.affinity_id, character_id=hapic_data.path.relation_character_id, ) character_doc = self._kernel.character_lib.get_document( hapic_data.path.relation_character_id) # for now, only permit manage member assert relation.accepted here_url = (f"/affinity/{hapic_data.path.character_id}" f"/manage-relations/{affinity.id}/{character_doc.id}") statuses = dict(json.loads(affinity.statuses)) if hapic_data.query.disallowed is not None and hapic_data.query.disallowed: relation.disallowed = True relation.accepted = False self._kernel.server_db_session.add(relation) self._kernel.server_db_session.commit() return Description( redirect=f"/affinity/{hapic_data.path.character_id}") if hapic_data.body.status: status_id = list(statuses.keys())[list(statuses.values()).index( hapic_data.body.status)] relation.status_id = status_id self._kernel.server_db_session.add(relation) self._kernel.server_db_session.commit() status_str = statuses[relation.status_id] return Description( title=character_doc.name, items=[ Part( is_link=True, form_action=here_url + "?disallowed=1", label=f"Exclure {character_doc.name}", ), Part( is_form=True, form_action=here_url, items=[ Part( label= f"Changer le status de {character_doc.name} ?", choices=list(statuses.values()), value=status_str, name="status", ) ], ), ], footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements"), Part( is_link=True, label=f"Voir la fiche de {affinity.name}", form_action= f"/affinity/{hapic_data.path.character_id}/see/{affinity.id}", classes=["primary"], ), Part( is_link=True, label="Voir les affinités", form_action=f"/affinity/{hapic_data.path.character_id}", ), ], can_be_back_url=True, )
async def main_page(self, request: Request, hapic_data: HapicData) -> Description: affiliated_parts = [] for relation, affinity in self._kernel.affinity_lib.get_with_relation( character_id=hapic_data.path.character_id): rel_str = "" if relation.accepted: rel_str = dict(json.loads( affinity.statuses))[relation.status_id] elif relation.rejected: rel_str = "Quitté" elif relation.disallowed: rel_str = "Exclu" elif relation.request: rel_str = "Demandé" elif not relation.accepted and not relation.request and not relation.fighter: rel_str = "Plus de lien" if relation.fighter: if rel_str: rel_str = f"{rel_str}, Combattant" else: rel_str = "Combattant" rel_str = f"({rel_str})" if rel_str else "" label = f"{affinity.name} {rel_str}" if self._kernel.affinity_lib.there_is_unvote_relation( affinity, relation): label = f"*{label}" affiliated_parts.append( Part( is_link=True, form_action= f"/affinity/{hapic_data.path.character_id}/see/{affinity.id}", label=label, )) return Description( title="Affinités", items=[ Part( text= "Les affinités permettent d'exprimer à quelles communautés se rattache " "votre personnage."), Part( label="Créer une affinité", is_link=True, form_action=f"/affinity/{hapic_data.path.character_id}/new", ), Part( label="Rejoindre une affinité", is_link=True, form_action= f"/affinity/{hapic_data.path.character_id}/list", ), Part( text= "Ci-dessous les affinités avec lesquelles vous etes affiliés" ), ] + affiliated_parts, can_be_back_url=True, )
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, )
async def see(self, request: Request, hapic_data: HapicData) -> Description: 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) character = self._kernel.character_lib.get( hapic_data.path.character_id) member_count = self._kernel.affinity_lib.count_members( hapic_data.path.affinity_id) fighter_count = self._kernel.affinity_lib.count_members( hapic_data.path.affinity_id, fighter=True) parts = [] edit_relation_url = ( f"/affinity/{hapic_data.path.character_id}/edit-relation/{hapic_data.path.affinity_id}" ) if relation: fighter_str = "" if relation.fighter: fighter_str = " (Vous vous battez pour elle)" status_str = (dict(json.loads( affinity.statuses))[relation.status_id] if relation.status_id else "") if relation.accepted: parts.append( Part( label= (f"Vous êtes membre de cette affinité et vous portez le status " f"de {status_str}{fighter_str}"), is_link=True, form_action=edit_relation_url, )) elif relation.request: parts.append( Part( label= f"Vous avez demandé à être membre de cette affinité{fighter_str}", is_link=True, form_action=edit_relation_url, )) elif relation.rejected: parts.append( Part( label=f"Vous avez renié cette affinité{fighter_str}", is_link=True, form_action=edit_relation_url, )) elif relation.disallowed: parts.append( Part( label= f"Vous avez été rejeté de cette affinité{fighter_str}", is_link=True, form_action=edit_relation_url, )) elif relation.fighter: parts.append( Part( label="Vous combattez pour cette affinité", is_link=True, form_action=edit_relation_url, )) else: parts.append( Part( label= "Vous n'avez plus aucune relation avec cette affinité", is_link=True, form_action=edit_relation_url, )) else: parts.append( Part( label="Vous n'avez aucune relation avec cette affinité", is_link=True, form_action=edit_relation_url, )) # can access management page ? if affinity.direction_type == AffinityDirectionType.ONE_DIRECTOR.value: if relation and relation.status_id == CHIEF_STATUS[0]: need_action = ( "*" if self._kernel.affinity_lib.there_is_unvote_relation( affinity, relation) else "") parts.extend([ Part( label=f"{need_action}Gérer cette affinité", is_link=True, form_action= f"/affinity/{hapic_data.path.character_id}/manage/{affinity.id}", ) ]) if (ActionType.FOLLOW_CHARACTER in self._kernel.game.config.actions and ActionType.STOP_FOLLOW_CHARACTER in self._kernel.game.config.actions): follow_action = self._kernel.action_factory.create_action( ActionType.FOLLOW_CHARACTER, ActionType.FOLLOW_CHARACTER.name) stop_follow_action = self._kernel.action_factory.create_action( ActionType.STOP_FOLLOW_CHARACTER, ActionType.STOP_FOLLOW_CHARACTER.name) for followable_character in self._kernel.affinity_lib.get_chief_or_warlord_of_affinity( affinity.id, row_i=character.world_row_i, col_i=character.world_col_i): if followable_character.id == character.id: break for action in [follow_action, stop_follow_action]: try: action.check_is_possible(character, followable_character) parts.extend([ Part( text=action_link.get_as_str(), form_action=action_link.link, is_link=True, link_group_name=action_link.group_name, ) for action_link in action.get_character_actions( character, followable_character) ]) except ImpossibleAction: pass return Description( title=affinity.name, items=[ Part(text=affinity.description), Part( text= f"D'après les dires, cette affinité compte {member_count} membre(s) " f"dont {fighter_count} prêt(s) à se battre. Pour plus de renseignements " f"sur ces personnes dans la zone où se trouve votre personnage, " f"rendez-vous sur la page de la zone."), ] + parts, footer_links=[ Part( is_link=True, label="Retour aux affinités", form_action=f"/affinity/{hapic_data.path.character_id}", ) ], can_be_back_url=True, )
async def edit_relation(self, request: Request, hapic_data: HapicData) -> Description: 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.request is not None or hapic_data.query.rejected is not None or hapic_data.query.fighter is not None): title = "ERROR" return_ = False if hapic_data.query.request and relation: title = f"Requete pour etre membre de {affinity.name} exprimé" if affinity.join_type == AffinityJoinType.ACCEPT_ALL and not ( relation.disallowed or relation.rejected): relation.accepted = True else: relation.request = True return_ = True elif hapic_data.query.request == 0 and relation: title = f"Vous ne demandez plus à être membre de {affinity.name}" relation.request = False return_ = True elif hapic_data.query.request and not relation: title = f"Requete pour etre membre de {affinity.name} exprimé" self._kernel.affinity_lib.join( character_id=hapic_data.path.character_id, affinity_id=hapic_data.path.affinity_id, accepted=True if affinity.join_type == AffinityJoinType.ACCEPT_ALL.value else False, fighter=True if hapic_data.query.fighter else False, request=True, ) return_ = True elif hapic_data.query.rejected and not relation: pass # should not be here elif hapic_data.query.rejected and relation: title = f"Vous avez déclaré avoir abandonné {affinity.name}" relation.accepted = False relation.request = False relation.rejected = True return_ = True elif hapic_data.query.fighter and not relation: title = f"Vous vous battez désormais pour {affinity.name}" self._kernel.affinity_lib.join( character_id=hapic_data.path.character_id, affinity_id=hapic_data.path.affinity_id, accepted=True if affinity.join_type == AffinityJoinType.ACCEPT_ALL else False, fighter=True, request=False, ) return_ = True elif hapic_data.query.fighter and relation: title = (f"Vous combatrez désormais dès lors qu'un membre de" f" {affinity.name} sera attaqué") relation.fighter = True return_ = True elif hapic_data.query.fighter == 0: title = ( f"Vous ne combatrez désormais plus lorsqu'un membre de" f" {affinity.name} sera attaqué") relation.fighter = False return_ = True if return_: self._kernel.server_db_session.commit() return Description( title=title, footer_links=[ Part( is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements", ), Part( is_link=True, label=f"Voir la fiche de {affinity.name}", form_action= f"/affinity/{hapic_data.path.character_id}/see/{affinity.id}", classes=["primary"], ), Part( is_link=True, label="Voir les affinités", form_action= f"/affinity/{hapic_data.path.character_id}", ), ], ) items = [] # FIXME BS: Must be in presence of director to request become member if not relation: items.extend([ Part( is_link=True, form_action=( f"/affinity/{hapic_data.path.character_id}" f"/edit-relation/{hapic_data.path.affinity_id}" f"?request=1"), label="Exprimer le souhait de devenir membre", ), Part( is_link=True, form_action=( f"/affinity/{hapic_data.path.character_id}" f"/edit-relation/{hapic_data.path.affinity_id}" f"?request=1&fighter=1"), label= "Exprimer le souhait de devenir membre et de me battre avec eux", ), Part( is_link=True, form_action=( f"/affinity/{hapic_data.path.character_id}" f"/edit-relation/{hapic_data.path.affinity_id}" f"?request=0&fighter=1"), label="Me battre pour eux", ), ]) else: if not relation.request: if not relation.fighter: items.append( Part( is_link=True, form_action=( f"/affinity/{hapic_data.path.character_id}" f"/edit-relation/{hapic_data.path.affinity_id}" f"?request=1&fighter=1"), label= "Exprimer le souhait de devenir membre et de me battre pour elle", )) else: items.append( Part( is_link=True, form_action=( f"/affinity/{hapic_data.path.character_id}" f"/edit-relation/{hapic_data.path.affinity_id}" f"?request=1"), label="Exprimer le souhait de devenir membre", )) else: items.append( Part( is_link=True, form_action=( f"/affinity/{hapic_data.path.character_id}" f"/edit-relation/{hapic_data.path.affinity_id}" f"?request=0"), label="Ne plus demander à être membre", )) if relation.accepted: items.append( Part( is_link=True, form_action=( f"/affinity/{hapic_data.path.character_id}" f"/edit-relation/{hapic_data.path.affinity_id}" f"?rejected=1"), label="Quitter cette affinité", )) if relation.fighter: items.append( Part( is_link=True, form_action=( f"/affinity/{hapic_data.path.character_id}" f"/edit-relation/{hapic_data.path.affinity_id}" f"?fighter=0"), label="Ne plus se battre pour cette affinité", )) else: items.append( Part( is_link=True, form_action=( f"/affinity/{hapic_data.path.character_id}" f"/edit-relation/{hapic_data.path.affinity_id}" f"?fighter=1"), label="Me battre pour cette affinité", )) return Description( title=affinity.name, items=items, footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements"), Part( is_link=True, label=f"Voir la fiche de {affinity.name}", form_action= f"/affinity/{hapic_data.path.character_id}/see/{affinity.id}", classes=["primary"], ), Part( is_link=True, label="Voir les affinités", form_action=f"/affinity/{hapic_data.path.character_id}", ), ], )
def perform(self, character: "CharacterModel", with_character: "CharacterModel", input_: TakeFromModel) -> Description: if input_.take_stuff_id is not None: stuff: StuffModel = self._kernel.stuff_lib.get_stuff( input_.take_stuff_id) likes_this_stuff = self._kernel.stuff_lib.get_carried_by( with_character.id, exclude_crafting=False, stuff_id=stuff.stuff_id) if input_.take_stuff_quantity is None: if len(likes_this_stuff) > 1: return Description( title=f"Prendre {stuff.name} sur {with_character.name}", items=[ Part( is_form=True, form_values_in_query=True, form_action=self._get_url( character, with_character, input_), submit_label="Prendre", items=[ Part( label="Quantité ?", type_=Type.NUMBER, name="take_stuff_quantity", default_value=str( len(likes_this_stuff)), ) ], ) ], can_be_back_url=True, ) input_.take_stuff_quantity = 1 for i in range(input_.take_stuff_quantity): self._kernel.stuff_lib.set_carried_by(likes_this_stuff[i].id, character.id) if input_.take_resource_id is not None: resource_description = self._kernel.game.config.resources[ input_.take_resource_id] carried_resource = self._kernel.resource_lib.get_one_carried_by( with_character.id, input_.take_resource_id) if input_.take_resource_quantity is None: unit_str = self._kernel.translation.get( resource_description.unit) return Description( title= f"Prendre {resource_description.name} sur {with_character.name}", items=[ Part( is_form=True, form_values_in_query=True, form_action=self._get_url(character, with_character, input_), submit_label="Prendre", items=[ Part( label=f"Quantité ({unit_str}) ?", type_=Type.NUMBER, name="take_resource_quantity", default_value=str( carried_resource.quantity), ) ], ) ], can_be_back_url=True, ) self._kernel.resource_lib.reduce_carried_by( character_id=with_character.id, resource_id=input_.take_resource_id, quantity=input_.take_resource_quantity, ) self._kernel.resource_lib.add_resource_to( character_id=character.id, resource_id=input_.take_resource_id, quantity=input_.take_resource_quantity, ) return self._get_take_something_description(character, with_character, input_)
async def new(self, request: Request, hapic_data: HapicData) -> Description: try: data = await request.json() except JSONDecodeError: data = {} if data.get("name"): affinity_doc = self._kernel.affinity_lib.create( name=data["name"], join_type=AffinityJoinType.ONE_CHIEF_ACCEPT, direction_type=AffinityDirectionType.ONE_DIRECTOR, commit=True, ) self._kernel.affinity_lib.join( character_id=hapic_data.path.character_id, affinity_id=affinity_doc.id, accepted=True, status_id=CHIEF_STATUS[0], fighter=True, commit=True, ) return Description( title="Affinités créée", items=[Part(text="Et vous en êtes le chef")], footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements"), Part( is_link=True, label=f"Voir la fiche de {affinity_doc.name}", form_action= f"/affinity/{hapic_data.path.character_id}/see/{affinity_doc.id}", classes=["primary"], ), Part( is_link=True, label="Voir les affinités", form_action=f"/affinity/{hapic_data.path.character_id}", ), ], ) return Description( title="Créer une affinité", items=[ Part( is_form=True, form_action=f"/affinity/{hapic_data.path.character_id}/new", items=[Part(label="Nom", name="name", type_=Type.STRING)], ) ], footer_links=[ Part( is_link=True, label="Retour aux affinités", form_action=f"/affinity/{hapic_data.path.character_id}", ) ], )
async def start(self, request: Request, hapic_data: HapicData) -> Description: character_doc = self._kernel.character_lib.get_document(hapic_data.path.character_id) zone_characters = self._kernel.character_lib.get_zone_players( row_i=character_doc.world_row_i, col_i=character_doc.world_col_i, exclude_ids=[hapic_data.path.character_id], ) try: data = await request.json() if data.get("message"): selected_character_ids = [c.id for c in zone_characters if data.get(c.id) == "on"] if not selected_character_ids: return Description( title="Démarrer une nouvelle conversation", items=[ Part(text="Vous devez choisir au moins un personnage"), Part( is_link=True, label="Retour", form_action=f"/conversation/{hapic_data.path.character_id}/start", ), Part( is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements", ), ], ) conversation_id = self._kernel.message_lib.add_conversation_message( author_id=hapic_data.path.character_id, subject=data.get("subject", "Une conversation"), message=data["message"], concerned=selected_character_ids, ) return Description( redirect=f"/conversation/{hapic_data.path.character_id}/read/{conversation_id}" ) except JSONDecodeError: pass # no json (i guess) if not zone_characters: return Description( title="Démarrer une nouvelle conversation", items=[ Part(text="Il n'y a personne ici avec qui converser"), Part( is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements" ), Part( is_link=True, label="Retourner aux conversations", form_action=f"/conversation/{hapic_data.path.character_id}", ), ], ) character_parts = [] for zone_character in zone_characters: character_parts.append( Part( label=zone_character.name, value="on", is_checkbox=True, name=zone_character.id, # FIXME BS NOW: test it checked=zone_character.id == hapic_data.query.with_character_id, ) ) return Description( title="Démarrer une nouvelle conversation", items=[ Part( text="Vous devez choisir les personnages avec qui entretenir cette conversation" ), Part( is_form=True, form_action=f"/conversation/{hapic_data.path.character_id}/start", items=character_parts + [ Part(label="Choisissez un titre", type_=Type.STRING, name="subject"), Part(label="Saisissez votre élocuction", type_=Type.STRING, name="message"), ], ), ], footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements"), Part( is_link=True, label="Retourner aux conversations", form_action=f"/conversation/{hapic_data.path.character_id}", ), ], )
def perform( self, character: "CharacterModel", stuff: "StuffModel", input_: ContinueStuffModel ) -> Description: bonus = character.get_skill_value("intelligence") + character.get_skill_value("crafts") bonus_divider = max(1.0, (bonus * 2) / DEFAULT_MAXIMUM_SKILL) remain_ap = stuff.ap_required - stuff.ap_spent remain_ap_for_character = remain_ap / bonus_divider if not input_.ap: return Description( title=f"Continuer de travailler sur {stuff.name}", items=[ Part( is_form=True, form_values_in_query=True, form_action=get_with_stuff_action_url( character_id=character.id, action_type=ActionType.CONTINUE_STUFF_CONSTRUCTION, query_params={}, action_description_id=self._description.id, stuff_id=stuff.id, ), items=[ Part( text=f"Il reste {round(remain_ap, 3)} PA à passer ({round(remain_ap_for_character, 3)} avec vos bonus)" ), Part( label=f"Combien de points d'actions dépenser ?", type_=Type.NUMBER, name="ap", ), ], ) ], ) consume_ap = min(remain_ap, input_.ap * bonus_divider) stuff_doc = self._kernel.stuff_lib.get_stuff_doc(stuff.id) stuff_doc.ap_spent = float(stuff_doc.ap_spent) + consume_ap self._kernel.character_lib.reduce_action_points(character.id, consume_ap, commit=False) if stuff_doc.ap_spent >= stuff_doc.ap_required: stuff_doc.under_construction = False title = f"Construction de {stuff.name} terminé" else: title = f"Construction de {stuff.name} avancé" self._kernel.server_db_session.commit() return Description( title=title, footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements"), Part( is_link=True, label="Voir l'objet commencé", form_action=DESCRIBE_LOOK_AT_STUFF_URL.format( character_id=character.id, stuff_id=stuff_doc.id ), classes=["primary"], ), ], force_back_url=f"/_describe/character/{character.id}/on_place_actions", )
def perform(self, character: "CharacterModel", input_: BeginStuffModel) -> Description: if not input_.description: require_txts = [] for consume in self._description.properties["consume"]: if "resource" in consume: resource_id = consume["resource"] resource_description = self._kernel.game.config.resources[resource_id] quantity_str = quantity_to_str( consume["quantity"], resource_description.unit, self._kernel ) require_txts.append(f"{quantity_str} de {resource_description.name}") elif "stuff" in consume: stuff_id = consume["stuff"] stuff_properties = self._kernel.game.stuff_manager.get_stuff_properties_by_id( stuff_id ) require_txts.append(f"{consume['quantity']} de {stuff_properties.name}") return Description( title=f"Commencer {self._description.name}", items=[ Part( is_form=True, form_values_in_query=True, form_action=get_character_action_url( character_id=character.id, action_type=ActionType.BEGIN_STUFF_CONSTRUCTION, query_params={}, action_description_id=self._description.id, ), items=[Part(text="Consommera :")] + [Part(text=txt) for txt in require_txts] + [ Part( label=f"Vous pouvez fournir une description de l'objet", type_=Type.STRING, name="description", default_value=self._description.properties["default_description"], ) ], ) ], ) for consume in self._description.properties["consume"]: if "resource" in consume: resource_id = consume["resource"] self._kernel.resource_lib.reduce_carried_by( character.id, resource_id=resource_id, quantity=consume["quantity"], commit=False, ) elif "stuff" in consume: stuff_id = consume["stuff"] carried_stuffs = self._kernel.stuff_lib.get_carried_by( character.id, stuff_id=stuff_id ) for i in range(consume["quantity"]): self._kernel.stuff_lib.destroy(carried_stuffs[i].id, commit=False) stuff_id = self._description.properties["produce_stuff_id"] stuff_properties = self._kernel.game.stuff_manager.get_stuff_properties_by_id(stuff_id) stuff_doc = self._kernel.stuff_lib.create_document_from_stuff_properties( properties=stuff_properties, world_row_i=character.world_row_i, world_col_i=character.world_col_i, zone_row_i=character.zone_row_i, zone_col_i=character.zone_col_i, ) stuff_doc.description = input_.description or "" stuff_doc.carried_by_id = character.id stuff_doc.ap_spent = 0.0 stuff_doc.ap_required = self._description.properties["craft_ap"] stuff_doc.under_construction = True self._kernel.stuff_lib.add_stuff(stuff_doc, commit=False) self._kernel.character_lib.reduce_action_points( character.id, cost=self.get_cost(character), commit=False ) self._kernel.server_db_session.commit() return Description( title=f"{stuff_properties.name} commencé", footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements"), Part( is_link=True, label="Voir l'objet commencé", form_action=DESCRIBE_LOOK_AT_STUFF_URL.format( character_id=character.id, stuff_id=stuff_doc.id ), classes=["primary"], ), ], force_back_url=f"/_describe/character/{character.id}/on_place_actions", )
def perform(self, character: "CharacterModel", resource_id: str, input_: input_model) -> Description: base_cost = self.get_cost(character, resource_id=resource_id) resource_mix_description = self._kernel.game.config.resource_mixs[ input_.resource_mix_id] unit_name = self._kernel.translation.get( resource_mix_description.produce_resource.unit) cost_per_unit = resource_mix_description.cost if input_.quantity is None: required = ", ".join([ f"{round(r.coeff * 100)}% {r.resource.name}" for r in resource_mix_description.required_resources ]) return Description( title=f"Faire {resource_mix_description.produce_resource.name}", items=[ Part( is_form=True, form_values_in_query=True, form_action=get_with_resource_action_url( character_id=character.id, action_type=ActionType.MIX_RESOURCES, resource_id=resource_id, query_params=self.input_model_serializer.dump( input_), action_description_id=self._description.id, ), items=[ Part( label= f"Quantité en {unit_name} (coût: {base_cost} + {cost_per_unit} par {unit_name}, avec {required}) ?", type_=Type.NUMBER, name="quantity", ) ], ) ], ) # Make mix for required_resource in resource_mix_description.required_resources: required_quantity = required_resource.coeff * input_.quantity self._kernel.resource_lib.reduce_carried_by( character.id, required_resource.resource.id, required_quantity, commit=False) self._kernel.resource_lib.add_resource_to( character_id=character.id, resource_id=resource_mix_description.produce_resource.id, quantity=input_.quantity, commit=False, ) self._kernel.character_lib.reduce_action_points( character_id=character.id, cost=self.get_cost(character, resource_id=resource_id, input_=input_), commit=False, ) self._kernel.server_db_session.commit() return Description( title=f"{input_.quantity} " f"{resource_mix_description.produce_resource.name} {unit_name} produits", footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements"), Part( is_link=True, label="Voir l'inventaire", form_action= f"/_describe/character/{character.id}/inventory", classes=["primary"], ), ], )
def perform( self, character: "CharacterModel", with_character: "CharacterModel", input_: ProposeTeachKnowledgeModel, ) -> Description: knowledge_description = self._kernel.game.config.knowledge[ input_.knowledge_id] if input_.ap is None: max_turns = self._kernel.game.config.max_action_propose_turns return Description( title= f"Enseigner {knowledge_description.name} à {with_character.name}", items=[ Part( is_form=True, form_values_in_query=True, form_action=self._get_url(character, with_character, input_), items=[ Part( label= f"Passer combien de points d'actions sur ce cours ?", type_=Type.NUMBER, name="ap", ), Part( label=( f"Proposition valable combien de tours " f"(max {max_turns}, en cours compris) ?"), type_=Type.NUMBER, name="expire", default_value="1", ), ], ) ], ) pending_action_document = self._kernel.action_factory.create_pending_action( action_scope=ActionScope.WITH_CHARACTER, action_type=ActionType.TEACH_KNOWLEDGE, action_description_id=ActionType.TEACH_KNOWLEDGE.value, character_id=character.id, with_character_id=with_character.id, parameters=TeachKnowledgeAction.input_model_serializer.dump( TeachKnowledgeModel(knowledge_id=input_.knowledge_id, ap=input_.ap)), expire_at_turn=self._kernel.universe_lib.get_last_state().turn + (input_.expire - 1), suggested_by=character.id, name=(f"Prendre un cours de {knowledge_description.name} avec" f" {character.name} pendant {input_.ap} points d'actions"), delete_after_first_perform=True, ) self._kernel.action_factory.add_pending_action_authorization( pending_action_id=pending_action_document.id, authorized_character_id=with_character.id) return Description( title="Proposition effectué", footer_links=[ Part(is_link=True, go_back_zone=True, label="Retourner à l'écran de déplacements") ], )