コード例 #1
0
 def __init__(self, nom, parent=None, modele=None):
     """Constructeur de la classe"""
     BaseObj.__init__(self)
     self.nom = nom
     self.synonymes = []
     self.titre = "un détail aux alentours"
     self.description = Description(parent=self)
     self.positions = {}
     self.est_visible = True
     self.script = ScriptDetail(self)
     self.peut_asseoir = False
     self.peut_allonger = False
     self.facteur_asseoir = 1.1
     self.facteur_allonger = 1.2
     self.connecteur = "sur"
     self.nb_places_assises = 1
     self.nb_places_allongees = 1
     self.parent = parent
     self.flags = 0
     self.supporte = None
     self._peut_supporter = 0.0
     self.message_supporte = "Dessus se trouve"
     self.message_installation = "Vous posez %objet sur %detail."
     self.message_desinstallation = "Vous retirez %objet% de %detail."
     if modele is not None:
         self.synonymes = modele.synonymes
         self.titre = modele.titre
         self.description = modele.description
     # On passe le statut en CONSTRUIT
     self._construire()
コード例 #2
0
 def __init__(self, cle):
     """Constructeur du modèle."""
     BaseObj.__init__(self)
     self.cle = cle
     self.nom = "un navire"
     self.vehicules = []
     self.salles = {}
     self.poids_max = 200
     self.tirant_eau = 3
     self.fond_plat = False
     self.graph = {}
     self.m_valeur = 1000
     self.duree_construction = 60
     self.description = Description(parent=self)
     self.description_vente = Description(parent=self)
     self.canot = False
     self.masculin = True
     self.peut_conquerir = True
     self.niveau = 5
     self.cale_max = 200
     self.facteur_rames = 0.8
     self.facteurs_orientations = {
         "vent debout": -0.3,
         "au près": 0.5,
         "bon plein": 0.8,
         "largue": 1.2,
         "grand largue": 0.9,
         "vent arrière": 0.7,
     }
     self.descriptions_independantes = False
コード例 #3
0
ファイル: mudmail.py プロジェクト: vlegoff/tsunami
 def __init__(self, parent=None, expediteur=None, source=None):
     """Constructeur de la classe"""
     BaseObj.__init__(self)
     self.parent = parent
     self.id = -1
     self.notifier = True
     if source is not None: # édition d'un brouillon
         self._etat = BROUILLON
         self.sujet = str(source.sujet)
         self.expediteur = expediteur
         self.liste_dest = []
         for d in list(source.liste_dest):
             self.liste_dest.append(d)
         self.aliases = source.aliases
         self.copies_a = source.copies_a
         self.contenu = Description(parent=self, scriptable=False)
         self.contenu.ajouter_paragraphe(str(source.contenu))
         self.id_source = int(source.id)
     else:
         self._etat = EN_COURS
         self.sujet = "aucun sujet"
         self.expediteur = expediteur
         self.liste_dest = []
         self.aliases = []
         self.copies_a = []
         self.contenu = Description(parent=self)
         self.id_source = 0
     self.destinataire = None
     self.date = None
     self.lu = False
     # On passe le statut en CONSTRUIT
     self._construire()
コード例 #4
0
 def __init__(self, parent=None, expediteur=None, source=None):
     """Constructeur de la classe"""
     BaseObj.__init__(self)
     self.parent = parent
     self.id = -1
     self.notifier = True
     if source is not None:  # édition d'un brouillon
         self._etat = BROUILLON
         self.sujet = str(source.sujet)
         self.expediteur = expediteur
         self.liste_dest = []
         for d in list(source.liste_dest):
             self.liste_dest.append(d)
         self.aliases = source.aliases
         self.copies_a = source.copies_a
         self.contenu = Description(parent=self, scriptable=False)
         self.contenu.ajouter_paragraphe(str(source.contenu))
         self.id_source = int(source.id)
     else:
         self._etat = EN_COURS
         self.sujet = "aucun sujet"
         self.expediteur = expediteur
         self.liste_dest = []
         self.aliases = []
         self.copies_a = []
         self.contenu = Description(parent=self)
         self.id_source = 0
     self.destinataire = None
     self.date = None
     self.lu = False
     # On passe le statut en CONSTRUIT
     self._construire()
コード例 #5
0
ファイル: fiche.py プロジェクト: wkta/tsunami
 def __init__(self, prototype):
     """Constructeur du matelot."""
     BaseObj.__init__(self)
     self.prototype = prototype
     self.nom_singulier = "un matelot"
     self.nom_pluriel = "matelots"
     self.poste_defaut = "matelot"
     self.description = Description(parent=self)
     self.aptitudes = {}
     self.m_valeur = 20
コード例 #6
0
    def __init__(self, cle=""):
        """Constructeur de l'objet"""
        BaseType.__init__(self, cle)
        self.nom_recette = "la recette de quelque chose"
        self.contenu = Description(parent=self, scriptable=False)

        # Éditeurs
        self.etendre_editeur("o", "nom de la recette", Uniligne, self,
                             "nom_recette", 0, LOWER)
        self.etendre_editeur("c", "contenu de la recette", EdtDesc, self,
                             "contenu")
コード例 #7
0
    def __init__(self, cle=""):
        """Constructeur d'un type"""
        if cle:
            valider_cle(cle)

        BaseObj.__init__(self)
        self.cle = cle

        self._attributs = {}
        self.nom = "un élément inconnu"
        self.description = Description(parent=self)

        # Editeur
        self._extensions_editeur = []
コード例 #8
0
ファイル: description.py プロジェクト: vlegoff/tsunami
    def afficher_apercu(apercu, objet, valeur):
        if valeur is None:
            return ""

        if isinstance(valeur, str):
            description = Desc(parent=objet, scriptable=False)

            for paragraphe in valeur.split("\n"):
                description.ajouter_paragraphe(
                        paragraphe.replace("|nl|", " "))

            valeur = description

        valeur = valeur.paragraphes_indentes
        return apercu.format(objet=objet, valeur=valeur)
コード例 #9
0
ファイル: detail.py プロジェクト: vlegoff/tsunami
 def __init__(self, nom, parent=None, modele=None):
     """Constructeur de la classe"""
     BaseObj.__init__(self)
     self.nom = nom
     self.synonymes = []
     self.titre = "un détail aux alentours"
     self.description = Description(parent=self)
     self.positions = {}
     self.est_visible = True
     self.script = ScriptDetail(self)
     self.peut_asseoir = False
     self.peut_allonger = False
     self.facteur_asseoir = 1.1
     self.facteur_allonger = 1.2
     self.connecteur = "sur"
     self.nb_places_assises = 1
     self.nb_places_allongees = 1
     self.parent = parent
     self.flags = 0
     self.supporte = None
     self._peut_supporter = 0.0
     self.message_supporte = "Dessus se trouve"
     self.message_installation = "Vous posez %objet sur %detail."
     self.message_desinstallation = "Vous retirez %objet% de %detail."
     if modele is not None:
         self.synonymes = modele.synonymes
         self.titre = modele.titre
         self.description = modele.description
     # On passe le statut en CONSTRUIT
     self._construire()
コード例 #10
0
ファイル: description.py プロジェクト: vlegoff/tsunami
    def __init__(self, pere, objet=None, attribut=None):
        """Constructeur de l'éditeur"""
        attribut = attribut or "description"
        Editeur.__init__(self, pere, objet, attribut)
        self.opts.echp_sp_cars = False
        self.nom_attribut = attribut
        contenu = ""
        if objet:
            contenu = getattr(self.objet, self.nom_attribut)
            if contenu is None:
                setattr(self.objet, self.nom_attribut, "")
            else:
                contenu = str(contenu)

        self.description_complete = Desc(parent=objet, scriptable=False)
        if contenu:
            for paragraphe in contenu.split("\n"):
                self.description_complete.ajouter_paragraphe(
                        paragraphe.replace("|nl|", " "))

        self.ajouter_option("?", self.opt_aide)
        self.ajouter_option("j", self.opt_ajouter_paragraphe)
        self.ajouter_option("a", self.opt_inserer_paragraphe)
        self.ajouter_option("d", self.opt_supprimer)
        self.ajouter_option("r", self.opt_remplacer)
        self.ajouter_option("e", self.opt_editer_evt)
        self.ajouter_option("t", self.opt_tabulations)
        self.ajouter_option("de", self.opt_supprimer_evt)
        self.ajouter_option("re", self.opt_renommer_evt)
        self.ajouter_option("o", self.opt_editer_options)
コード例 #11
0
ファイル: salle.py プロジェクト: stormi/tsunami
    def __init__(self, zone, mnemonic, x=0, y=0, z=0, valide=True):
        """Constructeur de la salle"""
        BaseObj.__init__(self)
        self._nom_zone = zone
        self._mnemonic = mnemonic
        self.coords = Coordonnees(x, y, z, valide, self)
        self.nom_terrain = "ville"
        self.titre = ""
        self.description = Description(parent=self)
        self.sorties = Sorties(parent=self)
        self.details = Details(parent=self)
        self._personnages = []
        self.objets_sol = ObjetsSol(parent=self)
        self.script = ScriptSalle(self)
        self.interieur = False
        self.illuminee = False
        self.magasin = None
        self.flags = 0

        # Repop
        self.pnj_repop = {}

        # Etendue
        self.etendue = None

        # Affections
        self.affections = {}

        # Décors
        self.decors = []

        self._construire()
コード例 #12
0
ファイル: description_flottante.py プロジェクト: wkta/tsunami
 def __init__(self, cle):
     """Constructeur de la description flottante."""
     BaseObj.__init__(self)
     self.cle = cle
     self.description = Description(parent=self, scriptable=True)
     self.details = Details(parent=self)
     self._construire()
コード例 #13
0
 def __init__(self, prototype):
     BaseObj.__init__(self)
     self.nom_singulier = "un bonhomme de neige"
     self.nom_pluriel = "bonhommes de neige"
     self.etat_singulier = "se tient ici"
     self.etat_pluriel = "se tiennent ici"
     self.description = Description(parent=self, scriptable=False)
     self._construire()
コード例 #14
0
ファイル: repere.py プロジェクト: vlegoff/tsunami
 def __init__(self, x, y):
     """Constructeur du repère."""
     BaseObj.__init__(self)
     self.x = x
     self.y = y
     self.nom = "un repère indéfini"
     self.description = Description(parent=self)
     self.amplificateur_portee = 1.5
コード例 #15
0
ファイル: recette.py プロジェクト: vlegoff/tsunami
class Recette(BaseType):

    """Type d'objet : recette de cuisine.

    Ce type d'objet permet de conserver une recette de cuisine sur
    un parchemin par exemple. En plus des attributs habituels, la
    recette de cuisine possède un nom (le ragoût de lapin)
    et un contenu.

    """

    nom_type = "recette"

    def __init__(self, cle=""):
        """Constructeur de l'objet"""
        BaseType.__init__(self, cle)
        self.nom_recette = "la recette de quelque chose"
        self.contenu = Description(parent=self, scriptable=False)

        # Éditeurs
        self.etendre_editeur("o", "nom de la recette", Uniligne, self,
                "nom_recette", 0, LOWER)
        self.etendre_editeur("c", "contenu de la recette", EdtDesc, self,
                "contenu")

    def travailler_enveloppes(self, enveloppes):
        """Travail sur les enveloppes"""
        contenu = enveloppes["c"]
        contenu.aide_courte = \
            "| |tit|" + "Contenu de la recette {}".format(self.cle).ljust(
            76) + "|ff||\n" + "-" * 79

        nom = enveloppes["o"]
        nom.apercu = "{valeur}"
        nom.prompt = "Nom de la recette"
        nom.aide_courte = \
            "Entrez |ent|le nom de la recette|ff| ou |cmd|/|ff| pour " \
            "revenir à la fenêtre parente.\n\n" \
            "Choisissez le nom de la recette avec article (par exemple,\n" \
            "|ent|le poulet farci aux chatâignes|ff|).\n\n"\
            "Nom actuel : {objet.nom_recette}"

    def regarder(self, personnage):
        """Le personnage regarde l'objet."""
        prototype = getattr(self, "prototype", self)
        variables = {
            "recette": prototype.nom_recette,
            "RECETTE": prototype.nom_recette.upper(),
            "Recette": prototype.nom_recette.capitalize(),
            "contenu": prototype.contenu.regarder(personnage, self),
        }
        BaseType.regarder(self, personnage, variables)

    def get_structure(self, structure):
        """Retourne la structure étenduee."""
        BaseType.get_structure(self, structure)
        structure.nom_recette = self.nom_recette
        structure.contenu = self.contenu.regarder(None, self)
コード例 #16
0
class Recette(BaseType):
    """Type d'objet : recette de cuisine.

    Ce type d'objet permet de conserver une recette de cuisine sur
    un parchemin par exemple. En plus des attributs habituels, la
    recette de cuisine possède un nom (le ragoût de lapin)
    et un contenu.

    """

    nom_type = "recette"

    def __init__(self, cle=""):
        """Constructeur de l'objet"""
        BaseType.__init__(self, cle)
        self.nom_recette = "la recette de quelque chose"
        self.contenu = Description(parent=self, scriptable=False)

        # Éditeurs
        self.etendre_editeur("o", "nom de la recette", Uniligne, self,
                             "nom_recette", 0, LOWER)
        self.etendre_editeur("c", "contenu de la recette", EdtDesc, self,
                             "contenu")

    def travailler_enveloppes(self, enveloppes):
        """Travail sur les enveloppes"""
        contenu = enveloppes["c"]
        contenu.aide_courte = \
            "| |tit|" + "Contenu de la recette {}".format(self.cle).ljust(
            76) + "|ff||\n" + "-" * 79

        nom = enveloppes["o"]
        nom.apercu = "{valeur}"
        nom.prompt = "Nom de la recette"
        nom.aide_courte = \
            "Entrez |ent|le nom de la recette|ff| ou |cmd|/|ff| pour " \
            "revenir à la fenêtre parente.\n\n" \
            "Choisissez le nom de la recette avec article (par exemple,\n" \
            "|ent|le poulet farci aux chatâignes|ff|).\n\n"\
            "Nom actuel : {objet.nom_recette}"

    def regarder(self, personnage):
        """Le personnage regarde l'objet."""
        prototype = getattr(self, "prototype", self)
        variables = {
            "recette": prototype.nom_recette,
            "RECETTE": prototype.nom_recette.upper(),
            "Recette": prototype.nom_recette.capitalize(),
            "contenu": prototype.contenu.regarder(personnage, self),
        }
        BaseType.regarder(self, personnage, variables)

    def get_structure(self, structure):
        """Retourne la structure étenduee."""
        BaseType.get_structure(self, structure)
        structure.nom_recette = self.nom_recette
        structure.contenu = self.contenu.regarder(None, self)
コード例 #17
0
ファイル: decor.py プロジェクト: wkta/tsunami
 def __init__(self, cle):
     BaseObj.__init__(self)
     self.cle = cle
     self.nom_singulier = "une décoration"
     self.nom_pluriel = "décorations"
     self.etat_singulier = "se trouve ici"
     self.etat_pluriel = "se trouvent ici"
     self.description = Description(parent=self, scriptable=False)
     self._construire()
コード例 #18
0
ファイル: etape.py プロジェクト: wkta/tsunami
 def __init__(self, quete):
     """Constructeur de l'étape."""
     BaseObj.__init__(self)
     self.type = "etape"
     self.quete = quete
     self.niveau = ()
     self.titre = "non renseigné"
     self.description = Description("", self)
     self.test = None
     self._construire()
コード例 #19
0
 def __init__(self, createur):
     """Constructeur d'un évènement"""
     BaseObj.__init__(self)
     self.id = type(self).id_actuel
     type(self).id_actuel += 1
     self.date = datetime.date.today()
     self.responsables = [createur]
     self.titre = "Sans Titre"
     self.description = Description(parent=self)
     self.commentaires = []
コード例 #20
0
 def __init__(self, nom):
     """Constructeur d'une race."""
     BaseObj.__init__(self)
     self.nom = nom
     self.description = Description(parent=self)
     self.stats = Stats(parent=self)
     self.genres = Genres(parent=self)
     self.squelette = None
     self.flags = 0
     self._construire()
コード例 #21
0
 def __init__(self, structure, nom):
     """Constructeur d'un éditeur personnalisé."""
     BaseObj.__init__(self)
     self.structure = structure
     self.nom = nom
     self.titre = nom
     self.description = Description(parent=self, scriptable=False)
     self.apercu = "$valeur"
     self.raccourci = None
     self._construire()
コード例 #22
0
ファイル: fiche.py プロジェクト: vlegoff/tsunami
 def __init__(self, prototype):
     """Constructeur du matelot."""
     BaseObj.__init__(self)
     self.prototype = prototype
     self.nom_singulier = "un matelot"
     self.nom_pluriel = "matelots"
     self.poste_defaut = "matelot"
     self.description = Description(parent=self)
     self.aptitudes = {}
     self.m_valeur = 20
