19 Exemples
Shadow15510 edited this page 2022-02-27 13:43:07 +01:00

Exemples d'utilisation

Exemple de manipulations basique

Quelques réflexions préliminaires sur les mécaniques de notre petit jeu :

  • des points de vie parce que c'est obligatoire
  • de l'argent

Nous allons commencer par créer une carte simple.

exterieur = (r"""
 _         ###
/o\__     #####
|_ <>\     ###  
|^|__|     /_\
 
   


|==|==|==|==|==|==|==|""",
# Points de passage
(1, 3, 1, 3, 5))

interieur = (r"""
+----------+
|          |
|          |
|          |
|          |
+-|^|------+
""",
# Points de passage
(3, 5, 0, 1, 3))


carte_monde = (exterieur, interieur)

entites = (
    ["sdf", "*", 0, 2, 5, "stand by"],
)

Nous avons un PnJ, immobile pour l'instant, aux coordonnées (2, 5) sur la carte 0, et pas d'adversaire, il nous reste à faire les dialogues avec cet unique PnJ, la fonction de combat ne sera pas traitée ici vu qu'il n'y a pas d'ennemi.

Comme il n'y a pas grand chose à faire, je vais en profiter pour vous montrer les dialogues, les statistiques et les changements d'animation de l'entité.

def pnj(data, stat, entites, identifiant):
    xp = data[0]["main"]

    if identifiant == "sdf":
        if xp in (2, 7): entites["sdf"].change_behavior("follow") # Si le joueur donne de l'argent, le PnJ suit le joueur
        elif xp == 4: entites["sdf"].change_behavior("stand by") # Si le joueur a demandé à ne plus être suivi
        elif xp == 6: # Si le joueur demande au PnJ de l'attendre à l'intérieur
            entites["sdf"].change_behavior("stand by")
            entites["sdf"].teleport(1, 2, 2)

        return {
            0: [0, "Mon bon monsieur, vous n'auriez pas quelques sous pour moi ?\n1. He non mon brave...\n2. Mais si, bien sur, tenez.", 2],
                1: [9, "Radin !"],
                2: [1, "Merci !", 0, (1, -1)], # 0 réponse possibles, -1 Argent

            3: [0, "Hmm ?\n1.Arretez de me suivre !\n2.Non rien.\n3.Attendez-moi a l'intérieur, j'en ai pour une minute.", 3],
                4: [2, "Soit..."],
                5: [-2, "Bien."],
                6: [1, "Soit."],
            7: [-4, "Je vous suis !"],

            "base": [0, "Hmm ?"]
        }

    return [0, "Hmm ?"] # Ce cas n'est jamais appelé ici car tous les cas sont prévus au-dessus

Pour les statistiques, nous avons des points de vie et de l'argent, on va bricoler un truc simple :

def affichage_stat(data stat):
    pv, argent = stat
    print("Statistiques")
    print("PV : {}".format(pv))
    print("Argent : {}".format(argent))
    input()

Il ne reste plus qu'à construire nos dictionnaires d'évènements et de touches puis à tout donner au moteur ! La limite d'XP de la partie est fixée à 10, on commence la partie avec 100 points de vie et 5 Argent, aux coordonnées (10; 3).

evenements = {"*": pnj}
touches = {6: affichage_stat}
def mon_jeu():
    rpg_python = Asci(carte_monde, entites, evenements, touches)
    rpg_python.mainloop(10, [100, 5], [{"main": 0}, 0, 10, 3])

Et voila ! N'oubliez pas d'importer asci_lib ! Pour ceux qui veulent tester, le code complet est dans le fichier samples/sample_1.py

Autre exemple basique

Nous allons reprendre la même carte que tout à l'heure, mais nous allons ajouter des PnJ à l'intérieur de la maison, la porte est toujours aux coordonnées (1, 3) :

 _         ###
/o\__     #####
|_ <>\     ###  
|^|__|     /_\

  ?


|==|==|==|==|==|==|==|

Il faut donc dessiner l'intérieur de la maison :

+--+--+--------+--+--+
|  |  |  ?     |  | ?|
|  +  +        +  +  |
|                    |
|  +  +        +  +  |
+--/  \--------/  \--+
|                    |
+---|^|--------------+

La porte à l'intérieur est aux coordonnées (5, 7). Nous avons donc le tuple des cartes, après avoir remplacé les entités par leurs tuples :

cartes = (
(r"""
 _         ###
/o\__     #####
|_ <>\     ###  
|^|__|     /_\

   


|==|==|==|==|==|==|==|""",
(1, 3, 1, 5, 7)),

(r"""
+--+--+--------+--+--+
|  |  |        |  |  |
|  +  +        +  +  |
|                    |
|  +  +        +  +  |
+--/  \--------/  \--+
|                    |
+---|^|--------------+""",
(5, 7, 0, 1, 3))
)

entites = (
    [1, "*", 0, 2, 5, "stand by"], # on peut aussi mettre des nombres en guise d'identifiant
    ["habitant", "?", 1, 9, 1, "stand by"],
    ["voleur", "?", 1, 20, 1, "stand by"]
)