コード例 #23
0
 def __init__(self, sujet):
     """Constructeur de la News Letter."""
     BaseObj.__init__(self)
     self.sujet = sujet
     self.contenu = Description(parent=self, scriptable=False)
     self.statut = "brouillon"
     self.editee = False
     self.date_creation = datetime.now()
     self.date_envoi = None
     self.nombre_envois = 0
     self._construire()
コード例 #24
0
ファイル: recette.py プロジェクト: vlegoff/tsunami
    def __init__(self, cle=""):
        """Constructeur de l'objet"""
        BaseType.__init__(self, cle)
        self.nom_recette = "la recette de quelque chose"
        self.contenu = Description(parent=self, scriptable=False)

        # Éditeurs
        self.etendre_editeur("o", "nom de la recette", Uniligne, self,
                "nom_recette", 0, LOWER)
        self.etendre_editeur("c", "contenu de la recette", EdtDesc, self,
                "contenu")
コード例 #25
0
ファイル: squelette.py プロジェクト: wkta/tsunami
    def __init__(self, cle):
        """Constructeur du squelette"""
        BaseObj.__init__(self)
        self.cle = cle
        self.nom = "un squelette"
        self.description = Description(parent=self)
        self.__membres = []
        self.__groupes = {}

        # Liste des personnages dont l'équipement dérive de ce squelette
        self.personnages = []
        self._construire()
コード例 #26
0
 def __init__(self, nom, cycle):
     """Constructeur de la période."""
     BaseObj.__init__(self)
     self.nom = nom
     self.cycle = cycle
     self.nom_singulier = "une plante"
     self.nom_pluriel = "plantes"
     self.description = Description(parent=self)
     self.fin = (0, 0)
     self.variation = 0
     self.elements = []
     self.poids_max = 0
     self.visible = True
コード例 #27
0
    def __init__(self, cle=""):
        """Constructeur d'un type"""
        BaseObj.__init__(self)
        self.cle = cle
        self._attributs = {}
        self.no = 0  # nombre d'objets créés sur ce prototype
        self.nom_singulier = "un objet indéfini"
        self.etat_singulier = "est posé là"
        self.nom_pluriel = "objets indéfinis"
        self.etat_pluriel = "sont posés là"
        self.noms_sup = []
        self.description = Description(parent=self)
        self.objets = []
        self.unique = True  # par défaut tout objet est unique
        self.flags = 0
        self._prix = 1  # valeur en magasin
        self.sans_prix = False
        self.poids_unitaire = 1  # 1 Kg
        self.depecer_de = []

        # Equipement
        self.peut_prendre = True  # définit si on peut manipuler l'objet à main
        self.peut_tenir = False  # définit si on peut tenir un objet par-dessus
        self.emplacement = ""
        self.epaisseur = 1
        self.positions = ()

        # Script
        self.script = ScriptObjet(self)
        self.etendre_script()

        # Editeur
        self._extensions_editeur = []

        # Erreur de validation du type
        self.err_type = "Le type de '{}' est invalide."

        self._construire()
コード例 #28
0
ファイル: base.py プロジェクト: stormi/tsunami
    def __init__(self, cle=""):
        """Constructeur d'un type"""
        if cle:
            valider_cle(cle)

        BaseObj.__init__(self)
        self.cle = cle

        self._attributs = {}
        self.nom = "un élément inconnu"
        self.description = Description(parent=self)

        # Editeur
        self._extensions_editeur = []
コード例 #29
0
ファイル: quete.py プロジェクト: wkta/tsunami
 def __init__(self, cle, auteur, parent=None, niveau=(1, )):
     """Constructeur de la quête."""
     BaseObj.__init__(self)
     self.type = "quete"
     self.cle = cle
     self.niveau = niveau
     self.auteur = auteur
     self.parent = parent
     self.date_creation = datetime.now()
     self.titre = "une quelconque quête"
     self.description = Description(parent=self)
     self.ordonnee = True
     self.__etapes = []
     self._construire()
コード例 #30
0
 def __init__(self, nom, auteur, parent):
     """Constructeur du canal"""
     BaseObj.__init__(self)
     self.nom = nom
     self.auteur = auteur
     self.flags = AUCUN_FLAG
     self.clr = "|cyc|"
     self.resume = "canal de communication"
     self.description = Description(parent=parent)
     self.moderateurs = []
     self.immerges = []
     self.connectes = []
     self.liste_noire = []
     self.parent = parent
     self._construire()
コード例 #31
0
    def __init__(self, zone, mnemonic, x=0, y=0, z=0, valide=True):
        """Constructeur de la salle"""
        BaseObj.__init__(self)
        self._nom_zone = zone
        self._mnemonic = mnemonic
        self.coords = Coordonnees(x, y, z, valide, self)
        self.nom_terrain = "ville"
        self.titre = ""
        self.description = Description(parent=self)
        self.sorties = Sorties(parent=self)
        self.details = Details(parent=self)
        self._personnages = []
        self.objets_sol = ObjetsSol(parent=self)
        self.script = ScriptSalle(self)
        self.interieur = False
        self.illuminee = False
        self.magasin = None
        self.flags = 0
        self.mod_temperature = 0

        # Repop
        self.pnj_repop = {}

        # Etendue
        self.etendue = None

        # Affections
        self.affections = {}

        # Décors
        self.decors = []

        # Propriétaires de la salle (maison, cabine de bateau, etc)
        self._proprietaires = []

        self._construire()
コード例 #32
0
ファイル: joueur.py プロジェクト: wkta/tsunami
 def __init__(self):
     """Constructeur du joueur"""
     Personnage.__init__(self)
     self.nom_groupe = type(self).importeur.joueur.groupe_par_defaut
     self.compte = None
     self.instance_connexion = None
     self.connecte = False
     self.garder_connecte = False
     self.afk = ""
     self.retenus = {}
     self.distinction_visible = ""
     self.distinction_audible = ""
     self.no_tick = 1
     self.alias_francais = {}
     self.alias_anglais = {}
     self.tips = importeur.information.cfg_info.tips
     self.creation = datetime.now()
     self.derniere_connexion = None
     self.adresse_ip = "inconnue"
     self.cpt_mort = 0
     self.description = Description(parent=self, scriptable=False)
     self.description_a_valider = Description(parent=self, scriptable=False)
     self.description_modifiee = False
     self.pk = False
コード例 #33
0
 def __init__(self, guilde, parent, nom_francais, nom_anglais):
     """Constructeur d'une commande dynamique."""
     BaseObj.__init__(self)
     self.guilde = guilde
     self.parent = parent
     self.nom_francais = nom_francais
     self.nom_anglais = nom_anglais
     self.commande = None  # commande statique liée
     self._utilisable = False
     self.doit_etre_membre = True
     self._groupe = "pnj"
     self._nom_categorie = "divers"
     self._aide_courte = "à renseigner..."
     self.aide_longue = Description(parent=self,
                                    scriptable=False,
                                    callback="maj")
     self._schema = ""
     self.etats = {}
     self.script = ScriptCommande(self)
     self._construire()
コード例 #34
0
ファイル: sort.py プロジェクト: wkta/tsunami
    def __init__(self, cle, parent=None):
        """Constructeur du sort"""
        BaseObj.__init__(self)
        self.parent = parent
        self.cle = cle
        self.nom = "sortilège"
        self.description = Description(parent=self)
        self.offensif = False
        self.elements = []
        self.stats = []
        self.type = "destruction"
        self._type_cible = "aucune"
        self.cout = 10
        self.duree = 3
        self.difficulte = 0
        self.distance = False
        self.points_tribut = 1
        self.script = ScriptSort(self)

        # On passe le statut en CONSTRUIT
        self._statut = CONSTRUIT
コード例 #35
0
ファイル: base.py プロジェクト: vlegoff/tsunami
    def __init__(self, cle=""):
        """Constructeur d'un type"""
        BaseObj.__init__(self)
        self.cle = cle
        self._attributs = {}
        self.no = 0 # nombre d'objets créés sur ce prototype
        self.nom_singulier = "un objet indéfini"
        self.etat_singulier = "est posé là"
        self.nom_pluriel = "objets indéfinis"
        self.etat_pluriel = "sont posés là"
        self.noms_sup = []
        self.description = Description(parent=self)
        self.objets = []
        self.unique = True # par défaut tout objet est unique
        self.flags = 0
        self._prix = 1 # valeur en magasin
        self.sans_prix = False
        self.poids_unitaire = 1 # 1 Kg
        self.depecer_de = []

        # Equipement
        self.peut_prendre = True # définit si on peut manipuler l'objet à main
        self.peut_tenir = False # définit si on peut tenir un objet par-dessus
        self.emplacement = ""
        self.epaisseur = 1
        self.positions = ()

        # Script
        self.script = ScriptObjet(self)
        self.etendre_script()

        # Editeur
        self._extensions_editeur = []

        # Erreur de validation du type
        self.err_type = "Le type de '{}' est invalide."

        self._construire()
コード例 #36
0
ファイル: base.py プロジェクト: stormi/tsunami
class BaseElement(BaseObj, metaclass=MetaElt):

    """Classe abstraite représentant le type de base d'un élément.

    Si des données doivent être communes à tous les types d'éléments
    c'est dans cette classe qu'elles apparaissent.

    """

    enregistrer = True
    nom_type = "" # à redéfinir
    def __init__(self, cle=""):
        """Constructeur d'un type"""
        if cle:
            valider_cle(cle)

        BaseObj.__init__(self)
        self.cle = cle

        self._attributs = {}
        self.nom = "un élément inconnu"
        self.description = Description(parent=self)

        # Editeur
        self._extensions_editeur = []

    def __getnewargs__(self):
        return ()

    def __str__(self):
        return self.cle

    def __getstate__(self):
        """Retourne le dictionnaire à enregistrer."""
        attrs = self.__dict__.copy()
        if "_extensions_editeur" in attrs:
            del attrs["_extensions_editeur"]
        if "_attributs" in attrs:
            del attrs["_attributs"]
        return attrs

    def etendre_editeur(self, raccourci, ligne, editeur, objet, attribut, *sup):
        """Permet d'étendre l'éditeur d'éléments en fonction du type.

        Paramètres à entrer :
        -   raccourci   le raccourci permettant d'accéder à la ligne
        -   ligne       la ligne de l'éditeur (exemple 'Description')
        -   editeur     le contexte-éditeur (exemple Uniligne)
        -   objet       l'objet à éditer
        -   attribut    l'attribut à éditer

        Cette méthode est appelée lors de la création de l'éditeur
        d'éléments.

        """
        self._extensions_editeur.append(
            (raccourci, ligne, editeur, objet, attribut, sup))

    def travailler_enveloppes(self, enveloppes):
        """Travail sur les enveloppes.

        On récupère un dictionnaire représentant la présentation avec en
        clé les raccourcis et en valeur les enveloppes.

        Cela peut permettre de travailler sur les enveloppes ajoutées par
        'etendre_editeur'.

        """
        pass

    def get_description_ligne(self, personnage):
        """Retourne une description d'une ligne de l'élément."""
        return self.nom.capitalize() + " est là"

    def construire(self, parent):
        """Construit l'élément basé sur le parent."""
        pass

    def get_nom_pour(self, personnage):
        """Retourne le nom de l'élément."""
        return self.nom

    def regarder(self, personnage):
        """personnage regarde self."""
        msg = "Vous regardez {} :".format(self.nom) + "\n\n"
        msg += self.description.regarder(personnage, self)
        return msg
コード例 #37
0
class BaseElement(BaseObj, metaclass=MetaElt):
    """Classe abstraite représentant le type de base d'un élément.

    Si des données doivent être communes à tous les types d'éléments
    c'est dans cette classe qu'elles apparaissent.

    """

    enregistrer = True
    nom_type = ""  # à redéfinir

    def __init__(self, cle=""):
        """Constructeur d'un type"""
        if cle:
            valider_cle(cle)

        BaseObj.__init__(self)
        self.cle = cle

        self._attributs = {}
        self.nom = "un élément inconnu"
        self.description = Description(parent=self)

        # Editeur
        self._extensions_editeur = []

    def __getnewargs__(self):
        return ()

    def __str__(self):
        return self.cle

    def __getstate__(self):
        """Retourne le dictionnaire à enregistrer."""
        attrs = self.__dict__.copy()
        if "_extensions_editeur" in attrs:
            del attrs["_extensions_editeur"]
        if "_attributs" in attrs:
            del attrs["_attributs"]
        return attrs

    def editer(self, presentation):
        """Méthode à rédéfinir, appelée quand l'éditeur s'affiche.

        Cette méthode est à redéfinir quand on souhaite ajouter des
        options spécifiques à l'éditeur de l'élément en question.

        """
        pass

    def get_description_ligne(self, personnage):
        """Retourne une description d'une ligne de l'élément."""
        return self.nom.capitalize() + " est là"

    def construire(self, parent):
        """Construit l'élément basé sur le parent."""
        pass

    def get_nom_pour(self, personnage):
        """Retourne le nom de l'élément."""
        return self.nom

    def regarder(self, personnage):
        """personnage regarde self."""
        msg = "Vous regardez {} :".format(self.nom) + "\n\n"
        msg += self.description.regarder(personnage, self)
        return msg
コード例 #38
0
ファイル: detail.py プロジェクト: vlegoff/tsunami
class Detail(BaseObj):

    """Cette classe représente un détail observable dans une salle.

    Elle permet d'ajouter à la description d'une salle des détails invisibles
    au premier abord, mais discernable avec la commande look.

    """

    nom_scripting = "le détail"
    def __init__(self, nom, parent=None, modele=None):
        """Constructeur de la classe"""
        BaseObj.__init__(self)
        self.nom = nom
        self.synonymes = []
        self.titre = "un détail aux alentours"
        self.description = Description(parent=self)
        self.positions = {}
        self.est_visible = True
        self.script = ScriptDetail(self)
        self.peut_asseoir = False
        self.peut_allonger = False
        self.facteur_asseoir = 1.1
        self.facteur_allonger = 1.2
        self.connecteur = "sur"
        self.nb_places_assises = 1
        self.nb_places_allongees = 1
        self.parent = parent
        self.flags = 0
        self.supporte = None
        self._peut_supporter = 0.0
        self.message_supporte = "Dessus se trouve"
        self.message_installation = "Vous posez %objet sur %detail."
        self.message_desinstallation = "Vous retirez %objet% de %detail."
        if modele is not None:
            self.synonymes = modele.synonymes
            self.titre = modele.titre
            self.description = modele.description
        # On passe le statut en CONSTRUIT
        self._construire()

    def __getnewargs__(self):
        return ("", "")

    def __str__(self):
        return self.titre.lower()

    @property
    def repos(self):
        return oui_ou_non(self.peut_asseoir or self.peut_allonger)

    @property
    def support(self):
        """Retourne oui_ou_non."""
        return oui_ou_non(self._peut_supporter > 0)

    def _get_peut_supporter(self):
        return self._peut_supporter
    def _set_peut_supporter(self, poids):
        self._peut_supporter = poids
        if poids:
            if self.supporte is None:
                self.supporte = ConteneurObjet(self)
        else:
            if self.supporte:
                self.supporte.detruire()
                self.supporte = None
    peut_supporter = property(_get_peut_supporter, _set_peut_supporter)

    def get_nom_pour(self, personnage):
        """Retourne le nom pour le personnage précisé."""
        return self.titre

    def a_flag(self, nom_flag):
        """Retourne True si le détail a le flag, False sinon."""
        valeur = FLAGS[nom_flag]
        return self.flags & valeur != 0

    def regarder(self, personnage):
        """Le personnage regarde le détail"""
        if not self.est_visible:
            personnage << "Il n'y a rien qui ressemble à cela par ici..."
            return
        personnage << "Vous examinez {} :".format(self.titre)
        self.script["regarde"]["avant"].executer(personnage=personnage,
                salle=personnage.salle)
        description = self.description.regarder(personnage, self)
        if not description:
            description = "Il n'y a rien de bien intéressant à voir."

        personnage << description

        self.script["regarde"]["apres"].executer(personnage=personnage,
                salle=personnage.salle)
コード例 #39
0
class Salle(BaseObj):
    """Classe représentant une salle de l'univers.

    Une salle est un élément détaillant la géographie locale d'une petite
    portion de l'univers. Bien que cela dépende des MUDs, les salles décrivent
    généralement un espace d'environ 5 mètres sur 5.

    Ces salles comportent une description détaillant les alentours proches.
    Cette description est envoyée à chaque fois qu'un personnage se déplace
    dans l'univers, pour lui donner une idée de son nouvel environnement.

    Note sur le positionnement des salles :
        Les salles peuvent être caractérisées par des coordonnées. Ces
        coordonnées sont en soi facultatives. Il est possible de créer un
        univers sans aucune coordonnée. Il s'agit d'une facilité
        lors de la constitution de votre univers qui permet à certains
        modules, comme 'vehicule', de fonctionner. Si votre salle n'a pas de
        coordonnées, vous devrez créer chaque sortie "à la main".
        Les salles ne sont donc pas identifiées par leurs coordonnées, sauf
        dans certains cas, mais bien par leur zone et mnémonique. Ce couple
        caractérise de façon unique une salle dans l'univers.
        Exemple : une salle ayant pour zone 'picte' et pour mnémonique '1'
        sera accessible depuis la clé 'picte:1' ; aucune autre salle de
        l'univers ne pourra posséder cette clé 'picte:1'.

    """

    nom_scripting = "la salle"
    _nom = "salle"
    _version = 4
    enregistrer = True

    def __init__(self, zone, mnemonic, x=0, y=0, z=0, valide=True):
        """Constructeur de la salle"""
        BaseObj.__init__(self)
        self._nom_zone = zone
        self._mnemonic = mnemonic
        self.coords = Coordonnees(x, y, z, valide, self)
        self.nom_terrain = "ville"
        self.titre = ""
        self.description = Description(parent=self)
        self.sorties = Sorties(parent=self)
        self.details = Details(parent=self)
        self._personnages = []
        self.objets_sol = ObjetsSol(parent=self)
        self.script = ScriptSalle(self)
        self.interieur = False
        self.illuminee = False
        self.magasin = None
        self.flags = 0
        self.mod_temperature = 0

        # Repop
        self.pnj_repop = {}

        # Etendue
        self.etendue = None

        # Affections
        self.affections = {}

        # Décors
        self.decors = []

        # Propriétaires de la salle (maison, cabine de bateau, etc)
        self._proprietaires = []

        self._construire()

    def __getnewargs__(self):
        return ("", "")

    def __repr__(self):
        """Affichage de la salle en mode debug"""
        return self._nom_zone + ":" + self._mnemonic

    def __str__(self):
        """Retourne l'identifiant 'zone:mnemonic'"""
        return self._nom_zone + ":" + self._mnemonic

    def _get_nom_zone(self):
        return self._nom_zone

    def _set_nom_zone(self, zone):
        prochain_ident = zone.lower() + ":" + self._mnemonic
        autre = importeur.salle.salles.get(prochain_ident)
        if autre is not None and autre is not self:
            raise ValueError("L'identifiant {} est déjà utilisé".format(
                repr(prochain_ident)))

        ident = self.ident
        self._nom_zone = zone.lower()
        type(self).importeur.salle.changer_ident(ident, self.ident)

    def _get_mnemonic(self):
        return self._mnemonic

    def _set_mnemonic(self, mnemonic):
        prochain_ident = self._nom_zone + ":" + mnemonic.lower()
        autre = importeur.salle.salles.get(prochain_ident)
        if autre is not None and autre is not self:
            raise ValueError("L'identifiant {} est déjà utilisé".format(
                repr(prochain_ident)))

        ident = self.ident
        self._mnemonic = mnemonic.lower()
        type(self).importeur.salle.changer_ident(ident, self.ident)

    nom_zone = property(_get_nom_zone, _set_nom_zone)
    mnemonic = property(_get_mnemonic, _set_mnemonic)

    @property
    def zone(self):
        """Retourne la zone  correspondante."""
        return type(self).importeur.salle.get_zone(self._nom_zone)

    @property
    def ident(self):
        """Retourne l'identifiant, c'est-à-dire une chaîne 'zone:mnemonic'"""
        return "{}:{}".format(self._nom_zone, self._mnemonic)

    @property
    def personnages(self):
        """Retourne une liste déférencée des personnages"""
        return list(self._personnages)

    @property
    def PNJ(self):
        """Retourne une liste déférencée des PNJ présents."""
        return [p for p in self._personnages if hasattr(p, "prototype")]

    @property
    def joueurs(self):
        """Retourne une liste déférencée des joueurs présents."""
        return [p for p in self._personnages if not hasattr(p, "prototype")]

    @property
    def exterieur(self):
        """Retourne True si la salle est extérieure, False sinon."""
        return not self.interieur

    @property
    def terrain(self):
        """Retourne l'objet terrain."""
        return importeur.salle.terrains[self.nom_terrain]

    @property
    def desc_survol(self):
        return self.terrain.desc_survol

    @property
    def str_coords(self):
        x, y, z = self.coords.tuple()
        if self.coords.valide:
            return "{}.{}.{}".format(x, y, z)

        return "Aucune"

    def get_etendue(self):
        return self.etendue

    @property
    def nom_unique(self):
        return self.ident

    @property
    def objets_uniques(self):
        """Retourne les objets uniques posés dans la salle."""
        objets = []
        for objet in self.objets_sol._objets:
            objets.append(objet)
            objets.extend(objet.prototype.objets_contenus(objet))

        return objets

    @property
    def a_magasin(self):
        """Y a-t-il un magasin dans cette salle ?"""
        return self.magasin is not None

    @property
    def nb_sorties(self):
        """Retourne le mombre de sorties."""
        sorties = [s for s in self.sorties if s and s.salle_dest]
        return len(sorties)

    @property
    def details_etendus(self):
        """Retourne la liste des détails étendus.

        Cette méthode retourne les détails de la salle et ceux des
        flottantes contenus dans la description.

        """
        details = self.details._details.copy()
        description = self.description
        for paragraphe in description.paragraphes:
            p, flottantes = description.charger_descriptions_flottantes(
                paragraphe)
            for flottante in flottantes:
                for d in flottante.details:
                    if d.nom not in details:
                        details[d.nom] = d

        return tuple(details.values())

    @property
    def proprietaires(self):
        """Retourne les propriétaires."""
        return self._proprietaires

    @property
    def temperature(self):
        """Retourne la température de la salle."""
        zone = self.zone
        if self.interieur:
            temperature = 18
        else:
            temperature = zone.temperature

        feu = importeur.salle.feux.get(self.ident)
        if feu:
            if feu.puissance < 8:
                temperature += feu.puissance
            elif feu.puissance < 15:
                temperature = 20
            elif feu.puissance < 40:
                temperature = 25
            elif feu.puissance < 55:
                temperature = 30
            else:
                temperature = 50

        # Modification singulière de la salle
        temperature += self.mod_temperature

        # Bonus temporaires
        temperature += importeur.bonus.get(self, "temperature")

        return temperature

    def changer_terrain(self, nouveau_terrain):
        """Change le terrain de la salle."""
        nouveau_terrain = supprimer_accents(nouveau_terrain).lower()
        for terrain in importeur.salle.terrains.keys():
            sa_terrain = supprimer_accents(terrain).lower()
            if sa_terrain == nouveau_terrain:
                self.nom_terrain = terrain
                return

        raise ValueError("terrain {} inconnu".format(repr(nouveau_terrain)))

    def voit_ici(self, personnage):
        """Retourne True si le personnage peut voir ici, False sinon.

        Un personnage peut voir dans la salle si :
            Il est immortel
            La salle est illuminée
            Le personnage est un PNJ nyctalope
            Le personnage est un PNJ contrôlé par un immortel
            Il y a un feu dans la salle
            La race du personnage est nyctalope
            Le personnage a une affection qui lui permet de voir dans le noir
            La salle est en extérieure et :
                Il fait jour ou
                Le ciel est dégagé
            Une lumière est posée sur le sol
            Le personnage a une lumière

        """
        if personnage.est_immortel():
            return True

        if self.illuminee:
            return True

        if hasattr(personnage, "prototype") and \
                (personnage.prototype.a_flag("nyctalope") or \
                personnage.controle_par):
            return True

        if self.ident in importeur.salle.feux:
            return True

        # Vérification de la race
        if personnage.race and personnage.race.a_flag("nyctalope"):
            return True

        # Vérification de l'affection
        for affection in personnage.affections.values():
            if affection.affection.a_flag("voit dans le noir"):
                return True

        if not self.interieur:
            if importeur.temps.temps.il_fait_jour:
                return True

            perturbation = importeur.meteo.salles.get(self)
            if perturbation is None or not perturbation.est_opaque():
                return True

        # Vérification des lumières au sol
        for objet in self.objets_sol:
            if objet.est_de_type("lumière") and objet.allumee_depuis:
                return True

        # Vérification des lumières équipées
        for objet in personnage.equipement.equipes:
            if objet.est_de_type("lumière") and objet.allumee_depuis:
                return True

        return False

    def a_detail_flag(self, flag):
        """Retourne True si la salle a un détail du flag indiqué."""
        for detail in self.details:
            if detail.a_flag(flag):
                return True

        description = self.description
        for paragraphe in description.paragraphes:
            p, flottantes = description.charger_descriptions_flottantes(
                paragraphe)
            for flottante in flottantes:
                for d in flottante.details:
                    if d.a_flag(flag):
                        return True

        return False

    def personnage_est_present(self, personnage):
        """Si le personnage est présent, retourne True, False sinon."""
        return personnage in self._personnages

    def a_flag(self, nom_flag):
        """Retourne True si la salle a le flag, False sinon."""
        valeur = FLAGS[nom_flag]
        return self.flags & valeur != 0

    def ajouter_personnage(self, personnage):
        """Ajoute le personnage dans la salle"""
        if personnage not in self._personnages:
            self._personnages.append(personnage)

    def retirer_personnage(self, personnage):
        """Retire le personnage des personnages présents"""
        if personnage in self.personnages:
            self._personnages.remove(personnage)

    def salles_autour(self, rayon=5):
        """Retourne les chemins autour de self dans le rayon précisé.

        Si la salle a des coordonnées valide, ajoute également
        les salles semblant proches. Cependant, pour celles-ci,
        presque aucune vérification de chemin n'est faite,
        c'est-à-dire qu'elles peuvent être entièrement inaccessible.

        """
        if self.accepte_discontinu():
            empruntable = False
        else:
            empruntable = True

        return Chemins.salles_autour(self, rayon, empruntable=empruntable)

    def trouver_chemin_absolu(self, destination, rayon=5, explicite=False):
        """Retourne, si trouvé, le chemin menant à destination ou None.

        Le rayon passé en argument est celui de recherche. Plus il est
        élevé, plus le temps de calcul risque d'être important.

        """
        chemins = Chemins.salles_autour(self, rayon, absolu=True)
        chemin = chemins.get(destination)
        if chemin is None and explicite:
            raise ValueError("le chemin absolu est introuvable")

        return chemin

    def trouver_chemin(self, destination, explicite=False):
        """Recherche et retourne le chemin entre deux salles.

        Plusieurs algorithmes de recherche sont utilisés en fonction
        des situations. Pour des raisons de performance, certains sont
        plus efficaces que d'autres.

        Avant tout, plusieurs chemins peuvent être retournés :
        *   Un chemin continue (toutes les salles entre deux points)
        *   Un chemin discontinu (ou brisé).

        La grande différence est qu'un personnage peut emprunter
        un chemin continu pour aller d'une salle à une autre, mais
        ne peut pas emprunter un chemin discontinu. Les chemins
        discontinus ne sont pas tolérés par défaut (ceci est réglable).

        La recherche du chemin (continu ou discontinu) fait ensuite
        appel à deux algorithmes :
        *   Si les coordonnées des deux salles sont valides, cherche le
            chemin relatifs (Chemin.salles_entre)
        *   Sinon, cherche le chemin absolu (toutes les salles
            autour de la première salle dans un rayon d'estime).

        Si explicite est mis à True, lève une exception ValueError
        décrivant pourquoi la recherche a échouée.

        """
        # Si l'une des salles n'a pas de coordonnées valide
        if self.coords.invalide or destination.coords.invalide:
            return self.trouver_chemin_absolu(destination,
                                              4,
                                              explicite=explicite)

        # Les deux salles ont des coordonnées valides
        # On vérifie que le rayon n'es tpas trop important
        v_origine = Vecteur(*self.coords.tuple())
        v_destination = Vecteur(*destination.coords.tuple())
        distance = (v_destination - v_origine).norme
        if distance > 25:
            if explicite:
                raise ValueError("la distance entre les deux salles " \
                        "est supérieure à 25")

            return None

        salles = Chemins.get_salles_entre(self, destination)

        # On détermine, si possible, le chemin entre chaque salle
        chemin = Chemin()
        a_salle = None
        continu = True
        for d_salle in salles:
            if a_salle is None:
                a_salle = d_salle
                continue

            d_chemin = a_salle.trouver_chemin_absolu(d_salle, 2)
            if d_chemin is None:
                continu = False
                vecteur = Vecteur(*d_salle.coords.tuple()) - \
                        Vecteur(*a_salle.coords.tuple())
                sortie = a_salle.get_sortie(vecteur, d_salle)
                chemin.sorties.append(sortie)
            else:
                chemin.sorties.extend(d_chemin.sorties)

            a_salle = d_salle

        if chemin.origine is not self or chemin.destination is not destination:
            if explicite:
                raise ValueError("le chemin retourné ne commence ou " \
                        "ne finit pas au bon endroit")

            return None

        if not continu and (not self.accepte_discontinu() or not \
                destination.accepte_discontinu()):
            if explicite:
                raise ValueError("un chemin non continu a été retourné")

            return None

        chemin.raccourcir()
        return chemin

    def accepte_discontinu(self):
        """Retourne True si cette salle supporte les chemins discontinu.

        Tsunami supporte ce type de chemin si la salle est une côte
        de l'étendue.

        """
        return self.etendue is not None

    def trouver_chemins_droits(self,
                               rayon,
                               dir_sortie=None,
                               chemins=None,
                               chemin=None,
                               hook=True):
        """Cherche les chemins droits d'une salle.

        La recherche de salle peut être étendue par un hook.
        Les chemins droits veulent dire que l'on vérifie les sorties
        initiales. Par exemple, si la salle A a la sortie est menant vers
        B, on demande à la salle B ses sorties... mais on ne sélectionnera
        que les sorties de même direcition que la salle A (c'est-à-dire est
        ici).

        """
        chemins = chemins or Chemins()
        chemin = chemin or Chemin()

        # On parcourt les sorties de la salle
        for sortie in self.sorties:
            if dir_sortie is None or sortie.direction == dir_sortie:
                n_chemin = Chemin()
                n_chemin.sorties.extend(chemin.sorties)
                n_chemin.sorties.append(sortie)
                origine = n_chemin.origine
                destination = n_chemin.destination
                ancien_chemin = chemins.get(destination)
                if origine is not destination and (ancien_chemin is None or \
                        len(ancien_chemin) > len(n_chemin)):
                    # On retire l'ancien chemin si besoin
                    if ancien_chemin:
                        chemins.chemins.remove(ancien_chemin)
                    chemins.chemins.append(n_chemin)
                    if rayon > 1:
                        destination.trouver_chemins_droits(
                            rayon - 1, sortie.direction, chemins, n_chemin,
                            False)

        if hook:
            importeur.hook["salle:trouver_chemins_droits"].executer(
                self, chemins, rayon)
        return chemins

    def get_sortie(self, vecteur, destination):
        """Retourne une sortie en fonction du vecteur donné."""
        sortie = Sortie(vecteur.nom_direction, vecteur.nom_direction, "le",
                        destination, "", self)
        sortie.longueur = vecteur.norme
        return sortie

    def sortie_empruntable(self, sortie):
        """Retourne True si la sortie est empruntable, False sinon.

        Pour une salle standard, une sortie est empruntable si elle existe
        réellement.

        """
        return sortie.direction in self.sorties

    def envoyer(self,
                message,
                *personnages,
                prompt=True,
                mort=False,
                ignore=True,
                lisser=False,
                **kw_personnages):
        """Envoie le message aux personnages présents dans la salle.

        Les personnages dans les paramètres supplémentaires (nommés ou non)
        sont utilisés pour formatter le message et font figure d'exceptions.
        Ils ne recevront pas le message.

        """
        exceptions = personnages + tuple(kw_personnages.values()) if ignore \
                else ()
        for personnage in self.personnages:
            if personnage not in exceptions:
                if personnage.est_mort() and not mort:
                    continue

                if hasattr(personnage, "instance_connexion") and \
                        personnage.instance_connexion and not prompt:
                    personnage.instance_connexion.sans_prompt()
                personnage.envoyer(message,
                                   *personnages,
                                   lisser=lisser,
                                   **kw_personnages)

    def envoyer_lisser(self, chaine, *personnages, **kw_personnages):
        """Méthode redirigeant vers envoyer mais lissant la chaîne."""
        self.envoyer(chaine, *personnages, lisser=True, **kw_personnages)

    def get_elements_observables(self, personnage):
        """Retourne une liste des éléments observables dans cette salle."""
        liste = []
        for methode in importeur.salle.details_dynamiques:
            liste.extend(methode(self, personnage))

        return liste

    def regarder(self, personnage):
        """Le personnage regarde la salle"""
        if personnage.est_mort():
            personnage << "|err|Vous êtes inconscient et ne voyez pas " \
                    "grand chose...|ff|"
            return

        if not self.voit_ici(personnage):
            personnage << "Il y fait trop sombre pour vos sens, " \
                    "l'obscurité vous environne."
            return

        res = ""
        if personnage.est_immortel():
            res += "# |rgc|" + self.nom_zone + "|ff|:|vrc|" + self.mnemonic
            res += "|ff| ({})".format(self.coords)
            res += "\n\n"
        res += "   |tit|" + (self.titre or "Une salle sans titre") + "|ff|\n"
        description = self.description.regarder(personnage, self)
        if not description:
            description = "   Vous êtes au milieu de nulle part."
        res += description + "\n"

        res_decors = []
        for nom in self.regrouper_decors():
            res_decors.append(nom.capitalize() + ".")

        if res_decors:
            res += "\n".join(res_decors) + "\n"

        plus = self.decrire_plus(personnage)
        if plus:
            res += plus + "\n"

        res_affections = []
        for affection in self.affections.values():
            message = affection.affection.message(affection)
            if message:
                res_affections.append(message)

        if res_affections:
            res += "\n".join(res_affections) + "\n"

        liste_messages = []
        flags = 0
        type(self).importeur.hook["salle:regarder"].executer(
            self, liste_messages, flags)
        if liste_messages:
            res += "\n".join(liste_messages) + "\n"

        res += "\nSorties : "
        res += self.afficher_sorties(personnage)

        # Personnages
        personnages = OrderedDict()
        # Si le personnage est un joueur, il se retrouve avec un nombre de 1
        # Si le personnage est un PNJ, on conserve son prototype avec
        # le nombre d'occurences de prototypes apparaissant
        etats = {}
        for personne in self.personnages:
            if personne is not personnage:
                if not hasattr(personne, "prototype"):
                    if personnage.peut_voir(personne):
                        personnages[personne] = 1
                else:
                    doit = importeur.hook["pnj:doit_afficher"].executer(
                        personne)
                    if any(not flag for flag in doit):
                        continue

                    nom = personne.get_nom_etat(personnage)
                    if nom in etats:
                        prototype = etats[nom]
                        personnages[prototype] = \
                                personnages[prototype] + 1
                    else:
                        etats[nom] = personne
                        personnages[personne] = 1

        if len(personnages):
            res += "\n"

            for personne, nombre in personnages.items():
                res += "\n- {}".format(
                    personne.get_nom_etat(personnage, nombre))

        # Objets
        noms_objets = self.afficher_noms_objets()
        if len(noms_objets):
            res += "\n"
            for nom_objet in noms_objets:
                res += "\n+ {}".format(nom_objet)

        return res

    def afficher_sorties(self, personnage):
        """Affiche les sorties de la salle"""
        noms = []
        for nom in NOMS_SORTIES.keys():
            sortie = self.sorties[nom]
            if sortie:
                nom = sortie.nom

            nom_aff = self.sorties.get_nom_abrege(nom)
            if self.sorties.sortie_existe(nom):
                if sortie.porte and sortie.porte.fermee:
                    res = "[|rgc|" + nom_aff + "|ff|]"
                else:
                    res = "|vr|" + nom_aff + "|ff|"
                if sortie.cachee:
                    if personnage.est_immortel():
                        res = "|rg|(I)|ff|" + res
                    else:
                        res = " ".ljust(
                            len(self.sorties.get_nom_abrege(sortie.direction)))
            else:
                res = " ".ljust(len(nom_aff))

            noms.append(res)

        return ", ".join(noms) + "."

    def afficher_noms_objets(self):
        """Retourne les noms et états des objets sur le sol de la salle"""
        objets = []
        for o, nb in self.objets_sol.get_objets_par_nom():
            objets.append(o.get_nom_etat(nb))

        return objets

    def decrire_plus(self, personnage):
        """Ajoute un message au-dessous de la description.

        Si cette méthode retourne une chaîne non vide, alors cette chaîne
        sera ajoutée sous la description quand un personnage regardera
        la salle.

        """
        pass

    def pop_pnj(self, pnj):
        """Méthode appelée quand un PNJ pop dans la salle."""
        pro = pnj.prototype
        if pro in self.pnj_repop:
            self.pnj_repop[pro] = self.pnj_repop[pro] - 1

    def det_pnj(self, pnj):
        """Méthode appelée quand un PNJ disparaît.."""
        pro = pnj.prototype
        if pro in self.pnj_repop:
            self.pnj_repop[pro] = self.pnj_repop[pro] + 1

    def repop(self):
        """Méthode appelée à chaque repop."""
        for pro, nb in self.pnj_repop.items():
            if nb > 0:
                for i in range(nb):
                    pnj = importeur.pnj.creer_PNJ(pro, self)
                    pnj.script["repop"].executer(pnj=pnj)

    def affecte(self, cle, duree, force):
        """Affecte la salle avec une affection.

        Si l'affection est déjà présente, la force est modulée.

        """
        affection = importeur.affection.get_affection("salle", cle)
        if cle in self.affections:
            concrete = self.affections[cle]
            affection.moduler(concrete, duree, force)
        else:
            concrete = Affection(affection, self, duree, force)
            self.affections[cle] = concrete
            concrete.affection.executer_script("cree", concrete)
            concrete.prevoir_tick()

    def peut_affecter(self, cle_affection):
        """La salle self peut-elle être affectée par l'affection ?"""
        if cle_affection == "neige":
            if self.interieur:
                return False
            elif self.nom_terrain in ["aquatique", "subaquatique"]:
                return False

            sortie = self.sorties["bas"]
            if sortie:
                return False

        return True

    def tick(self):
        """Méthode appelée à chaque tick de la salle."""
        for affection in self.affections.values():
            affection.affection.dec_duree(affection)

        for cle, affection in list(self.affections.items()):
            if not affection.e_existe or affection.duree <= 0:
                del self.affections[cle]

        self.script["tick"].executer(salle=self)

    def regrouper_decors(self):
        """Regroupe les décors par nom."""
        decors = OrderedDict()
        nombres = OrderedDict()
        res = OrderedDict()
        for decor in self.decors:
            nom = decor.get_nom()
            decors[nom] = decor
            nb = nombres.get(nom, 0)
            nb += 1
            nombres[nom] = nb

        for nom, nb in nombres.items():
            decor = decors[nom]
            nom = decor.get_nom_etat(nb)
            res[nom] = decor

        return res

    def ajouter_decor(self, prototype):
        """Ajoute un décor dans la salle."""
        if isinstance(prototype, PrototypeBonhommeNeige):
            decor = BonhommeNeige(prototype, self)
        else:
            decor = Decor(prototype, self)

        self.decors.append(decor)
        return decor

    def get_decors(self, cle):
        """Retourne tous les décors ayant la clé indiquée."""
        return [d for d in self.decors if d.prototype and \
                d.prototype.cle == cle]

    def supprimer_decor(self, decor):
        """Supprime les décors indiqués."""
        self.decors.remove(decor)
        decor.detruire()

    def supprimer_decors(self, cle):
        """Supprime les décors de clé indiquée."""
        for decor in self.get_decors(cle):
            self.supprimer_decor(decor)

    def supprimer_bonhommes_neige(self):
        """Supprime les bonhommes de neige présents."""
        for decor in list(self.decors):
            if isinstance(decor, BonhommeNeige):
                self.supprimer_decor(decor)
                self.envoyer("{} se met à fondre rapidement.".format(
                    decor.get_nom().capitalize()))

    def get_structure(self):
        """Retourne la structure de la salle."""
        structure = StructureSimple()
        structure.zone = self._nom_zone
        structure.mnemonique = self._mnemonic
        structure.terrain = self.nom_terrain
        structure.titre = self.titre
        structure.interieur = Fraction(self.interieur)
        structure.illuminee = Fraction(self.illuminee)
        structure.proprietaires = list(self.proprietaires)

        # Coordonnées
        structure.coordonnees = Fraction(self.coords.valide)
        structure.x = Fraction(self.coords.x)
        structure.y = Fraction(self.coords.y)
        structure.z = Fraction(self.coords.z)
        structure.temperature = Fraction(self.temperature)
        structure.mod_temperature = Fraction(self.mod_temperature)
        structure.bonus_temperature = Fraction(
            importeur.bonus.get(self, "temperature", precision=1))
        # Flags
        flags = []
        for nom, valeur in FLAGS.items():
            if self.flags & valeur != 0:
                flags.append(nom)

        structure.flags = flags
        return structure

    def appliquer_structure(self, structure):
        """Applique la structure passée en paramètre."""
        for cle, valeur in structure.donnees.items():
            if cle == "zone":
                self.nom_zone = str(valeur)
            elif cle == "mnemonique":
                self.mnemonic = str(valeur)
            elif cle == "terrain":
                self.changer_terrain(str(valeur))
            elif cle == "titre":
                self.titre = str(valeur)
            elif cle == "interieur":
                self.interieur = bool(valeur)
            elif cle == "illuminee":
                self.illuminee = bool(valeur)
            elif cle == "proprietaires":
                proprietaires = [p for p in valeur if p]
                self.proprietaires[:] = proprietaires
            elif cle == "coordonnees":
                self.coords.valide = bool(valeur)
            elif cle == "x":
                self.coords.x = int(valeur)
            elif cle == "y":
                self.coords.y = int(valeur)
            elif cle == "z":
                self.coords.z = int(valeur)
            elif cle == "mod_temperature":
                self.mod_temperature = int(valeur)
            elif cle == "flags":
                self.flags = 0
                for nom, val in FLAGS.items():
                    if nom in valeur:
                        self.flags += val