Pour les évènements, rien de nouveau, étant donné que nous n'avons a priori pas de statistiques à part la vie, il n'y a pas forcément grand chose à faire, vous pouvez vous amuser à faire des dialogues un peu complexes avec différentes fins.

def pnj(data, stat, entites, identifiant):
    carte_actuelle = data[1]
    
    if carte_actuelle == 0:
        if identifiant == 1: return {
            0: [0, "Hey ! J'ai entendu du bruit dans la maison, mais je n'ose pas rentrer... 1. Rien entendu. 2. Je vais jeter un oeil.", 2],
                1: [3, "Etes-vous sourd ?"],
                2: [1, "J'etais sur que vous m'ecouteriez !"],

            3: [2, "C'est la maison juste au nord. Soyez prudent !"],
            4: [0, "Enfin, vous entendez bien du bruit la ? Et si c'etait un voleur ? 1. Bon ok j'y vais. 2. Mais foutez moi la paix !", 2],
                6: [0, "..."],

            5: [0, "Alors ?"],

            10: [1, "J'etais sur d'avoir entendu un truc !"],
            "base": [0, "Vous avez entendu quelque chose ?"]
            }

    elif carte_actuelle == 1:
        if identifiant == "habitant": return {
            5: [0, "Je crois que le voleur est dans la piece d'a cote... 1. Je vais regarder. 2. Debrouillez-vous !", 2],
                6: [2, "Merci !"],
                7: [0, "Pleutre ! Hors de ma vue !"],

            9: [1, "Ah, merci !"],
            "base": [0, "J'ai peur de sortir de cette piece"]
            }

        elif identifiant == "voleur": return {
                8: [1, "Ciel, je suis fait !"],
                9: [0, "Je pars, je pars !"],
                "base": [0, "File avant que je ne te detrousse !"]
            }

    return [0, "Hmm ?"]

De même que pour l'exemple précédent, il ne reste les dictionnaires et la fonction finale, cette fois-ci le joueur sera symbolisé par le symbole $, et les PnJ par les symboles ? ou *.

evenements = {"?*": pnj}
touches = {8: affichage_stat}

def mon_jeu():
    rpg_python = Asci(cartes, entites, evenements, touche)
    rpg_python.mainloop(11, [100], [{"main": 0}, 0, 10, 3], player="$")

Le fichier complet est disponible sous le nom samples/sample_2.py

Exemple de manipulation avancées

Maintenant que vous êtes bien chaud, on va commencer les choses sérieuses. Enfin "sérieuses" je vous propose de faire pas à pas un petit jeu avec disons une quête et quelques mécaniques un peu bidon (ça reste un exemple). On peut déjà poser quelques petits trucs :

  • le joueur a des points de vie, d'attaque et de défense
  • le scénario se résumera à une seule quête : tuer un adversaire. La quête peut être refusée.

On peut déjà rapidement voir à quoi va ressembler l'arbre d'XP (j'en fait pas mal et je vous invite à en faire aussi, ça aide vraiment à voir le déroulement de l'histoire) :

    +--1--3--4--5-
-0--+        
    +--2-X

Pour expliquer rapidement :

  • 0 : début de l'histoire, énoncé de la quête
  • 1 : quête acceptée
  • 2 : quête refusée
  • 3 : combat avec le bandit
  • 4 : debrief avec le PnJ
  • 5 : fin de la partie (limite de l'XP pour le jeu)

Comme on est vraiment chaud, on va essayer de faire une map un peu plus joli que d'habitude avec des PnJ qui vont avoir des dialogues sans forcément avoir un rôle dans l'histoire, ça va être l'occasion de sortir un peu du modèle "1 PnJ = 1 dictionnaire d'évènements" et de vous rappeller que vous pouvez renvoyer un évènement seul.

Allez ! On est parti avec une map et deux maisons :

cartes = (
(r"""
 __                      
/  \___    ###           
|<>    \  #####    _     
|^|____|   ###    / \    
           /_\    |^|    
                         
             __     __ 
        ##  /  \___/  \  ##   
       #### |<>     <>| ####
        ##  |_________|  ##
        ||               ||""",
(1, 3, 1, 5, 7),
(19, 4, 2, 4, 4)),

(r"""
+--+--+--------+--+--+
|  |  |        |  |  |
|  +  +        +  +  |
|                    |
|  +  +        +  +  |
+--/  \--------/  \--+
|                    |
+---|^|--------------+""",
(5, 7, 0, 1, 3)),

(r"""
+-------+
|       |
|       |
|       |
+--|^|--+
""",
(4, 4, 0, 19, 4))
)

entites = (
    ["medecin", "*", 0, 24, 4, "stand by"],
    ["ami", "*", 0, 16, 1, "stand by"],
    ["bandit", "$", 0, 4, 7, "walk", 0, ((4, 7), (3, 7), (3, 6), (4, 6))],
    [0, "*", 1, 5, 5, "stand by"]
)

On va commencer par faire le PnJ qui n'a pas un rôle énorme dans le scénario. On va faire un médecin (comme y a du combat, c'est pratique de mettre un medecin dans la place). L'idée est simple : si le joueur a moins de 100 points de vie, il est blessé, donc on lui rajoute des points de vie avec un texte. Si il est en pleine forme, on le lui signale et on ne touche pas aux points de vie.