コード例 #40
0
ファイル: repere.py プロジェクト: vlegoff/tsunami
class Repere(BaseObj):

    """Classe représentant un repère dans une étendue d'eau.

    Les repères sont des détails généralement visibles à distance, des
    phares, de très hautes montagnes. Ils sont caractérisés par une
    position en 2D (x;y), un amplificateur de portée (qui détermine à quelle
    distance ils sont visibles) et plusieurs informations concernant
    comment ils sont affichés.

    """

    enregistrer = True
    def __init__(self, x, y):
        """Constructeur du repère."""
        BaseObj.__init__(self)
        self.x = x
        self.y = y
        self.nom = "un repère indéfini"
        self.description = Description(parent=self)
        self.amplificateur_portee = 1.5

    def __getnewargs__(self):
        return (0, 0)

    def __repr__(self):
        return "<Repère {}>".format(repr(self.nom))

    def __str__(self):
        return self.nom

    @property
    def desc_survol(self):
        return self.nom

    def get_nom_pour(self, personnage):
        return self.nom

    def regarder(self, personnage):
        """Le personnage regarde le repère."""
        salle = personnage.salle
        portee = get_portee(salle)
        visible = Visible.observer(personnage, portee, 5)
        points = [couple[1][1] for couple in visible.points.items()]
        if self not in points:
            personnage << "|err|Vous ne pouvez voir cela d'ici.|ff|"
            return

        salle.envoyer("{{}} regarde {}.".format(self.nom.lower()),
                personnage)
        msg = "Vous regardez " + self.nom + " :\n\n"
        msg += self.description.regarder(personnage, self)
        personnage << msg

    @staticmethod
    def trouver_reperes(visible, personnage, portee, precision,
            exceptions=None):
        """Cherche les repères visibles dans l'étendue."""
        navire = personnage.salle.navire
        etendue = navire.etendue
        altitude = etendue.altitude
        nav_direction = navire.direction.direction
        pos_coords = personnage.salle.coords.tuple()
        x, y, z = pos_coords
        position = Vector(x, y, altitude)

        for t_coords, repere in importeur.navigation.reperes.items():
            t_x, t_y = t_coords
            # On cherche l'angle entre la position du navire et du point
            v_point = Vector(t_x, t_y, altitude)
            v_dist = v_point - position
            if v_dist.mag > portee * repere.amplificateur_portee:
                continue

            direction = get_direction(v_dist)
            r_direction = (direction - navire.direction.direction) % 360
            # On détermine l'angle minimum fonction de la précision
            angle = norme_angle(round(r_direction / precision) * precision)
            visible.entrer_point(angle, v_dist, repere, nav_direction,
                    precision, exceptions=exceptions)
コード例 #41
0
ファイル: base.py プロジェクト: vlegoff/tsunami
class BaseType(BaseObj, metaclass=MetaType):

    """Classe abstraite représentant le type de base d'un objet.

    Si des données doivent être communes à tous les types d'objet
    (un objet a un nom, une description, quelque soit son type) c'est dans
    cette classe qu'elles apparaissent.

    Notons les attributs d'objet :
        empilable_sur -- une liste de chaînes définissant les types
                         sur lesquels on peut empiler le type d'objet
        empilable_sous -- une liste de chaînes identiques mais
                          désignant les types d'objets qui peuvent être
                          empilés par-dessus le type défini. On évitera
                          d'utiliser cet attribut sauf si le type
                          d'objet est défini dans un module secondaire

    """

    nom_type = "" # à redéfinir
    nom_scripting = "l'objet"
    type_achat = "objet"
    _nom = "base_type_objet"
    _version = 3

    # Doit-t-on nettoyer l'objet en cas d'inactivité
    nettoyer = True

    # Type d'objet sélectable dans le oedit
    selectable = True

    # Types enfants
    types = {}
    enregistrer = True

    # Équipement
    empilable_sur = []
    empilable_sous = []
    protection_froid = 0

    def __init__(self, cle=""):
        """Constructeur d'un type"""
        BaseObj.__init__(self)
        self.cle = cle
        self._attributs = {}
        self.no = 0 # nombre d'objets créés sur ce prototype
        self.nom_singulier = "un objet indéfini"
        self.etat_singulier = "est posé là"
        self.nom_pluriel = "objets indéfinis"
        self.etat_pluriel = "sont posés là"
        self.noms_sup = []
        self.description = Description(parent=self)
        self.objets = []
        self.unique = True # par défaut tout objet est unique
        self.flags = 0
        self._prix = 1 # valeur en magasin
        self.sans_prix = False
        self.poids_unitaire = 1 # 1 Kg
        self.depecer_de = []

        # Equipement
        self.peut_prendre = True # définit si on peut manipuler l'objet à main
        self.peut_tenir = False # définit si on peut tenir un objet par-dessus
        self.emplacement = ""
        self.epaisseur = 1
        self.positions = ()

        # Script
        self.script = ScriptObjet(self)
        self.etendre_script()

        # Editeur
        self._extensions_editeur = []

        # Erreur de validation du type
        self.err_type = "Le type de '{}' est invalide."

        self._construire()

    def __getnewargs__(self):
        return ()

    def __repr__(self):
        return "<{} {}>".format(self.nom_type, self.cle)

    def __str__(self):
        return self.cle

    def __getstate__(self):
        """Retourne le dictionnaire à enregistrer."""
        attrs = self.__dict__.copy()
        if "_extensions_editeur" in attrs:
            del attrs["_extensions_editeur"]
        if "_attributs" in attrs:
            del attrs["_attributs"]
        return attrs

    def _get_prix(self):
        """Retourne le prix"""
        return self._prix
    def _set_prix(self, prix):
        """Modifie le prix"""
        self._prix = int(prix)
    prix = property(_get_prix, _set_prix)

    @property
    def m_valeur(self):
        return self._prix

    @property
    def nom_achat(self):
        return self.nom_singulier

    @property
    def poids(self):
        """Retourne le poids unitaire."""
        return self.poids_unitaire

    def etendre_script(self):
        """Méthode appelée pour étendre le scripting.

        Si une classe-fille la surcharge, elle peut ajouter des évènements
        au script de ce type d'objet, par exemple.

        """
        pass

    def etendre_editeur(self, raccourci, ligne, editeur, objet, attribut, *sup):
        """Permet d'étendre l'éditeur d'objet en fonction du type.

        Paramètres à entrer :
        -   raccourci   le raccourci permettant d'accéder à la ligne
        -   ligne       la ligne de l'éditeur (exemple 'Description')
        -   editeur     le contexte-éditeur (exemple Uniligne)
        -   objet       l'objet à éditer
        -   attribut    l'attribut à éditer

        Cette méthode est appelée lors de la création de l'éditeur de
        prototype.

        """
        self._extensions_editeur.append(
            (raccourci, ligne, editeur, objet, attribut, sup))

    def reduire_editeur(self, raccourci):
        """Permet de supprimer un contexte-éditeur de la liste d'extensions."""
        sup = ()
        for editeur in self._extensions_editeur:
            if editeur[0] == raccourci:
                sup = editeur
                break
        if sup:
            self._extensions_editeur.remove(sup)

    def travailler_enveloppes(self, enveloppes):
        """Travail sur les enveloppes.

        On récupère un dictionnaire représentant la présentation avec en
        clé les raccourcis et en valeur les enveloppes.

        Cela peut permettre de travailler sur les enveloppes ajoutées par
        'etendre_editeur'.

        """
        pass

    def changer_type(self, nouveau_type):
        """Cette méthode force le changement de type du prototype.

        ATTENTION : cette méthode change le type en changeant '__class__'
        et performent certaines opérations automatiques. Il y a des
        conséquences à changer une instance de classe comme cela, et
        cela ne devrait pas se produire fréquemment ni automatiquement.

        """
        classe = importeur.objet.get_type(nouveau_type)
        self.__class__ = classe

        # On force le prototype d'objet à réinitialiser ses attributs manquants
        del self._extensions_editeur
        self.__setstate__(self.__dict__.copy())

        # Réinitialise les attributs
        for objet in self.objets:
            for nom, val in self._attributs.items():
                if not hasattr(objet, nom):
                    setattr(objet, nom, val.construire(self))

    def get_nom(self, nombre=1, pluriels=True):
        """Retourne le nom complet en fonction du nombre.

        Par exemple :
        Si nombre == 1 : retourne le nom singulier
        Sinon : retourne le nombre et le nom pluriel

        """
        if nombre <= 0:
            raise ValueError("la fonction get_nom a été appelée " \
                    "avec un nombre négatif ou nul.")
        elif nombre == 1:
            return self.nom_singulier
        else:
            if pluriels and self.noms_sup:
                noms_sup = list(self.noms_sup)
                noms_sup.reverse()
                for nom in noms_sup:
                    if nombre >= nom[0]:
                        variables = {}
                        variables["nb"] = nombre
                        for i in range(2, 1 + int(sqrt(nombre))):
                            variables["nb{}".format(i)] = nombre // i

                        return nom[1].format(nb=nombre,
                                nb2=nombre // 2, nb4=nombre // 4,
                                nb8=nombre // 8, nb16=nombre // 16)

            return str(nombre) + " " + self.nom_pluriel

    def get_nom_pour(self, personnage):
        """Retourne le nom pour le personnage précisé."""
        return self.get_nom()

    def get_nom_etat(self, nombre):
        """Retourne le nom et l'état en fonction du nombre."""
        nom = self.get_nom(nombre)
        if nombre == 1:
            return nom + " " + self.etat_singulier
        else:
            if self.noms_sup:
                noms_sup = list(self.noms_sup)
                noms_sup.reverse()
                for nom_sup in noms_sup:
                    if nombre >= nom_sup[0]:
                        return nom + " " + nom_sup[2]
            return nom + " " + self.etat_pluriel

    def extraire_contenus(self, quantite=None, contenu_dans=None):
        """Méthode redéfinie pour la manipulation d'objets non uniques."""
        return [self]

    def extraire_contenus_qtt(self):
        """Méthode redéfinie pour la manipulation d'objets non uniques."""
        return [(self, 1)]

    def est_de_type(self, nom_type):
        """Retourne True si le type d'objet est de celui entré ou dérivé.

        Par exemple, si on test si une épée est une arme, retournera True
        car le type 'arme' a pour classes-filles 'épée' (notamment).

        """
        classe = importeur.objet.types[nom_type]
        prototype = hasattr(self, "prototype") and self.prototype or self
        return isinstance(prototype, classe)

    def calculer_poids(self):
        """Retourne le poids de l'objet."""
        return self.poids_unitaire

    def objets_contenus(self, objet):
        """Retourne les objets contenus."""
        return []

    def detruire_objet(self, objet):
        """Détruit l'objet passé en paramètre.

        Par défaut cette méthode ne fait rien, mais si le type
        est fait pour contenir d'autres objets, il doit les détruire.

        """
        pass

    # Actions sur les objets
    def acheter(self, quantite, magasin, transaction):
        """Achète les objets dans la quantité spécifiée."""
        salle = magasin.parent
        objets = []
        for i in range(quantite):
            objet = importeur.objet.creer_objet(self)
            salle.objets_sol.ajouter(objet)
            objets.append(objet)

        return objets

    def peut_vendre(self, vendeur):
        """Retourne True si peut vendre l'objet."""
        return True

    def estimer_valeur(self, magasin, vendeur):
        """Estime la valeur d'un objet."""
        valeur = self.m_valeur
        return valeur * 0.7

    def peut_ramasser(self):
        """Essaye de ramasser l'objet."""
        return self.peut_prendre

    def regarder(self, personnage, variables=None):
        """Le personnage regarde l'objet"""
        salle = personnage.salle
        variables = variables or {}
        personnage << "Vous regardez {} :".format(self.get_nom())
        autre = "{{}} regarde {}.".format(self.get_nom())
        salle.envoyer(autre, personnage)

        # Appel du script regarde.avant
        self.script["regarde"]["avant"].executer(
                objet=self, personnage=personnage)

        description = self.description.regarder(personnage, self, variables)
        if not description:
            description = "Il n'y a rien de bien intéressant à voir."

        personnage << description

        # Appel du script regarde.après
        self.script["regarde"]["apres"].executer(
                objet=self, personnage=personnage)
        return ""

    def veut_jeter(self, personnage, sur):
        """Méthode appelée pour tester si le personnage peut jeter l'objet.

        On doit préciser :
            personnage -- le personnage voulant jeter l'objet
            sur -- sur quoi veut-il jeter l'objet ?

        Le dernier paramètre peut être n'importe quel élément observable
        (un autre objet, un autre personnage...).

        La méthode doit retourner :
            Une chaîne vide si l'objet ne peut pas être lancé
            Un nom de méthode à appeler si l'objet peut être lancé

        """
        return ""

    def jeter(self, personnage, sur):
        """Jette self sur sur.

        Les paramètres sont les mêmes que veut_jeter.

        On retourne :
            True si on a pu jeter l'objet
            False sinon

        """
        return False

    def poser(self, objet, personnage, qtt=1):
        """L'objet est posé."""
        objet.script["pose"].executer(objet=objet, personnage=personnage,
                quantite=Fraction(qtt))

    def detruire(self):
        """Destruction du prototype d'objet."""
        # Destruction des objets à dépecer
        for proto in self.depecer_de:
            if self in proto.a_depecer:
                del proto.a_depecer[self]

        BaseObj.detruire(self)

    def nettoyage_cyclique(self):
        """Nettoyage cyclique de l'objet si besoin."""
        pass

    def get_structure(self, structure):
        """Retourne la structure étenduee.

        La structure doit être envoyée par l'objet. Il est peu probable
        d'avoir besoin d'appeler cette méthode direction.

        """
        structure.type = self.nom_type
        flags = []
        for nom, valeur in FLAGS.items():
            if self.flags & valeur != 0:
                flags.append(nom)

        structure.flags = flags

    def appliquer_structure(self, structure):
        """Applique la structure passée en apramètre."""
        for cle, valeur in structure.donnees.items():
            if cle == "type":
                if valeur != self.nom_type:
                    self.changer_type(valeur)
コード例 #42
0
ファイル: mudmail.py プロジェクト: vlegoff/tsunami
class MUDmail(BaseObj):

    """Cette classe contient un mudmail.

    """

    def __init__(self, parent=None, expediteur=None, source=None):
        """Constructeur de la classe"""
        BaseObj.__init__(self)
        self.parent = parent
        self.id = -1
        self.notifier = True
        if source is not None: # édition d'un brouillon
            self._etat = BROUILLON
            self.sujet = str(source.sujet)
            self.expediteur = expediteur
            self.liste_dest = []
            for d in list(source.liste_dest):
                self.liste_dest.append(d)
            self.aliases = source.aliases
            self.copies_a = source.copies_a
            self.contenu = Description(parent=self, scriptable=False)
            self.contenu.ajouter_paragraphe(str(source.contenu))
            self.id_source = int(source.id)
        else:
            self._etat = EN_COURS
            self.sujet = "aucun sujet"
            self.expediteur = expediteur
            self.liste_dest = []
            self.aliases = []
            self.copies_a = []
            self.contenu = Description(parent=self)
            self.id_source = 0
        self.destinataire = None
        self.date = None
        self.lu = False
        # On passe le statut en CONSTRUIT
        self._construire()

    def __getnewargs__(self):
        return ()

    def __getstate__(self):
        """Enregistrement de l'objet.

        On ne peut pas enregistrer les salles telles qu'elles car
        MongoDB n'aime pas les dictionnaires contenant des tuples en
        clés.

        """
        def retirer_aliases(liste):
            for i, e in enumerate(liste):
                if isinstance(e, type) and issubclass(e, Alias):
                    liste[i] = e.nom_alias

        attrs = BaseObj.__getstate__(self)
        retirer_aliases(attrs["liste_dest"])
        retirer_aliases(attrs["aliases"])
        retirer_aliases(attrs["copies_a"])
        return attrs

    def __setstate__(self, attrs):
        """Récupération de l'objet enregistré."""
        def remettre_aliases(liste):
            for i, e in enumerate(liste):
                if isinstance(e, str):
                    liste[i] = aliases[e]

        remettre_aliases(attrs["liste_dest"])
        remettre_aliases(attrs["aliases"])
        remettre_aliases(attrs["copies_a"])
        BaseObj.__setstate__(self, attrs)

    @property
    def etat(self):
        """Renvoie l'état du mail"""
        return self._etat

    @property
    def aff_dest(self):
        """Renvoie le(s) destinataire(s) si existant(s)"""
        res = ""
        if self.destinataire:
            res += self.destinataire.nom
        else:
            res += ", ".join([dest.nom for dest in self.liste_dest])
            if self.aliases:
                res += ", " if res else ""
                res += ", ".join(["|bc|@" + a.nom_alias + "|ff|" \
                        for a in self.aliases])
            if not res:
                res += "aucun"
        return res

    @property
    def apercu_contenu(self):
        """Renvoie un aperçu du corps du message"""
        apercu = self.contenu.paragraphes_indentes
        if apercu == "\n   Aucune description.":
            apercu = "\n   Aucun contenu."
        return apercu

    @property
    def nom_expediteur(self):
        """Retourne, si trouvé, le nom de l'expéditeur."""
        return self.expediteur and self.expediteur.nom or "inconnu"

    def afficher(self):
        """Affiche le mail"""
        ret = "Expéditeur      : " + self.expediteur.nom + "\n"
        ret += "Destinataire(s) : " + self.aff_dest + "\n"
        ret += "Sujet           : " + echapper_accolades(self.sujet) + "\n"
        ret += echapper_accolades(str(self.contenu))
        ret += "\n" + get_date(self.date.timetuple()).capitalize() + "."
        return ret

    def envoyer(self):
        """Envoie le mail"""
        liste_dest = set(self.liste_dest)
        if self.aliases:
            for alias in self.aliases:
                [liste_dest.add(j) for j in alias.retourner_joueurs()]
        if self.expediteur in liste_dest:
            liste_dest.remove(self.expediteur)

        if not liste_dest:
            raise ValueError("la liste de destinataires est vide")

        self.date = datetime.datetime.now()
        for dest in liste_dest:
            mail = type(self).importeur.communication.mails.creer_mail(
                    self.expediteur)
            mail.date = datetime.datetime.now()
            mail.sujet = self.sujet
            mail.destinataire = dest
            mail.contenu = self.contenu
            mail.copies_a = list(self.liste_dest) + list(self.aliases)
            if dest in mail.copies_a:
                mail.copies_a.remove(dest)
            mail._etat = RECU
            if dest in type(self).importeur.connex.joueurs_connectes:
                dest << "\n|jn|Vous avez reçu un nouveau message.|ff|"
            if self.notifier:
                self.envoyer_email(dest)
        self._etat = ENVOYE

    def archiver(self):
        """Archive le mail"""
        self._etat = ARCHIVE

    def restaurer(self):
        """Restaure le mail"""
        self._etat = RECU

    def envoyer_email(self, dest):
        """Envoie l'e-mail au destinataire."""
        if not dest.compte.email:
            return

        destinateur = "equipe"
        destinataire = dest.compte.adresse_email
        expediteur = self.expediteur.nom
        sujet = "[VanciaMUD] : " + self.sujet
        nom_compte = dest.compte.nom
        contenu = self.afficher().replace("|tab|", "   ")
        corps = contenu + bas_page.format(nom_compte=nom_compte,
                expediteur=expediteur)
        if not importeur.email.serveur_mail:
            return

        if not destinataire:
            return

        importeur.email.envoyer(destinateur, destinataire, sujet, corps)
コード例 #43
0
class Detail(BaseObj):
    """Cette classe représente un détail observable dans une salle.

    Elle permet d'ajouter à la description d'une salle des détails invisibles
    au premier abord, mais discernable avec la commande look.

    """

    nom_scripting = "le détail"

    def __init__(self, nom, parent=None, modele=None):
        """Constructeur de la classe"""
        BaseObj.__init__(self)
        self.nom = nom
        self.synonymes = []
        self.titre = "un détail aux alentours"
        self.description = Description(parent=self)
        self.positions = {}
        self.est_visible = True
        self.script = ScriptDetail(self)
        self.peut_asseoir = False
        self.peut_allonger = False
        self.facteur_asseoir = 1.1
        self.facteur_allonger = 1.2
        self.connecteur = "sur"
        self.nb_places_assises = 1
        self.nb_places_allongees = 1
        self.parent = parent
        self.flags = 0
        self.supporte = None
        self._peut_supporter = 0.0
        self.message_supporte = "Dessus se trouve"
        self.message_installation = "Vous posez %objet sur %detail."
        self.message_desinstallation = "Vous retirez %objet% de %detail."
        if modele is not None:
            self.synonymes = modele.synonymes
            self.titre = modele.titre
            self.description = modele.description
        # On passe le statut en CONSTRUIT
        self._construire()

    def __getnewargs__(self):
        return ("", "")

    def __str__(self):
        return self.titre.lower()

    @property
    def repos(self):
        return oui_ou_non(self.peut_asseoir or self.peut_allonger)

    @property
    def support(self):
        """Retourne oui_ou_non."""
        return oui_ou_non(self._peut_supporter > 0)

    def _get_peut_supporter(self):
        return self._peut_supporter

    def _set_peut_supporter(self, poids):
        self._peut_supporter = poids
        if poids:
            if self.supporte is None:
                self.supporte = ConteneurObjet(self)
        else:
            if self.supporte:
                self.supporte.detruire()
                self.supporte = None

    peut_supporter = property(_get_peut_supporter, _set_peut_supporter)

    def get_nom_pour(self, personnage):
        """Retourne le nom pour le personnage précisé."""
        return self.titre

    def a_flag(self, nom_flag):
        """Retourne True si le détail a le flag, False sinon."""
        valeur = FLAGS[nom_flag]
        return self.flags & valeur != 0

    def regarder(self, personnage):
        """Le personnage regarde le détail"""
        if not self.est_visible:
            personnage << "Il n'y a rien qui ressemble à cela par ici..."
            return
        personnage << "Vous examinez {} :".format(self.titre)
        self.script["regarde"]["avant"].executer(personnage=personnage,
                                                 salle=personnage.salle)
        description = self.description.regarder(personnage, self)
        if not description:
            description = "Il n'y a rien de bien intéressant à voir."

        personnage << description

        self.script["regarde"]["apres"].executer(personnage=personnage,
                                                 salle=personnage.salle)
コード例 #44
0
class MUDmail(BaseObj):
    """Cette classe contient un mudmail.

    """
    def __init__(self, parent=None, expediteur=None, source=None):
        """Constructeur de la classe"""
        BaseObj.__init__(self)
        self.parent = parent
        self.id = -1
        self.notifier = True
        if source is not None:  # édition d'un brouillon
            self._etat = BROUILLON
            self.sujet = str(source.sujet)
            self.expediteur = expediteur
            self.liste_dest = []
            for d in list(source.liste_dest):
                self.liste_dest.append(d)
            self.aliases = source.aliases
            self.copies_a = source.copies_a
            self.contenu = Description(parent=self, scriptable=False)
            self.contenu.ajouter_paragraphe(str(source.contenu))
            self.id_source = int(source.id)
        else:
            self._etat = EN_COURS
            self.sujet = "aucun sujet"
            self.expediteur = expediteur
            self.liste_dest = []
            self.aliases = []
            self.copies_a = []
            self.contenu = Description(parent=self)
            self.id_source = 0
        self.destinataire = None
        self.date = None
        self.lu = False
        # On passe le statut en CONSTRUIT
        self._construire()

    def __getnewargs__(self):
        return ()

    def __getstate__(self):
        """Enregistrement de l'objet.

        On ne peut pas enregistrer les salles telles qu'elles car
        MongoDB n'aime pas les dictionnaires contenant des tuples en
        clés.

        """
        def retirer_aliases(liste):
            for i, e in enumerate(liste):
                if isinstance(e, type) and issubclass(e, Alias):
                    liste[i] = e.nom_alias

        attrs = BaseObj.__getstate__(self)
        retirer_aliases(attrs["liste_dest"])
        retirer_aliases(attrs["aliases"])
        retirer_aliases(attrs["copies_a"])
        return attrs

    def __setstate__(self, attrs):
        """Récupération de l'objet enregistré."""
        def remettre_aliases(liste):
            for i, e in enumerate(liste):
                if isinstance(e, str):
                    liste[i] = aliases[e]

        remettre_aliases(attrs["liste_dest"])
        remettre_aliases(attrs["aliases"])
        remettre_aliases(attrs["copies_a"])
        BaseObj.__setstate__(self, attrs)

    @property
    def etat(self):
        """Renvoie l'état du mail"""
        return self._etat

    @property
    def aff_dest(self):
        """Renvoie le(s) destinataire(s) si existant(s)"""
        res = ""
        if self.destinataire:
            res += self.destinataire.nom
        else:
            res += ", ".join([dest.nom for dest in self.liste_dest])
            if self.aliases:
                res += ", " if res else ""
                res += ", ".join(["|bc|@" + a.nom_alias + "|ff|" \
                        for a in self.aliases])
            if not res:
                res += "aucun"
        return res

    @property
    def apercu_contenu(self):
        """Renvoie un aperçu du corps du message"""
        apercu = self.contenu.paragraphes_indentes
        if apercu == "\n   Aucune description.":
            apercu = "\n   Aucun contenu."
        return apercu

    @property
    def nom_expediteur(self):
        """Retourne, si trouvé, le nom de l'expéditeur."""
        return self.expediteur and self.expediteur.nom or "inconnu"

    def afficher(self):
        """Affiche le mail"""
        ret = "Expéditeur      : " + self.expediteur.nom + "\n"
        ret += "Destinataire(s) : " + self.aff_dest + "\n"
        ret += "Sujet           : " + echapper_accolades(self.sujet) + "\n"
        ret += echapper_accolades(str(self.contenu))
        ret += "\n" + get_date(self.date.timetuple()).capitalize() + "."
        return ret

    def envoyer(self):
        """Envoie le mail"""
        liste_dest = set(self.liste_dest)
        if self.aliases:
            for alias in self.aliases:
                [liste_dest.add(j) for j in alias.retourner_joueurs()]
        if self.expediteur in liste_dest:
            liste_dest.remove(self.expediteur)

        if not liste_dest:
            raise ValueError("la liste de destinataires est vide")

        self.date = datetime.datetime.now()
        for dest in liste_dest:
            mail = type(self).importeur.communication.mails.creer_mail(
                self.expediteur)
            mail.date = datetime.datetime.now()
            mail.sujet = self.sujet
            mail.destinataire = dest
            mail.contenu = self.contenu
            mail.copies_a = list(self.liste_dest) + list(self.aliases)
            if dest in mail.copies_a:
                mail.copies_a.remove(dest)
            mail._etat = RECU
            if dest in type(self).importeur.connex.joueurs_connectes:
                dest << "\n|jn|Vous avez reçu un nouveau message.|ff|"
            if self.notifier:
                self.envoyer_email(dest)
        self._etat = ENVOYE

    def archiver(self):
        """Archive le mail"""
        self._etat = ARCHIVE

    def restaurer(self):
        """Restaure le mail"""
        self._etat = RECU

    def envoyer_email(self, dest):
        """Envoie l'e-mail au destinataire."""
        if not dest.compte.email:
            return

        destinateur = "equipe"
        destinataire = dest.compte.adresse_email
        expediteur = self.expediteur.nom
        sujet = "[VanciaMUD] : " + self.sujet
        nom_compte = dest.compte.nom
        contenu = self.afficher().replace("|tab|", "   ")
        corps = contenu + bas_page.format(nom_compte=nom_compte,
                                          expediteur=expediteur)
        if not importeur.email.serveur_mail:
            return

        if not destinataire:
            return

        importeur.email.envoyer(destinateur, destinataire, sujet, corps)
コード例 #45
0
 def __init__(self, prototype, titre):
     BaseObj.__init__(self)
     self.prototype = prototype
     self.titre = titre
     self.description = Description(parent=self)
コード例 #46
0
ファイル: base.py プロジェクト: vlegoff/tsunami
class BaseElement(BaseObj, metaclass=MetaElt):

    """Classe abstraite représentant le type de base d'un élément.

    Si des données doivent être communes à tous les types d'éléments
    c'est dans cette classe qu'elles apparaissent.

    """

    enregistrer = True
    nom_type = "" # à redéfinir
    def __init__(self, cle=""):
        """Constructeur d'un type"""
        if cle:
            valider_cle(cle)

        BaseObj.__init__(self)
        self.cle = cle

        self._attributs = {}
        self.nom = "un élément inconnu"
        self.description = Description(parent=self)

        # Editeur
        self._extensions_editeur = []

    def __getnewargs__(self):
        return ()

    def __str__(self):
        return self.cle

    def __getstate__(self):
        """Retourne le dictionnaire à enregistrer."""
        attrs = self.__dict__.copy()
        if "_extensions_editeur" in attrs:
            del attrs["_extensions_editeur"]
        if "_attributs" in attrs:
            del attrs["_attributs"]
        return attrs

    def editer(self, presentation):
        """Méthode à rédéfinir, appelée quand l'éditeur s'affiche.

        Cette méthode est à redéfinir quand on souhaite ajouter des
        options spécifiques à l'éditeur de l'élément en question.

        """
        pass

    def get_description_ligne(self, personnage):
        """Retourne une description d'une ligne de l'élément."""
        return self.nom.capitalize() + " est là"

    def construire(self, parent):
        """Construit l'élément basé sur le parent."""
        pass

    def get_nom_pour(self, personnage):
        """Retourne le nom de l'élément."""
        return self.nom

    def regarder(self, personnage):
        """personnage regarde self."""
        msg = "Vous regardez {} :".format(self.nom) + "\n\n"
        msg += self.description.regarder(personnage, self)
        return msg
コード例 #47
0
ファイル: fiche.py プロジェクト: wkta/tsunami
class FicheMatelot(BaseObj):
    """Fiche d'un matelot à créer.

    La fiche comprend des informations sur les aptitudes et postes
    spécifiques d'un matelot au niveau prototype. Une fiche pourrait, par
    exemple, dire que le prototype 'marin_ctn' a comme poste par défaut
    'charpentier' et la compétence 'calfeutrage' à 'bon' (ce qui se
    traduit, pour le PNJ créé depuis cette fiche, en l'obtension du
    talent 'calfeutrage' à quelque chose comme 30%).

    """

    enregistrer = True
    type_achat = "matelot"

    def __init__(self, prototype):
        """Constructeur du matelot."""
        BaseObj.__init__(self)
        self.prototype = prototype
        self.nom_singulier = "un matelot"
        self.nom_pluriel = "matelots"
        self.poste_defaut = "matelot"
        self.description = Description(parent=self)
        self.aptitudes = {}
        self.m_valeur = 20

    def __getnewargs__(self):
        return (None, )

    def __repr__(self):
        return "<FicheMâtelot {}>".format(repr(self.cle))

    def __str__(self):
        return self.cle

    @property
    def cle(self):
        return self.prototype and self.prototype.cle or "aucune"

    @property
    def nom_achat(self):
        return self.nom_singulier

    def get_nom(self, nombre=1):
        """Retourne le nom complet en fonction du nombre."""
        if nombre == 0:
            raise ValueError("Nombre invalide")
        elif nombre == 1:
            return self.nom_singulier
        else:
            return str(nombre) + " " + self.nom_pluriel

    def ajouter_aptitude(self, nom_aptitude, nom_niveau):
        """Ajoute une aptitude."""
        nom_aptitude = supprimer_accents(nom_aptitude).lower()
        nom_niveau = supprimer_accents(nom_niveau).lower()
        aptitude = CLES_APTITUDES.get(nom_aptitude)
        if aptitude is None:
            raise KeyError("Aptitude inconnue {}".format(repr(nom_aptitude)))

        niveau = VALEURS_NIVEAUX.get(nom_niveau)
        if niveau is None:
            raise KeyError("Niveau inconnu {}".format(repr(nom_niveau)))

        self.aptitudes[aptitude] = niveau

    def creer_PNJ(self, salle=None, nb=1):
        """Crée le PNJ sur la fiche."""
        pnjs = []
        if self.prototype is None:
            raise ValueError("Le prototype de cette fiche est inconnu")

        for i in range(nb):
            pnj = importeur.pnj.creer_PNJ(self.prototype, salle)
            pnjs.append(pnj)

            # Modifie les aptitudes
            for aptitude, niveau in self.aptitudes.items():
                talents = TALENTS.get(aptitude, [])
                connaissance = CONNAISSANCES[niveau]
                for talent in talents:
                    pnj.talents[talent] = connaissance

            # Annonce l'arrivée du PNJ
            if salle:
                salle.envoyer("{} arrive.", pnj)

        return pnjs

    def recruter(self, personnage, navire):
        """Recrute le matelot sur la fiche."""
        salle = navire.salles[0, 0, 0]
        personnage.salle = salle
        salle.envoyer("{} arrive.", personnage)
        navire.equipage.ajouter_matelot(personnage)

    def acheter(self, quantite, magasin, transaction):
        """Achète les matelots dans la quantité spécifiée."""
        salle = magasin.parent
        acheteur = transaction.initiateur
        nom = "trans_" + str(id(transaction))
        importeur.diffact.ajouter_action(nom, 10, self.creer_PNJ, salle,
                                         quantite)

    def regarder(self, personnage):
        """Le personnage regarde le service (avant achat)."""
        desc = self.description.regarder(personnage, elt=self.prototype)
        desc += "\n\nPoste (par défaut) : " + self.poste_defaut
        if self.aptitudes:
            desc += "\nAptitudes :\n"
            for aptitude, niveau in self.aptitudes.items():
                nom = NOMS_APTITUDES[aptitude]
                nom_niveau = NOMS_NIVEAUX[niveau]
                desc += "\n  {:<15} : {:>10} ({} / 6)".format(
                    nom.capitalize(), nom_niveau, niveau + 1)

        return desc
コード例 #48
0
ファイル: fiche.py プロジェクト: vlegoff/tsunami
class FicheMatelot(BaseObj):

    """Fiche d'un matelot à créer.

    La fiche comprend des informations sur les aptitudes et postes
    spécifiques d'un matelot au niveau prototype. Une fiche pourrait, par
    exemple, dire que le prototype 'marin_ctn' a comme poste par défaut
    'charpentier' et la compétence 'calfeutrage' à 'bon' (ce qui se
    traduit, pour le PNJ créé depuis cette fiche, en l'obtension du
    talent 'calfeutrage' à quelque chose comme 30%).

    """

    enregistrer = True
    type_achat = "matelot"
    def __init__(self, prototype):
        """Constructeur du matelot."""
        BaseObj.__init__(self)
        self.prototype = prototype
        self.nom_singulier = "un matelot"
        self.nom_pluriel = "matelots"
        self.poste_defaut = "matelot"
        self.description = Description(parent=self)
        self.aptitudes = {}
        self.m_valeur = 20

    def __getnewargs__(self):
        return (None, )

    def __repr__(self):
        return "<FicheMâtelot {}>".format(repr(self.cle))

    def __str__(self):
        return self.cle

    @property
    def cle(self):
        return self.prototype and self.prototype.cle or "aucune"

    @property
    def nom_achat(self):
        return self.nom_singulier

    def get_nom(self, nombre=1):
        """Retourne le nom complet en fonction du nombre."""
        if nombre == 0:
            raise ValueError("Nombre invalide")
        elif nombre == 1:
            return self.nom_singulier
        else:
            return str(nombre) + " " + self.nom_pluriel

    def ajouter_aptitude(self, nom_aptitude, nom_niveau):
        """Ajoute une aptitude."""
        nom_aptitude = supprimer_accents(nom_aptitude).lower()
        nom_niveau = supprimer_accents(nom_niveau).lower()
        aptitude = CLES_APTITUDES.get(nom_aptitude)
        if aptitude is None:
            raise KeyError("Aptitude inconnue {}".format(repr(nom_aptitude)))

        niveau = VALEURS_NIVEAUX.get(nom_niveau)
        if niveau is None:
            raise KeyError("Niveau inconnu {}".format(repr(nom_niveau)))

        self.aptitudes[aptitude] = niveau

    def creer_PNJ(self, salle=None, nb=1):
        """Crée le PNJ sur la fiche."""
        pnjs = []
        if self.prototype is None:
            raise ValueError("Le prototype de cette fiche est inconnu")

        for i in range(nb):
            pnj = importeur.pnj.creer_PNJ(self.prototype, salle)
            pnjs.append(pnj)

            # Modifie les aptitudes
            for aptitude, niveau in self.aptitudes.items():
                talents = TALENTS.get(aptitude, [])
                connaissance = CONNAISSANCES[niveau]
                for talent in talents:
                    pnj.talents[talent] = connaissance

            # Annonce l'arrivée du PNJ
            if salle:
                salle.envoyer("{} arrive.", pnj)

        return pnjs

    def recruter(self, personnage, navire):
        """Recrute le matelot sur la fiche."""
        salle = navire.salles[0, 0, 0]
        personnage.salle = salle
        salle.envoyer("{} arrive.", personnage)
        navire.equipage.ajouter_matelot(personnage)

    def acheter(self, quantite, magasin, transaction):
        """Achète les matelots dans la quantité spécifiée."""
        salle = magasin.parent
        acheteur = transaction.initiateur
        nom = "trans_" + str(id(transaction))
        importeur.diffact.ajouter_action(nom, 10, self.creer_PNJ,
                salle, quantite)

    def regarder(self, personnage):
        """Le personnage regarde le service (avant achat)."""
        desc = self.description.regarder(personnage, elt=self.prototype)
        desc += "\n\nPoste (par défaut) : " + self.poste_defaut
        if self.aptitudes:
            desc += "\nAptitudes :\n"
            for aptitude, niveau in self.aptitudes.items():
                nom = NOMS_APTITUDES[aptitude]
                nom_niveau = NOMS_NIVEAUX[niveau]
                desc += "\n  {:<15} : {:>10} ({} / 6)".format(
                        nom.capitalize(), nom_niveau, niveau + 1)

        return desc
コード例 #49
0
ファイル: salle.py プロジェクト: stormi/tsunami
class Salle(BaseObj):

    """Classe représentant une salle de l'univers.

    Une salle est un élément détaillant la géographie locale d'une petite
    portion de l'univers. Bien que cela dépende des MUDs, les salles décrivent
    généralement un espace d'environ 5 mètres sur 5.

    Ces salles comportent une description détaillant les alentours proches.
    Cette description est envoyée à chaque fois qu'un personnage se déplace
    dans l'univers, pour lui donner une idée de son nouvel environnement.

    Note sur le positionnement des salles :
        Les salles peuvent être caractérisées par des coordonnées. Ces
        coordonnées sont en soi facultatives. Il est possible de créer un
        univers sans aucune coordonnée. Il s'agit d'une facilité
        lors de la constitution de votre univers qui permet à certains
        modules, comme 'vehicule', de fonctionner. Si votre salle n'a pas de
        coordonnées, vous devrez créer chaque sortie "à la main".
        Les salles ne sont donc pas identifiées par leurs coordonnées, sauf
        dans certains cas, mais bien par leur zone et mnémonique. Ce couple
        caractérise de façon unique une salle dans l'univers.
        Exemple : une salle ayant pour zone 'picte' et pour mnémonique '1'
        sera accessible depuis la clé 'picte:1' ; aucune autre salle de
        l'univers ne pourra posséder cette clé 'picte:1'.

    """

    nom_scripting = "la salle"
    _nom = "salle"
    _version = 4

    enregistrer = True
    def __init__(self, zone, mnemonic, x=0, y=0, z=0, valide=True):
        """Constructeur de la salle"""
        BaseObj.__init__(self)
        self._nom_zone = zone
        self._mnemonic = mnemonic
        self.coords = Coordonnees(x, y, z, valide, self)
        self.nom_terrain = "ville"
        self.titre = ""
        self.description = Description(parent=self)
        self.sorties = Sorties(parent=self)
        self.details = Details(parent=self)
        self._personnages = []
        self.objets_sol = ObjetsSol(parent=self)
        self.script = ScriptSalle(self)
        self.interieur = False
        self.illuminee = False
        self.magasin = None
        self.flags = 0

        # Repop
        self.pnj_repop = {}

        # Etendue
        self.etendue = None

        # Affections
        self.affections = {}

        # Décors
        self.decors = []

        self._construire()

    def __getnewargs__(self):
        return ("", "")

    def __repr__(self):
        """Affichage de la salle en mode debug"""
        return self._nom_zone + ":" + self._mnemonic

    def __str__(self):
        """Retourne l'identifiant 'zone:mnemonic'"""
        return self._nom_zone + ":" + self._mnemonic

    def _get_nom_zone(self):
        return self._nom_zone
    def _set_nom_zone(self, zone):
        ident = self.ident
        self._nom_zone = zone.lower()
        type(self).importeur.salle.changer_ident(ident, self.ident)

    def _get_mnemonic(self):
        return self._mnemonic
    def _set_mnemonic(self, mnemonic):
        ident = self.ident
        self._mnemonic = mnemonic.lower()
        type(self).importeur.salle.changer_ident(ident, self.ident)

    nom_zone = property(_get_nom_zone, _set_nom_zone)
    mnemonic = property(_get_mnemonic, _set_mnemonic)

    @property
    def zone(self):
        """Retourne la zone  correspondante."""
        return type(self).importeur.salle.get_zone(self._nom_zone)

    @property
    def ident(self):
        """Retourne l'identifiant, c'est-à-dire une chaîne 'zone:mnemonic'"""
        return "{}:{}".format(self._nom_zone, self._mnemonic)

    @property
    def personnages(self):
        """Retourne une liste déférencée des personnages"""
        return list(self._personnages)

    @property
    def PNJ(self):
        """Retourne une liste déférencée des PNJ présents."""
        return [p for p in self._personnages if hasattr(p, "prototype")]

    @property
    def joueurs(self):
        """Retourne une liste déférencée des joueurs présents."""
        return [p for p in self._personnages if not hasattr(p, "prototype")]

    @property
    def exterieur(self):
        """Retourne True si la salle est extérieure, False sinon."""
        return not self.interieur

    @property
    def terrain(self):
        """Retourne l'objet terrain."""
        return type(self).importeur.salle.terrains[self.nom_terrain]

    @property
    def desc_survol(self):
        return self.terrain.desc_survol

    @property
    def str_coords(self):
        x, y, z = self.coords.tuple()
        if self.coords.valide:
            return "{}.{}.{}".format(x, y, z)

        return "Aucune"

    def get_etendue(self):
        return self.etendue

    @property
    def nom_unique(self):
        return self.ident

    @property
    def objets_uniques(self):
        """Retourne les objets uniques posés dans la salle."""
        objets = []
        for objet in self.objets_sol._objets:
            objets.append(objet)
            objets.extend(objet.prototype.objets_contenus(objet))

        return objets

    @property
    def a_magasin(self):
        """Y a-t-il un magasin dans cette salle ?"""
        return self.magasin is not None
    
    @property
    def nb_sorties(self):
        """Retourne le mombre de sorties."""
        sorties = [s for s in self.sorties if s and s.salle_dest]
        return len(sorties)
    
    @property
    def details_etendus(self):
        """Retourne la liste des détails étendus.
        
        Cette méthode retourne les détails de la salle et ceux des
        flottantes contenus dans la description.
        
        """
        details = self.details._details.copy()
        description = self.description
        for paragraphe in description.paragraphes:
            p, flottantes = description.charger_descriptions_flottantes(
                    paragraphe)
            for flottante in flottantes:
                for d in flottante.details:
                    if d.nom not in details:
                        details[d.nom] = d
        
        return tuple(details.values())

    def voit_ici(self, personnage):
        """Retourne True si le personnage peut voir ici, False sinon.

        Un personnage peut voir dans la salle si :
            Il est immortel
            La salle est illuminée
            Le personnage est un PNJ nyctalope
            Le personnage est un PNJ contrôlé par un immortel
            Il y a un feu dans la salle
            La race du personnage est nyctalope
            Le personnage a une affection qui lui permet de voir dans le noir
            La salle est en extérieure et :
                Il fait jour ou
                Le ciel est dégagé
            Le personnage a une lumière

        """
        if personnage.est_immortel():
            return True

        if self.illuminee:
            return True

        if hasattr(personnage, "prototype") and \
                (personnage.prototype.a_flag("nyctalope") or \
                personnage.controle_par):
            return True
        
        if self.ident in importeur.salle.feux:
            return True

        # Vérification de la race
        if personnage.race and personnage.race.a_flag("nyctalope"):
            return True

        # Vérification de l'affection
        for affection in personnage.affections.values():
            if affection.affection.a_flag("voit dans le noir"):
                return True

        if not self.interieur:
            if importeur.temps.temps.il_fait_jour:
                return True

            perturbation = importeur.meteo.get_perturbation(self)
            if perturbation is None or not perturbation.est_opaque():
                return True

        for objet in personnage.equipement.equipes:
            if objet.est_de_type("lumière") and objet.allumee_depuis:
                return True

        return False

    def a_detail_flag(self, flag):
        """Retourne True si la salle a un détail du flag indiqué."""
        for detail in self.details:
            if detail.a_flag(flag):
                return True

        description = self.description
        for paragraphe in description.paragraphes:
            p, flottantes = description.charger_descriptions_flottantes(
                    paragraphe)
            for flottante in flottantes:
                for d in flottante.details:
                    if d.a_flag(flag):
                        return True
        
        return False

    def personnage_est_present(self, personnage):
        """Si le personnage est présent, retourne True, False sinon."""
        return personnage in self._personnages

    def a_flag(self, nom_flag):
        """Retourne True si la salle a le flag, False sinon."""
        valeur = FLAGS[nom_flag]
        return self.flags & valeur != 0

    def ajouter_personnage(self, personnage):
        """Ajoute le personnage dans la salle"""
        if personnage not in self._personnages:
            self._personnages.append(personnage)

    def retirer_personnage(self, personnage):
        """Retire le personnage des personnages présents"""
        if personnage in self.personnages:
            self._personnages.remove(personnage)

    def salles_autour(self, rayon=5):
        """Retourne les chemins autour de self dans le rayon précisé.

        Si la salle a des coordonnées valide, ajoute également
        les salles semblant proches. Cependant, pour celles-ci,
        presque aucune vérification de chemin n'est faite,
        c'est-à-dire qu'elles peuvent être entièrement inaccessible.

        """
        if self.accepte_discontinu():
            empruntable = False
        else:
            empruntable = True

        return Chemins.salles_autour(self, rayon, empruntable=empruntable)

    def trouver_chemin_absolu(self, destination, rayon=5,
            explicite=False):
        """Retourne, si trouvé, le chemin menant à destination ou None.

        Le rayon passé en argument est celui de recherche. Plus il est
        élevé, plus le temps de calcul risque d'être important.

        """
        chemins = Chemins.salles_autour(self, rayon, absolu=True)
        chemin = chemins.get(destination)
        if chemin is None and explicite:
            raise ValueError("le chemin absolu est introuvable")

        return chemin

    def trouver_chemin(self, destination, explicite=False):
        """Recherche et retourne le chemin entre deux salles.

        Plusieurs algorithmes de recherche sont utilisés en fonction
        des situations. Pour des raisons de performance, certains sont
        plus efficaces que d'autres.

        Avant tout, plusieurs chemins peuvent être retournés :
        *   Un chemin continue (toutes les salles entre deux points)
        *   Un chemin discontinu (ou brisé).

        La grande différence est qu'un personnage peut emprunter
        un chemin continu pour aller d'une salle à une autre, mais
        ne peut pas emprunter un chemin discontinu. Les chemins
        discontinus ne sont pas tolérés par défaut (ceci est réglable).

        La recherche du chemin (continu ou discontinu) fait ensuite
        appel à deux algorithmes :
        *   Si les coordonnées des deux salles sont valides, cherche le
            chemin relatifs (Chemin.salles_entre)
        *   Sinon, cherche le chemin absolu (toutes les salles
            autour de la première salle dans un rayon d'estime).

        Si explicite est mis à True, lève une exception ValueError
        décrivant pourquoi la recherche a échouée.

        """
        # Si l'une des salles n'a pas de coordonnées valide
        if self.coords.invalide or destination.coords.invalide:
            return self.trouver_chemin_absolu(destination, 4,
                    explicite=explicite)

        # Les deux salles ont des coordonnées valides
        # On vérifie que le rayon n'es tpas trop important
        v_origine = Vecteur(*self.coords.tuple())
        v_destination = Vecteur(*destination.coords.tuple())
        distance = (v_destination - v_origine).norme
        if distance > 25:
            if explicite:
                raise ValueError("la distance entre les deux salles " \
                        "est supérieure à 25")

            return None

        salles = Chemins.get_salles_entre(self, destination)

        # On détermine, si possible, le chemin entre chaque salle
        chemin = Chemin()
        a_salle = None
        continu = True
        for d_salle in salles:
            if a_salle is None:
                a_salle = d_salle
                continue

            d_chemin = a_salle.trouver_chemin_absolu(d_salle, 2)
            if d_chemin is None:
                continu = False
                vecteur = Vecteur(*d_salle.coords.tuple()) - \
                        Vecteur(*a_salle.coords.tuple())
                sortie = a_salle.get_sortie(vecteur, d_salle)
                chemin.sorties.append(sortie)
            else:
                chemin.sorties.extend(d_chemin.sorties)

            a_salle = d_salle

        if chemin.origine is not self or chemin.destination is not destination:
            if explicite:
                raise ValueError("le chemin retourné ne commence ou " \
                        "ne finit pas au bon endroit")

            return None

        if not continu and (not self.accepte_discontinu() or not \
                destination.accepte_discontinu()):
            if explicite:
                raise ValueError("un chemin non continu a été retourné")

            return None

        chemin.raccourcir()
        return chemin

    def accepte_discontinu(self):
        """Retourne True si cette salle supporte les chemins discontinu.

        Tsunami supporte ce type de chemin si la salle est une côte
        de l'étendue.

        """
        return self.etendue is not None

    def trouver_chemins_droits(self, rayon, dir_sortie=None, chemins=None,
            chemin=None, hook=True):
        """Cherche les chemins droits d'une salle.

        La recherche de salle peut être étendue par un hook.
        Les chemins droits veulent dire que l'on vérifie les sorties
        initiales. Par exemple, si la salle A a la sortie est menant vers
        B, on demande à la salle B ses sorties... mais on ne sélectionnera
        que les sorties de même direcition que la salle A (c'est-à-dire est
        ici).

        """
        chemins = chemins or Chemins()
        chemin = chemin or Chemin()

        # On parcourt les sorties de la salle
        for sortie in self.sorties:
            if dir_sortie is None or sortie.direction == dir_sortie:
                n_chemin = Chemin()
                n_chemin.sorties.extend(chemin.sorties)
                n_chemin.sorties.append(sortie)
                origine = n_chemin.origine
                destination = n_chemin.destination
                ancien_chemin = chemins.get(destination)
                if origine is not destination and (ancien_chemin is None or \
                        len(ancien_chemin) > len(n_chemin)):
                    # On retire l'ancien chemin si besoin
                    if ancien_chemin:
                        chemins.chemins.remove(ancien_chemin)
                    chemins.chemins.append(n_chemin)
                    if rayon > 1:
                        destination.trouver_chemins_droits(rayon - 1,
                                sortie.direction, chemins, n_chemin, False)

        if hook:
            importeur.hook["salle:trouver_chemins_droits"].executer(self,
                    chemins, rayon)
        return chemins

    def get_sortie(self, vecteur, destination):
        """Retourne une sortie en fonction du vecteur donné."""
        sortie = Sortie(vecteur.nom_direction, vecteur.nom_direction,
                        "le", destination, "", self)
        sortie.longueur = vecteur.norme
        return sortie

    def sortie_empruntable(self, sortie):
        """Retourne True si la sortie est empruntable, False sinon.

        Pour une salle standard, une sortie est empruntable si elle existe
        réellement.

        """
        return sortie.direction in self.sorties

    def envoyer(self, message, *personnages, prompt=True, mort=False,
            ignore=True, lisser=False, **kw_personnages):
        """Envoie le message aux personnages présents dans la salle.

        Les personnages dans les paramètres supplémentaires (nommés ou non)
        sont utilisés pour formatter le message et font figure d'exceptions.
        Ils ne recevront pas le message.

        """
        exceptions = personnages + tuple(kw_personnages.values()) if ignore \
                else ()
        for personnage in self.personnages:
            if personnage not in exceptions:
                if personnage.est_mort() and not mort:
                    continue

                if hasattr(personnage, "instance_connexion") and \
                        personnage.instance_connexion and not prompt:
                    personnage.instance_connexion.sans_prompt()
                personnage.envoyer(message, *personnages, lisser=lisser,
                        **kw_personnages)

    def envoyer_lisser(self, chaine, *personnages, **kw_personnages):
        """Méthode redirigeant vers envoyer mais lissant la chaîne."""
        self.envoyer(chaine, *personnages, lisser=True, **kw_personnages)

    def get_elements_observables(self, personnage):
        """Retourne une liste des éléments observables dans cette salle."""
        liste = []
        for methode in importeur.salle.details_dynamiques:
            liste.extend(methode(self, personnage))

        return liste

    def regarder(self, personnage):
        """Le personnage regarde la salle"""
        if personnage.est_mort():
            personnage << "|err|Vous êtes inconscient et ne voyez pas " \
                    "grand chose...|ff|"
            return

        if not self.voit_ici(personnage):
            personnage << "Il y fait trop sombre pour vos sens, " \
                    "l'obscurité vous environne."
            return

        res = ""
        if personnage.est_immortel():
            res += "# |rgc|" + self.nom_zone + "|ff|:|vrc|" + self.mnemonic
            res += "|ff| ({})".format(self.coords)
            res += "\n\n"
        res += "   |tit|" + (self.titre or "Une salle sans titre") + "|ff|\n"
        description = self.description.regarder(personnage, self)
        if not description:
            description = "   Vous êtes au milieu de nulle part."
        res += description + "\n"

        res_decors = []
        for nom in self.regrouper_decors():
            res_decors.append(nom.capitalize() + ".")

        if res_decors:
            res += "\n".join(res_decors) + "\n"

        plus = self.decrire_plus(personnage)
        if plus:
            res += plus + "\n"

        res_affections = []
        for affection in self.affections.values():
            message = affection.affection.message(affection)
            if message:
                res_affections.append(message)

        if res_affections:
            res += "\n".join(res_affections) + "\n"

        liste_messages = []
        flags = 0
        type(self).importeur.hook["salle:regarder"].executer(self,
                liste_messages, flags)
        if liste_messages:
            res += "\n".join(liste_messages) + "\n"

        res += "\nSorties : "
        res += self.afficher_sorties(personnage)

        # Personnages
        personnages = OrderedDict()
        # Si le personnage est un joueur, il se retrouve avec un nombre de 1
        # Si le personnage est un PNJ, on conserve son prototype avec
        # le nombre d'occurences de prototypes apparaissant
        etats = {}
        for personne in self.personnages:
            if personne is not personnage:
                if not hasattr(personne, "prototype"):
                    if personnage.peut_voir(personne):
                        personnages[personne] = 1
                else:
                    doit = importeur.hook["pnj:doit_afficher"].executer(personne)
                    if any(not flag for flag in doit):
                        continue

                    nom = personne.get_nom_etat(personnage)
                    if nom in etats:
                        prototype = etats[nom]
                        personnages[prototype] = \
                                personnages[prototype] + 1
                    else:
                        etats[nom] = personne
                        personnages[personne] = 1

        if len(personnages):
            res += "\n"

            for personne, nombre in personnages.items():
                res += "\n- {}".format(personne.get_nom_etat(
                            personnage, nombre))

        # Objets
        noms_objets = self.afficher_noms_objets()
        if len(noms_objets):
            res += "\n"
            for nom_objet in noms_objets:
                res += "\n+ {}".format(nom_objet)

        return res

    def afficher_sorties(self, personnage):
        """Affiche les sorties de la salle"""
        noms = []
        for nom in NOMS_SORTIES.keys():
            sortie = self.sorties[nom]
            if sortie:
                nom = sortie.nom

            nom_aff = self.sorties.get_nom_abrege(nom)
            if self.sorties.sortie_existe(nom):
                if sortie.porte and sortie.porte.fermee:
                    res = "[|rgc|" + nom_aff + "|ff|]"
                else:
                    res = "|vr|" + nom_aff + "|ff|"
                if sortie.cachee:
                    if personnage.est_immortel():
                        res = "|rg|(I)|ff|" + res
                    else:
                        res = " ".ljust(len(self.sorties.get_nom_abrege(
                                sortie.direction)))
            else:
                res = " ".ljust(len(nom_aff))

            noms.append(res)

        return ", ".join(noms) + "."

    def afficher_noms_objets(self):
        """Retourne les noms et états des objets sur le sol de la salle"""
        objets = []
        for o, nb in self.objets_sol.get_objets_par_nom():
            objets.append(o.get_nom_etat(nb))

        return objets

    def decrire_plus(self, personnage):
        """Ajoute un message au-dessous de la description.

        Si cette méthode retourne une chaîne non vide, alors cette chaîne
        sera ajoutée sous la description quand un personnage regardera
        la salle.

        """
        pass

    def pop_pnj(self, pnj):
        """Méthode appelée quand un PNJ pop dans la salle."""
        pro = pnj.prototype
        if pro in self.pnj_repop:
            self.pnj_repop[pro] = self.pnj_repop[pro] - 1

    def det_pnj(self, pnj):
        """Méthode appelée quand un PNJ disparaît.."""
        pro = pnj.prototype
        if pro in self.pnj_repop:
            self.pnj_repop[pro] = self.pnj_repop[pro] + 1

    def repop(self):
        """Méthode appelée à chaque repop."""
        for pro, nb in self.pnj_repop.items():
            if nb > 0:
                for i in range(nb):
                    pnj = importeur.pnj.creer_PNJ(pro, self)
                    pnj.script["repop"].executer(pnj=pnj)


    def affecte(self, cle, duree, force):
        """Affecte la salle avec une affection.

        Si l'affection est déjà présente, la force est modulée.

        """
        affection = importeur.affection.get_affection("salle", cle)
        if cle in self.affections:
            concrete = self.affections[cle]
            affection.moduler(concrete, duree, force)
        else:
            concrete = Affection(affection, self, duree, force)
            self.affections[cle] = concrete
            concrete.affection.executer_script("cree", concrete)
            concrete.prevoir_tick()

    def peut_affecter(self, cle_affection):
        """La salle self peut-elle être affectée par l'affection ?"""
        if cle_affection == "neige":
            if self.interieur:
                return False
            elif self.nom_terrain in ["aquatique", "subaquatique"]:
                return False

            sortie = self.sorties["bas"]
            if sortie:
                return False

        return True

    def tick(self):
        """Méthode appelée à chaque tick de la salle."""
        for affection in self.affections.values():
            affection.affection.dec_duree(affection)

        for cle, affection in list(self.affections.items()):
            if not affection.e_existe or affection.duree <= 0:
                del self.affections[cle]

        self.script["tick"].executer(salle=self)

    def regrouper_decors(self):
        """Regroupe les décors par nom."""
        decors = OrderedDict()
        nombres = OrderedDict()
        res = OrderedDict()
        for decor in self.decors:
            nom = decor.get_nom()
            decors[nom] = decor
            nb = nombres.get(nom, 0)
            nb += 1
            nombres[nom] = nb

        for nom, nb in nombres.items():
            decor = decors[nom]
            nom = decor.get_nom_etat(nb)
            res[nom] = decor

        return res

    def ajouter_decor(self, prototype):
        """Ajoute un décor dans la salle."""
        if isinstance(prototype, PrototypeBonhommeNeige):
            decor = BonhommeNeige(prototype, self)
        else:
            decor = Decor(prototype, self)

        self.decors.append(decor)
        return decor

    def get_decors(self, cle):
        """Retourne tous les décors ayant la clé indiquée."""
        return [d for d in self.decors if d.prototype and \
                d.prototype.cle == cle]

    def supprimer_decor(self, decor):
        """Supprime les décors indiqués."""
        self.decors.remove(decor)
        decor.detruire()

    def supprimer_decors(self, cle):
        """Supprime les décors de clé indiquée."""
        for decor in self.get_decors(cle):
            self.supprimer_decor(decor)
コード例 #50
0
ファイル: description.py プロジェクト: vlegoff/tsunami
class Description(Editeur):

    """Contexte-éditeur description.

    Ce contexte sert à éditer des descriptions.

    """

    nom = "editeur:base:description"

    def __init__(self, pere, objet=None, attribut=None):
        """Constructeur de l'éditeur"""
        attribut = attribut or "description"
        Editeur.__init__(self, pere, objet, attribut)
        self.opts.echp_sp_cars = False
        self.nom_attribut = attribut
        contenu = ""
        if objet:
            contenu = getattr(self.objet, self.nom_attribut)
            if contenu is None:
                setattr(self.objet, self.nom_attribut, "")
            else:
                contenu = str(contenu)

        self.description_complete = Desc(parent=objet, scriptable=False)
        if contenu:
            for paragraphe in contenu.split("\n"):
                self.description_complete.ajouter_paragraphe(
                        paragraphe.replace("|nl|", " "))

        self.ajouter_option("?", self.opt_aide)
        self.ajouter_option("j", self.opt_ajouter_paragraphe)
        self.ajouter_option("a", self.opt_inserer_paragraphe)
        self.ajouter_option("d", self.opt_supprimer)
        self.ajouter_option("r", self.opt_remplacer)
        self.ajouter_option("e", self.opt_editer_evt)
        self.ajouter_option("t", self.opt_tabulations)
        self.ajouter_option("de", self.opt_supprimer_evt)
        self.ajouter_option("re", self.opt_renommer_evt)
        self.ajouter_option("o", self.opt_editer_options)

    @property
    def description(self):
        """Retourne la description, attribut de self.objet"""
        attribut = getattr(self.objet, self.nom_attribut)
        if isinstance(attribut, str):
            return self.description_complete

        return attribut

    def get_prompt(self):
        """Retourne le prompt."""
        description = self.description
        personnage = self.pere.joueur
        options = importeur.interpreteur.options
        if not options.a_option(personnage, OPT_AUTONL) and not \
                description.saut_de_ligne:
            return "-? "
        else:
            return "-> "

    @staticmethod
    def afficher_apercu(apercu, objet, valeur):
        if valeur is None:
            return ""

        if isinstance(valeur, str):
            description = Desc(parent=objet, scriptable=False)

            for paragraphe in valeur.split("\n"):
                description.ajouter_paragraphe(
                        paragraphe.replace("|nl|", " "))

            valeur = description

        valeur = valeur.paragraphes_indentes
        return apercu.format(objet=objet, valeur=valeur)

    def mettre_a_jour(self):
        """Met à jour l'attribut si nécessaire."""
        attribut = getattr(self.objet, self.nom_attribut)
        if isinstance(attribut, str):
            contenu = self.description_complete.affichage_simple("|nl|")
            setattr(self.objet, self.nom_attribut, contenu)

    def accueil(self):
        """Retourne l'aide"""
        description = self.description

        # Message d'aide
        msg = self.aide_courte.format(objet = self.objet) + "\n"
        msg += "Entrez une |cmd|phrase|ff| à ajouter à la description " \
                "ou |ent|/|ff| pour revenir à la\nfenêtre mère.\n" \
                "Symboles :\n" \
                " - |ent||tab||ff| : symbolise une tabulation\n" \
                "Options :\n" \
                " - |ent|/?|ff| pour obtenir la liste complète " \
                "des options disponibles\n" \
                " - |ent|/d <numéro>/*|ff| : supprime un paragraphe ou " \
                "toute la description\n" \
                " - |ent|/r <texte 1> / <texte 2>|ff| : remplace " \
                "|cmd|texte 1|ff| par |cmd|texte 2|ff|\n" \
                "Pour ajouter un paragraphe, entrez-le tout simplement.\n\n" \
                "Description existante :\n"

        if len(description.paragraphes) > 0:
            no_ligne = 1
            for paragraphe in description.paragraphes:
                paragraphe = description.wrap_paragraphe(paragraphe,
                        aff_sp_cars=True)
                paragraphe = paragraphe.replace("\n", "\n   ")
                msg += "\n{: 2} {}".format(no_ligne, paragraphe)
                no_ligne += 1
        else:
            msg += "\n Aucune description."

        return msg

    def opt_aide(self, arguments):
        """Affiche la liste des options.

        Syntaxe :
          /?

        """
        msg = \
            "Liste des options disponibles :\n" \
            " - |ent|/d *|ff| pour supprimer toute la description\n" \
            " - |ent|/d <numéro du paragraphe>|ff| pour supprimer un " \
            "paragraphe\n" \
            " - |ent|/a <numéro du paragraphe> <texte>|ff| pour ajouter " \
            "du texte\n   à la fin du paragraphe spécifié\n" \
            " - |ent|/j <numéro du paragraphe> <texte>|ff| pour insérer " \
            "le\n   paragraphe avant celui spécifié\n" \
            " - |/r <texte 1> / <texte 2>|ff| pour remplacer " \
            "|ent|texte 1|ff| par |ent|texte 2|ff|\n" \
            " - |ent|/t|ff| pour ajouter ou retirer les tabulations"

        if self.description.scriptable:
            msg += \
                "\nScripting de description :\n" \
                " - |ent|/e (description dynamique)|ff| pour éditer un " \
                "élément de\n   description dynamique. Sans arguments, " \
                "affiche les descriptions dynamiques\n   existantes\n" \
                " - |ent|/de <description dynamique>|ff| supprime la " \
                "description dynamique\n" \
                " - |ent|/re <ancien nom> <nouveau nom>|ff| renomme la " \
                "description dynamique"

        self.pere << msg

    def opt_ajouter_paragraphe(self, arguments):
        """Ajoute un paragraphe.

        Syntaxe :
          /j <numéro> <texte>

        """
        description = self.description
        paragraphes = description.paragraphes
        arguments = arguments.split(" ")
        if len(arguments) < 2:
            self.pere << "|err|Syntaxe invalide :|ent|/a <numéro du " \
                    "paragraphe> <texte à insérer>|ff|"
            return

        numero = arguments[0]
        texte = " ".join(arguments[1:])

        # Conversion du numéro de paragraphe
        try:
            numero = int(numero)
            assert 1 <= numero <= len(paragraphes)
        except (ValueError, AssertionError):
            self.pere << "|err|Le numéro précisé est invalide.|ff|"
            return

        description.paragraphes.insert(numero - 1, texte)
        self.mettre_a_jour()
        self.actualiser()

    def opt_inserer_paragraphe(self, arguments):
        """Insère du texte à la fin d'un paragraphe.

        Syntaxe :
          /a <numéro> <texte>

        """
        description = self.description
        paragraphes = description.paragraphes
        arguments = arguments.split(" ")
        if len(arguments) < 2:
            self.pere << "|err|Syntaxe invalide :|ent|/a <numéro du " \
                    "paragraphe> <texte à insérer>|ff|"
            return

        numero = arguments[0]
        texte = " ".join(arguments[1:])

        # Conversion du numéro de paragraphe
        try:
            numero = int(numero)
            assert 1 <= numero <= len(paragraphes)
        except (ValueError, AssertionError):
            self.pere << "|err|Le numéro précisé est invalide.|ff|"
            return

        paragraphe = paragraphes[numero - 1]
        if not paragraphe.endswith(" "):
            paragraphe += " "
        paragraphe += texte
        description.paragraphes[numero - 1] = paragraphe
        self.mettre_a_jour()
        self.actualiser()

    def opt_supprimer(self, arguments):
        """Fonction appelé quand on souhaite supprimer un morceau de la
        description
        Les arguments peuvent être :
        *   le signe '*' pour supprimer toute la description
        *   un nombre pour supprimer le paragraphe n°<nombre>

        """
        description = self.description
        if arguments == "*": # on supprime toute la description
            description.vider()
            self.mettre_a_jour()
            self.actualiser()
        else:
            # Ce doit être un nombre
            try:
                no = int(arguments) - 1
                assert no >= 0 and no < len(description.paragraphes)
            except ValueError:
                self.pere << "|err|Numéro de ligne invalide.|ff|"
            except AssertionError:
                self.pere << "|err|Numéro de ligne inexistant.|ff|"
            else:
                description.supprimer_paragraphe(no)
                self.mettre_a_jour()
                self.actualiser()

    def opt_remplacer(self, arguments):
        """Fonction appelé pour remplacer du texte dans la description.

        La syntaxe de remplacement est :
        <texte 1> / <texte à remplacer>

        """
        description = self.description

        # On commence par split au niveau du pipe
        try:
            recherche, remplacer_par = arguments.split(" / ")
        except ValueError:
            self.pere << "|err|Syntaxe invalide.|ff|"
        else:
            description.remplacer(recherche, remplacer_par)
            self.mettre_a_jour()
            self.actualiser()

    def opt_tabulations(self, arguments):
        """Ajoute ou retire les tabulations d'une description.

        Syntaxe :
            /t

        """
        description = self.description

        # On parcourt tous les paragraphes
        for i, paragraphe in enumerate(description.paragraphes):
            if paragraphe.startswith("|tab|"):
                paragraphe = paragraphe[5:]
            else:
                paragraphe = "|tab|" + paragraphe
            description.paragraphes[i] = paragraphe

        self.mettre_a_jour()
        self.actualiser()

    def opt_editer_evt(self, arguments):
        """Edite ou affiche les éléments de la description."""
        description = self.description
        if not description.scriptable:
            self.pere << "|err|Option inconnue.|ff|"
            return

        evenements = description.script["regarde"].evenements
        evt = supprimer_accents(arguments).strip()
        if not evt:
            msg = \
                "Ci-dessous se trouve la liste des éléments observables " \
                "dans cette description :\n"
            for nom in sorted(evenements.keys()):
                msg += "\n  {}".format(nom)
            if not evenements:
                msg += "\n  |att|Aucun|ff|"
            self.pere << msg
        else:
            if evt in evenements.keys():
                evenement = evenements[evt]
            else:
                evenement = description.script["regarde"].creer_evenement(evt)
                description.script.init()
                evenement.creer_sinon()
            enveloppe = EnveloppeObjet(EdtInstructions, evenement.sinon)
            enveloppe.parent = self
            contexte = enveloppe.construire(self.pere.joueur)

            self.migrer_contexte(contexte)

    def opt_supprimer_evt(self, arguments):
        """Supprime un évènement de la description dynamique.

        Syntaxe :
          /de <nom>

        """
        description = self.description
        if not description.scriptable:
            self.pere << "|err|Option inconnue.|ff|"
            return

        regarde = description.script["regarde"]
        evt = supprimer_accents(arguments).strip()
        if evt in regarde.evenements:
            regarde.supprimer_evenement(evt)
            self.actualiser()
        else:
            self.pere << "|err|Évènement inconnu : {}.|ff|".format(
                    repr(evt))

    def opt_renommer_evt(self, arguments):
        """Renomme un évènement de la description dynamique.

        Syntaxe :
          /de <nom> <nouveau nom>

        """
        description = self.description
        if not description.scriptable:
            self.pere << "|err|Option inconnue.|ff|"
            return

        regarde = description.script["regarde"]
        try:
            ancien, nouveau = arguments.split(" ")
        except ValueError:
            self.pere << "|err|Entrez l'ancien nom, un espace et le " \
                    "nouveau nom de variable.|ff|"
            return

        ancien = supprimer_accents(ancien).strip()
        if ancien in regarde.evenements:
            regarde.renommer_evenement(ancien, nouveau)
            self.actualiser()
        else:
            self.pere << "|err|Évènement inconnu : {}.|ff|".format(
                    repr(ancien))

    def opt_editer_options(self, arguments):
        """Fonction appelé pour consulter et éditer ses options.

        La syntaxe est :
            /o pour consulter ses options
            /o nom pour modifier ses options

        """
        arguments = arguments.strip()
        personnage = self.pere.joueur
        if arguments:
            options = importeur.interpreteur.options
            try:
                nombre = options.get_nombre_option(arguments)
            except ValueError:
                self.pere << "|err|Ce nom d'option est inconnu.|ff|"
            else:
                options.changer_option(personnage, nombre)
                self.pere << "Vos options ont bien été modifiées."
        else:
            # Affichage des options
            options = importeur.interpreteur.options.afficher_options(
                    personnage)
            self.pere << "Vos options actuelles :\n\n  " + "\n  ".join(
                    options)

    def interpreter(self, msg):
        """Interprétation du contexte"""
        description = self.description
        personnage = self.pere.joueur
        options = importeur.interpreteur.options
        if not options.a_option(personnage, OPT_AUTONL):
            if msg and not description.saut_de_ligne:
                if description.paragraphes:
                    paragraphe = description.paragraphes[-1]
                    msg = paragraphe + " " + msg
                    description.supprimer_paragraphe(-1)
            elif not msg:
                description.saut_de_ligne = True
                self.mettre_a_jour()
                self.actualiser()
                return
        elif options.a_option(personnage, OPT_AUTOTAB) and msg:
            msg = "|tab|" + msg
        description.ajouter_paragraphe(msg)
        description.saut_de_ligne = False
        self.mettre_a_jour()
        self.actualiser()