On a donc la fonction qui ressemble à ça (je met des ... au niveau des trucs pas encore définis):

def pnj(data, stat, entites, identifiant):
    carte_actuelle = data[1]
    xp = data[0]["main"]

    if carte_actuelle == 0:
        if identifiant == "medecin":
            if stat[0] < 100: return [0, "Oh, mais tu es blesse !", 0, (0, 50)]
            else: return [0, "Reviens me voir quand tu seras blesse."]
    return [0, "Hmm ?"]

Comme il s'agit d'un personnage très secondaire (voir inutile) on peut prendre pour médecin le PnJ tout à l'est, aux coordonnées (24, 4). On prend alors l'autre PnJ pour le personnage qui donne la quête au joueur (aux coordonnées (16, 1))

On peut compléter notre fonction avec la proposition de la quête :

def pnj(data, stat, entites, identifiant):
    carte_actuelle = data[1]
    xp = data[0]["main"]

    if carte_actuelle == 0:
        if identifiant == "medecin":
            if stat[0] < 100: return [0, "Oh, mais tu es blesse !", 0, (0, 50)]
            else: return [0, "Reviens me voir quand tu seras blesse."]

        elif identifiant == "ami": return {
            "base": [0, "Alors ? T'en sorts-tu ?"],

            0: [0, "J'ai une quete pour toi ! Un ami a moi a des problemes : un personnage louche traine autour de sa maison... Si tu pouvais l'en debarasser, il t'en serai reconnaissant. 1. Je m'en charge ! 2. Trouve quelqu'un d'autre.", 2],
                1: [2, "J'etais sur que je pouvais compter sur toi ! Tiens, voila une dague et une petit bouclier.", 0, (1, 10), (2, 10)],
                2: [3, "Si un jour tu as besoin de moi, tu seras sympa de m'oublier."],

            3: [0, "Alors ? Il est mort ce bandit ?"],
            4: [1, "Merci, tu as rendu un grand service a mon ami !"]
        }

    return [0, "Hmm ?"]

Maintenant il reste à programmer les évènements des ennemis. Le bandit est sur les coordonnées (4, 7).

def ennemi(data, stat, entites, identifiant):
    carte_actuelle = data[1]
    coords = data[2], data[3]
    xp = data[0]["main"]

    if carte_actuelle == 0:
        if identifiant == "bandit":
            # Bandit vivant
            if xp == 3:
                if combat(stat, [75, randint(5, 10), randint(5, 10)]):
                    return [1, "Vous avez reussi la quete !"]
            elif xp < 3: return [0, "Qu'est-ce tu regardes toi ? Casses-toi !"]
            else: return [0, "Vous regardez le cadavre froid du bandit."]

Une fonction pour les combats maintenant !

def combats(stat, ennemi_stat):
    defense_temporaire = defense_temporaire_ennemi = 0
    
    while stat[0] > 0 and ennemi_stat[0] > 0:

        print("Vos PV : {0}\nPV ennemi : {1}".format(stat[0], ennemi_stat[0]))
        print("<*> Actions <*>")
        print("1. Attaquer")
        print("2. Defendre")

        action = int(input(">"))

        defense_temporaire = 0
        if action == 1:
            pv = (stat[1] - ennemi_stat[2] - defense_temporaire_ennemi) + randint(-5, 10)
            if pv < 0: pv = 0
            ennemi_stat[0] -= pv
        elif action == 2:
            defense_temporaire = randint(1, 5)

        defense_temporaire_ennemi = 0
        if randint(1, 2) == 1:
            pv = (ennemi_stat[1] - stat[2] - defense_temporaire) + randint(-5, 10)
            if pv < 0: pv = 0
            stat[0] -= pv
        else:
            defense_temporaire_ennemi = randint(1, 5)

    return stat[0] > 0

Il reste à faire la fonction d'affichage des statistiques et la petite fonction d'appel :

def affichage_stat(data, stat):
    pv, pa, pd = stat

    print("<*> Statistiques <*>")
    print("Points de vie .: {}".format(pv))
    print("Points attaque : {}".format(pa))
    print("Points defense : {}".format(pd))
    input()

et :

evenements = {"*": pnj, "$": ennemi}
touche = {7: affichage_stat}

def mon_jeu(stat=[100, 0, 0], data=[{"main": 0}, 0, 10, 3]):
    rpg_python = Asci(cartes, entites, evenements, touche)
    stat, data = rpg_python.mainloop(5, stat, data=data)
    print("Pour reprendre :")
    print("mon_jeu({}, {})".format(stat, data))

La fonction est légèrement différente de celle vues précédemment, les arguments permettent de reprendre la partie en cours. Vous pouvez retrouver le fichier complet dans samples/sample_3.py

Un exemple des quêtes annexes

Un exemple avec des quêtes annexes est dans samples/sample_4.py. Cet exemple est commenté, mais non détaillé ici : les manipulations effectuées ne sont pas plus complexes que celles déjà vues ici.