Annonce

Réduire

Charte du Forum

Sur ces forums, il est possible d'aborder n'importe quels sujets, de façon sérieuse ou tout simplement pour délirer !

Afin de préserver son harmonie et éviter les débordements, nous avons établi un ensemble de règles très simple que nous vous demandons de respecter.

1) Les thèmes suivants ne doivent jamais être abordés: La politique, La religion, La haine raciale, La pédophilie. Faites appel à votre bon sens pour éviter tout autre sujet susceptible de provoquer une polémique trop violente.

2) Ce forum est destiné a l'Abandonware (jeux a l'abandon). Les discussions relatives au Warez (requêtes, liens ...) seront systématiquement fermées ou effacées.

3) Lorsque vous lancez un sujet, veillez a ce qu'il soit dans le forum approprié (ne faites pas par exemple une requête dans le forum Discussion générale).

4) Avant de poser une question d'ordre technique, assurez vous d'avoir consulté la FAQ Abandonware-France et la FAQ Abandonware-Forums (questions fréquemment posées) !

5) Ne publiez pas d'images dans vos messages qui pourraient choquer les autres visiteurs.

6) Nous détestons le SPAM et le Flood ! Évitez donc de poster des messages inutiles et de façon répétée.

7) Le forum n'est pas un concours de posts. Il est inutile de poster des messages dans le simple but de monter en grade (Le nb de posts sera décrémenté en cas d'abus).

8) Les sujets privés destinés a une minorité sont interdits. Le forum est une communauté et vos messages sont destinés a l'ensemble des visiteurs. Nous mettons a votre disposition gratuitement des outils tels que Chat ou Messagerie privée.

9) Il est souhaitable de ne pas faire dévier un sujet. Cela n'est pas très sympa pour celui qui a lancé le sujet !

10) Peace and Love ! Les forums sont un endroit de détente, amusez vous, ne vous prenez pas la tête inutilement en public.

11) Les admins et modérateurs sont la pour vous protéger, et non pour vous faire la guerre ! Ils se réservent le droit de déplacer, modifier, fermer ou effacer un sujet en cas de besoin.

12) Concernant la zone arcade sur le forum, toute personne trichant se verra temporairement bannie du forum et TOUT ses scores seront purement et simplement effacés.

13) Ne proposez pas de lien vers du contenu illégal et n'encouragez pas au piratage d'oeuvres protégés par les droits d'auteurs.

14) Ce forum n'est pas un téléphone portable ! Corollaire à la proposition précédente: la plupart des gens susceptible de vous répondre n'a pas appris à lire le langage "texto/SMS". Vos messages ne sont pas limités à 160 caractères, alors s'il vous plait, écrivez correctement ! Clairement, on ne va pas vous tomber dessus pour un "s" oublié ou pour un accord incorrect, mais pas de "g chrché c je pandan dé mwa"! Copiez-collez votre message dans Word pour profiter du correcteur orthographique, au besoin.

Ces règles sont très importantes, merci de les respecter ! En cas de non respect, vous pourrez recevoir un avertissement, ou en fonction de la faute, être banni temporairement, voir définitivement du forum.
Voir plus
Voir moins

Ultima VI - les codes sources régénérés

Réduire
Ceci est une discussion importante.
X
X
  • Filtre
  • Heure
  • Afficher
Tout nettoyer
nouveaux messages

  • Ultima VI - les codes sources régénérés

    Billet #01

    Ultima VI fait partie de ces jeux tellement classiques qu'ils ont été analysés sous toutes les coutures, et de pratiquement toutes les manieres possibles.
    On peut trouver des dizaines de sites repertoriant une foule d'information sur:
    - Le jeu lui-même; les personnages, l'histoire, la solution, les inconsistances, ...
    - Ses aspects techniques; bugs, hacks, cheat, format des images, explications des fichiers, ...
    - Il y a aussi des remakes; Nuvie, Project Ultima 6, ...

    Mais jusqu'à présent, personne n'a, à ce qu'il me semble, cherché à décompiler le code du jeu.
    C'est le défi que je me suis lancé il y a maintenant plusieurs mois, et autant vous dire que j'ai bien avancé.

    Mettons-nous tout d'abord d'accord sur le terme "décompiler".
    Et si vous ne savez pas ce qu'est un compilateur, je vous conseille d'arrêter ici la lecture, parce que ca ne va pas être joli à voir.

    Mettons que vous avez un fichier source "toto.c" qui se compile en "toto.exe". Et bien décompiler "toto.exe", ça voudra dire créer un fichier "bobo.c" qui génère à la compilation un fichier identique à "toto.exe".
    Bien entendu, vous perdez en route beaucoup d'informations de "toto.c"; les noms des variables, fonctions, les commentaires, les macros, et j'en passe.
    Mais au moins, vous avez devant vous les algorithmes originaux, et partant de la, [...]

    Prêts a soulever le capot ?

    Ultima VI, dans sa version MSDOS, est constitué de plusieurs fichiers ".exe" et d'une multitude de ressources (images, musiques, dialogues, ...).
    Pour ce qui est des ressources, internet regorge déjà d'informations tres intéressantes (ex: http://ultima.wikia.com/wiki/Ultima_VI_Internal_Formats).

    Intéressons-nous d'abord au moteur du jeu, "GAME.EXE".

    "GAME.EXE" a été compilé avec Turbo C version 2.0 (de la maintenant défunte compagnie Borland).
    Et attention, ce n'est pas la version 2.1 (celle que l'on trouve le plus facilement sur le web).
    Pourquoi c'est important ?
    Parce que d'une part les compilateurs générent pour la même source du code différent,
    et d'autre part, les fonctions de la librairie C diffèrent également.
    Ce qui pose un problème quand on veut régénérer un exe à l'identique.

    Toutes les fonctions C (à une ou deux exceptions près) sont compilées avec la convention d'appel PASCAL.
    Et là vous vous dites: mais de quoi est-ce qu'il parle ?
    Je sais, ça ne tient pas debout, dit comme ca, mais c'est juste pour la précision.
    En fait, le choix de la convention d'appel n'a aucune incidence sur la programmation.
    Borland, dans son manuel, indique que les fonctions compilées ainsi permettent de réaliser un gain d'espace dans l'exe et un gain de temps lors de l'appel.
    Il y a juste une petite limitation qui empêche d'utiliser une specificité du langage C, le nombre variable d'arguments.
    Enfin pour le coup si ça vous intéresse, je vous conseille d'aller faire une petite recherche sur le web, ça y sera beaucoup mieux expliqué.

    Le modèle de mémoire utilisé est medium; c'est-à-dire que les fonctions sont appelées par défaut avec la convention FAR, et les données sont dans un seul segment.

    Certaines routines (tres peu je vous rassure) semblent ne pas avoir été écrites en C, j'ai donc supposé qu'elles étaient à l'origine en assembleur.
    Par rapport à l'époque, MASM version 3.0 m'a paru un bon choix.

    Pour le désassemblage j'ai utilisé ce bon vieux "debug" sous l'irremplaçable DosBox.
    Dans ma configuration, le segment 0 de l'exe (celui qui commence juste après l'en-tête) est chargé dans la memoire en 0838:0000.
    C'est l'adresse qui m'a servi de base pour nommer les fonctions;

    Les noms de fonctions (non encore renommées "humainement") utilisent le format:
    "C_" + SEGMENT + "_" + OFFSET
    et les noms de variables:
    "D_" + OFFSET



    Les données (par opposition au code) générées dans le modèle medium peuvent être réparties en deux grands groupes:
    les données initialisées (ex: "int toto = 3;" hors du corps d'une fonction):
    le compilateur les place, en fonction de l'ordre de leur apparition dans le source ".c", dans le segment _DATA

    les données non-initialisées (ex: "int bobo;" hors du corps d'une fonction):
    elles vont dans le segment _BSS;
    L'ordre dépend cette fois du nom de la variable; alors ordre alphabetique, hash, ..., je n'ai pas encore cherché à percer le secret.

    Pour ne pas me prendre la tête avec cette histoire d'ordre, j'ai décidé de sortir toutes ces variables des fichiers ".c" et de les réunir dans un seul écrit assembleur (histoire de pouvoir imposer l'ordre).
    Ca fait partie des TODO.

    enfin, à part quelques exceptions pour les longues listes de constantes, j'ai décidé de ne créer qu'un seul fichier en-tête regroupant toutes les macros et définitions externes: u6.h
    Ca fait partie des TODO aussi.


    Quelques sources d'informations:
    http://www.ultimaaiera.com/blog/ulti...cal-documents/
    Un ancien programmeur de Origin System a publié quelques documents de conceptions du jeu; ça m'a permis de retrouver le nom de certaines fonctions, ou données, ainsi que de comprendre certains aspects du code.

    http://nuvie.sourceforge.net/phorum/...p?1,113,page=1
    Ca c'est sur un forum, un autre programmeur a publié d'autres documents.

    http://bitsavers.informatik.uni-stut...rland/turbo_c/
    On peut y télécharger le manuel de programmation de la version 1.5.
    Ce n'est pas la version qui nous intéresse, mais certains concepts importants (et valables dans la version 2.0) y sont assez bien expliqués.
    Dernière modification par ergonomy_joe, 18-07-2013, 12h20. Motif: (corrections des URLs)

  • #2
    C'est un projet très ambitieux, et qui m'apparaît comme terriblement complexe.

    J'ai une question : comment fais-tu pour retrouver les fonctions dans ce que tu décompiles ?

    Bonne chance en tous les cas

    Commentaire


    • #3
      Question un peu bête mais a quoi ça va servir de dé-compiler le code du jeu ? A le patcher ?
      "Les ordinateurs sont inutiles : ils ne donnent que des réponses."
      - Pablo Picasso

      "Deux intellectuels assis vont moins loin qu'une brute qui marche."
      - Michel Audiard

      Commentaire


      • #4
        @Ian

        Dans le cas d'un processeur 8086, retrouver (dans le sens reperer je suppose) les fonctions dans le codes desassembles est assez facile a partir du moment ou tu connais le compilateur original et le modele memoire utilise.
        Dans notre cas, leur en-tete ressemble a:
        PUSH BP
        MOV BP,SP

        S'il y a des variables locales
        SUB SP,x ;x = taille des variables locales

        Et eventuellement, si les registres SI et DI sont utilises:
        PUSH SI
        PUSH DI

        De la meme maniere, la fin de la fonction:

        si les registres SI et DI ont ete utilises:
        POP DI
        POP SI

        puis:
        POP BP
        RETF x ;x = taille des arguments dans le cas des fonctions PASCAL

        J'espere ne pas avoir repondu a cote de la plaque ? :-/
        Si la question etait "comment passer du code assembleur au langage C" j'essairai de faire un petit topo dans l'avenir.

        @Azur

        Mon but premier n'est pas de patcher le jeu (a la rigeur le recompiler pour une autre plateforme pourquoi pas).
        Je voyais plus un aspect "documentaire" dans ce travail:
        .Voir comment on programmait chez Origin Systems
        .Mettre la main sur des erreurs
        .Trouver des fonctions cachees
        .Retrouver les algorithmes originaux
        .etc
        D'autres approches permettent d'arriver au meme resultat, mais comme je l'ai dit dans mon intro, l'approche de la decompilation n'a pas jamais encore ete tentee, alors ...

        Et puis comme passe-temps, ca fait travailler les meninges.

        Commentaire


        • #5
          @Azur

          Mon but premier n'est pas de patcher le jeu (a la rigeur le recompiler pour une autre plateforme pourquoi pas).
          Je voyais plus un aspect "documentaire" dans ce travail:
          .Voir comment on programmait chez Origin Systems
          .Mettre la main sur des erreurs
          .Trouver des fonctions cachees
          .Retrouver les algorithmes originaux
          .etc
          D'autres approches permettent d'arriver au meme resultat, mais comme je l'ai dit dans mon intro, l'approche de la decompilation n'a pas jamais encore ete tentee, alors ...

          Et puis comme passe-temps, ca fait travailler les meninges.
          Oui comme je te le disais ma question était "bête", quand tu apprécies un jeu / Une compagnie savoir les moindres détails sur celui-ci est important, en fait ce qui m'a toujours épaté dans les jeux "modernes" c'est le système de sauvegarde ou comment dans quelques ko référencer autant de données et les interpréter ensuite, le tour de force d'Ultima VI c'est aussi de faire tenir dans si peu d'espace un jeu aussi riche (ça vaut pour tous les JDR sortis à l'époque des disquettes), maintenant cet épisode n'est pas abandonware et je me souviens qu'un membre du forum qui ne passe plus maintenant (Dorian Gray), me disait que même abandonware dé-compiler un jeu n'était pas forcément autorisé par l'éditeur (il avait eu ce problème avec Woodruff de Sierra/Coktel), dans mon souvenir il avait pu créer des gif animé avec tous les jeux utilisant le même code que Woodruff par exemple (Gobliins etc...)

          Ensuite savoir comment on programmait chez Origin cela peu aider à mieux connaître les autres titres du studio

          Hop une des discussions d'époque : http://www.abandonware-forums.org/sh...een-Adibou-%29

          Dommage que son site n'existe plus parce qu'il donnait des tas d'outils fait maison pour la décompilation, je me souviens qu'il avait aussi participé à SCUMM pour y intégrer les jeux Coktel Vision, mais je vois qu'il n'est plus venu ici depuis 2006 mais à ma connaissance ça a été le premier (le seul ?) à avoir dé-compilé les jeux Coktel et en avoir tiré des applications sympas !
          Dernière modification par Azur, 14-07-2013, 04h05.
          "Les ordinateurs sont inutiles : ils ne donnent que des réponses."
          - Pablo Picasso

          "Deux intellectuels assis vont moins loin qu'une brute qui marche."
          - Michel Audiard

          Commentaire


          • #6
            Super post, on va enfin comprendre par quel "tour de magie" tu arrives à extraire les sources de ces vieux jeux.

            Envoyé par ergonomy_joe Voir le message
            Billet #01
            Dans ma configuration, le segment 0 de l'exe (celui qui commence juste après l'en-tête) est chargé dans la memoire en 0838:0000.
            C'est l'adresse qui m'a servi de base pour nommer les fonctions;

            Les noms de fonctions (non encore renommées "humainement") utilisent le format:
            "C_" + SEGMENT + "_" + OFFSET
            et les noms de variables:
            "D_" + OFFSET
            Donc de cette façon ça va te donner la fonction main je suppose, mais pour les autres fonctions tu les trouves comment ? C'est le debugger qui te les donne directement ?

            Envoyé par Azur Voir le message
            Question un peu bête mais a quoi ça va servir de dé-compiler le code du jeu ? A le patcher ?
            Ca permet aussi d'améliorer le jeu d'origine, c'est grâce au travail d'ergonomy_joe que j'ai pu faire le Maitre des âmes+

            Envoyé par ergonomy_joe Voir le message
            Et puis comme passe-temps, ca fait travailler les meninges.
            La suite, la suite !

            Commentaire


            • #7
              Félicitations pour tout le travail...et une autre question stupide Une fois recompilée, la version Ultima VI + sera t-elle téléchargeable comme un freeware?

              Commentaire


              • #8
                Bon courage dans ta décompilation du jeu, il est nettement plus "lourd" que LMDA ou les vieux jeux sur disquette 5"1/4 sur lesquels on a dû intervenir !

                Pour ce qui est du Borland C 2.0, je crois avoir ça sous la main (au fond de mon stock en fait) en disquettes d'installation. Si c'est le cas, je te "up" ça sur le serveur ce lundi soir

                Steph

                Commentaire


                • #9
                  @Azur

                  Oui, dommage qu'on n'ait plus de nouvelles de Dorian. Ca avait l'air interessant tout son travail.


                  @Karamoon

                  J'espere ne pas t'avoir induit en erreur, mais dans notre cas, meme si le cs:ip initial est bien a l'offset 0 (0838:0000 dans ma config), ce n'est pas la fonction main, mais le loader de la libc;
                  ce derniere appelle la fonction main apres avoir procede a quelques initialisations. Dans mon cas, le main est en fait en 0903:0C5E.


                  J'en profite pour ajouter une precision; GAME.EXE est compresse (a la EXEPACK) dans sa version originale, et toutes les informations que je donne ne sont valable que pour la version decompressee.

                  @Reggio52

                  merci
                  Par contre pas de version + de prevue dans l'immediat; l'objectif principal etant les codes sources.

                  @Stephh
                  Ok merci; en fait c'est plutot la documentation que je cherche, mais ca me permettra de voir si il manque des fichiers dans ma version.



                  @All
                  Merci a tous pour vos messages.

                  Alors pour info, la phase decompilation est terminee.
                  Mon GAME.EXE differe d'une dizaine d'octets avec l'original, pour une histoire de padding que je ne comprends pas trop et qui fera surement l'objet d'un post.
                  Reste a rendre ce code lisible maintenant...

                  mais voila que se profile a l'horizon le ....

                  Edit -------------------------------------

                  Billet #02

                  Si on omet le premier segment -- dans la logique Borland, il s'appelle _TEXT (0838:0000 dans ma config) --
                  et quelques autres segment speciaux, dans le modele medium:
                  un fichier .c = un segment

                  C'est d'ailleurs grace a ca qu'on arrive a reperer la separation entre les differents modules.

                  Bien, dressons-en la liste:

                  0903: contient la fonction main et quelques constantes; initialisation
                  0a33: boucle principale
                  0c9c: gestion de l'affichage du texte, de la sauvegarde
                  101c: gestion de la "carte" (fichier map)
                  1100: gestion de ... comment dire ... de cacher les parties de la carte qu'on ne peut pas voir (on dirait du Homer Simpson cette phrase)
                  1184: gestion des objets du jeu (c'est un des modules decrits dans les documents originaux de Origin System)
                  155d: affichage des differents informations personnage/equipement/...
                  16e1: initialisation d'une conversation (module tres court)
                  1703: moteur de conversation ("talkdr" d'apres un des messages d'erreur)
                  1944: gestion des sorts de magie (un gros morceau celui-la)
                  1e0f: moteur AI (NPCTracker d'apres les docs Origin)
                  2337: gestion des commandes d'attaque .. en gros, des combats.
                  27a1: commandes "look" "drop" "get" "move" "use" (un bon gros module aussi).
                  2e2d: generation des monstres (les docs font reference a des oeufs, "Egg", alors j'utilise aussi cette appellation)
                  2f1a: gestion musique et bruits ambiants
                  2fc1: initialization du driver graphique, quelques routines graphiques (comme la fonction "peer" du jeu) et curieusement, ballistique.
                  3200: fonction "Rest/Repair" du jeu
                  32c3: gestion du changement de disquette du jeu

                  On a ensuite quelques segments ecrits (d'apres moi) en assembleur.
                  Dans le desordre: delay, gestion souris, random, sound, ...
                  Puis les fonctions de la libc ecrites en C (celles en assembleurs sont linkes dans le segment _TEXT) commencent a 33e6:

                  Restent deux modules qui, bien que places juste avant le segment de donnees, ne contiennent que des constantes:
                  3522: donnees pour la generation des monstres(voir 2e2d: )
                  356a: des chaines de caracteres

                  pour le nommage des fichiers, pour le moment j'ai fait simple:
                  "seg_" + segment + ".c"

                  d'autant que le compilateur de Borland ne fonctionne qu'avec des noms de fichiers de 11 caracteres (dont l'extension) maxi.
                  Tout une epoque ;-)

                  en cadeau bonus, je joint le fichier "seg_3200.c".
                  C'est celui qui gere ? Bon vous avez deja compris.
                  Comme ca vous pourrez vous faire une idee de la lisibilite du code. Attention, certains noms sont susceptibles de changer l'avenir.
                  Les constantes "OBJ_xxx" ne sont pas du tout explicites, je sais, mais je n'ai pas encore eu le loisir de m'attaquer a leur renommage.
                  Pour savoir a quoi elles correspondent, je joint le fichier obj.h qui les definit.

                  seg_3200_20130716.zip
                  Dernière modification par ergonomy_joe, 17-07-2013, 03h05.

                  Commentaire


                  • #10
                    Billet #03
                    Aujourd'hui, nous allons nous interesser a ces fameux oeufs (EGG).

                    Le moteur du jeu maintient en permanence une zone de 40x40 (dont seuls 11x11 sont visibles);
                    sachant que le monde -- sa partie exterieure-- a une taille de 1024x1024, vous voyez que vous avez le temps pour tout visiter.

                    Je ne me suis pas encore interesse aux modalites exactes du rafraishissement de cette zone 40x40, mais pour le moment, notons juste le fait que lors de ce rafraichissement, le systeme procede a l'eclosion des oeufs.
                    Les oeufs en questions sont des objets fixes de la carte; ils sont invisibles, inamovibles, et indestructibles.
                    En gros, ce ne sont pas des objets avec lequel le joueur interagit, comme des pieces d'or, une armure, une chaise ...


                    Le code se trouve dans "seg_2e2d.c", les donnees dans "seg_3522.c".
                    Si vous voulez mon avis, et je vous le donne d'ailleurs, les donnees sont generees par un outil externe (un de ceux dont les documents sont signales dans le premier post).
                    C'est pourquoi meme avec toute la bonne volonte du monde, il est assez dur de rendre ce code source lisible.

                    Voici neanmoins quelques informations basiques:

                    Le tableau D_3522_0000 contient la liste des types de monstres geres.
                    Et nous appelerons l'index d'un type de monstre dans ce tableau sa "classe".
                    Cette classe sert ensuite a obtenir les informations dans les autres tableaux: force, points de vie, alignement, ...

                    Un point qui m'avait gene par rapport a cette classe etait son utilisation dans le code; par exemple dans le module combat on pouvait trouver dans la fonction qui gere la perte des points de vie:
                    Code:
                    if(GetMonsterClass(objTyp) == 0x1f) {
                    	HitPoints[objNum] = 255;
                    } ...
                    Un coup d'oeil rapide a l'objet de la classe 0x1f: OBJ_199, c'est a dire Lord British soi-meme.
                    En gros, chaque fois que Lord British se prend une baffe, ses points de vie reviennent a 255.

                    Ce qui m'a embete donc, c'est que d'autres cas de code specifique a un type de monstre sont traites avec le type justement, et non sa classe, et le fait de voir les deux approches en meme temps de me paraissait pas tres elegant.
                    Et puis j'ai eu l'illumination (hum hum) ce weekend en travaillant sur le tableau D_3522_0242: C'est un ensemble de flags qui indique la resistance du monstre au feu, au poison, sa capacite a voler, ...
                    Les anciennes doc de Origin mentionnent dans aussi des flags:
                    -vol de nourriture
                    -acide
                    -immortalite
                    -...
                    alors que le tableau ne contient pas de tels flags ...

                    Et voila donc l'illumination: si on reecrivait le code ci-dessus en:
                    Code:
                    if(IsMonster_IM(GetMonsterClass(objTyp))) {
                    	HitPoints[objNum] = 255;
                    } ...
                    (avec IM pour IMMORTAL)
                    Ca a tout de suite plus de gueule non ? Et surtout, ca devient coherent avec les autres macros qui permettent d'acceder aux flags en question.

                    Encore une fois, voila mon avis; Peut-etre qu'a un stade primaire du developpement du jeu l'immortalite etait geree par un des flags.
                    Et en cours de route, le nombre de flags augmentant, les developpeurs se sont retrouves a court de bit (16 maxi dans notre cas).
                    Quelqu'un a du donc decider que puisque l'immortalite (ou l'acide, le vol de nourriture, ... voir seg_3522.h pour la liste) ne concernait qu'un seul type de monstre, on pouvait sortir l'information du tableau, utiliser la classe de monstre dans la macro, et liberer ainsi un bit.


                    Voila donc le tout en piece jointe pour vous permettre de vous faire une idee.

                    U6_EGGS_20130722.zip

                    PS: Vous noterez qu'a la fin de seg_3522.c je force un padding. Un probleme deja evoque dans un precedent post, et que je n'ai toujours pas resolu.
                    Dernière modification par ergonomy_joe, 22-07-2013, 06h52.

                    Commentaire


                    • #11
                      BILLET #04 - Qui vole un oeuf ...

                      La gestion des AI dans Ultima 6 fait partie, vous vous en doutez bien, des "gros morceaux" du code source.
                      Le "coeur" du systeme consiste en gros, pour les NPC "important", en deux parties:
                      .l'emploi du temps: sous la forme "a telle et a tel endroit commence telle activite"
                      .activite: dois-je vraiment expliquer la ?

                      (Pour les NPC "moins importants", les monstres errants, pas d'emploi du temps, juste un comportement, souvent un comportement de combat)

                      En extrayant les donnees du fichier "Schedule", et en les retravaillant pour les rendre lisible, on trouve pour notre ami Lord British:

                      8 AI_SIT MkCoordXYZ(0x133,0x15C,0)
                      12 AI_EAT MkCoordXYZ(0x13C,0x16F,0)
                      14 AI_SIT MkCoordXYZ(0x133,0x15C,0)
                      18 AI_EAT MkCoordXYZ(0x13C,0x16F,0)
                      19 AI_SIT MkCoordXYZ(0x12D,0x160,0)
                      21 AI_SLEEP MkCoordXYZ(0x129,0x15E,0)

                      En francais, ca donne:
                      -assis sur le trone (royal bien sur) de 8h00 a 12h00
                      -collation de 12h00 a 14h00 a la cantine
                      -retour sur le trone de 14h00 a 18h00
                      -diner a la cantine de 18h00 a 19h00
                      -lecture dans la chambre (avec Sherry?) de 19h00 a 21h00
                      -et puis au lit, de 21h00 a 8h00 ... 11 heures de sommeil !!
                      (Pas tres occupe notre souverrain)


                      L'activite pour une plage horaire ne commencant que si le NPC a atteint le lieu concerne;
                      On a donc droit dans le code a un pave assez illisible, sur la recherche de chemin.
                      Ce n'est pas la partie la plus interessante a mon gout, mais si quelqu'un a le coeur de s'y plonger, le code est en piece-jointe.

                      La partie qui m'interesse -- nous interesse j'espere -- aujourd'hui concerne la gestion de l'activite du NPC; on a ici un gros "switch" parametre par le type d'activite.

                      Dont voici un petit extrait:
                      Code:
                      case AI_CONVERSE:
                      case AI_THIEF:
                      	if(CLOSE_ENOUGH0_S(5, sched_x, sched_y, MapX, MapY)) {
                      		if((di = C_1E0F_39B3(objNum)) >= 0) {
                      			NPCMode[objNum] = AI_LOITER;/*bug?*/
                      			if(NPCMode[objNum] == AI_THIEF)
                      				C_1E0F_3D8D(objNum, di);/*steal gold*/
                      			else
                      				C_1E0F_3E08(objNum, di);/*talk to*/
                      		}
                      	} else {
                      		C_1E0F_33C4(objNum, sched_x, sched_y);/*vagabonder?*/
                      	}
                      break;
                      Les deux activites "voleur" et "parleur" (on se croirait avec les 7 nains avec une telle traduction) partagent le meme comportement: vagabonder jusqu'a l'approche du joueur.
                      Quand ce dernier est a moins de 5 cases, le NPC va a sa rencontre, et en cas de contact, va respectivement le voler ou lui parler.

                      Il n'y a rien qui vous frappe ?

                      Mais si bien sur, la valeur AI_LOITER est affectee a l'activite du NPC juste avant qu'elle ne soit testee. Du coup, un NPC en mode "voleur" ne volera jamais et se comportera comme un "parleur".

                      Alors .... bug ?

                      Bon, si vous voulez mon avis, et c'est gratuit, je pense que l'affectation aurait du se trouver apres le test. En gros, apres avoir comis son forfait, ou entame la conversation, le NPC passe dans un autre mode proche du vagabondage.

                      Maintenant, y-a-t'il une quelconque influence sur le jeu ?

                      Pour commencer, je me suis plonge dans le fichier "Schedule", et la, deception: aucun personnage n'a de comportement voleur dans son emploi du temps.

                      Quelqu'un -- le probleme a deja ete pose dans la partie privee du forum -- a rappele la nature voleuse qu'ont les gremlins dans la serie des Ultima.
                      Mais la encore, deception: le vol de nourriture (et pas d'or d'ailleurs) est gere dans le module combat, par l'intermediaire des flags evoques dans le post precedent:
                      en l'occurence "IsMonster_SF" (SF pour "Steal Food")

                      Ce qui nous laisse deux endroits ou le comportement d'un NPC peut etre modifie:
                      .dans le module de conversation (Ultima VI represente ainsi une vraie avancee dans la serie dans la mesure ou les dialogues sont geres par un veritable petit interpreteur de script, et un script a la possibilite d'agir sur pratiquement tous les aspects du jeu).
                      .dans le module de generation de monstres: les oeufs (ouf! mon titre enfin explique)

                      La, je sens que je vais vous decevoir, je n'ai pas eu le courage de me lancer dans les 100 scripts de dialogues et les 60 fichiers de sauvegardes pour cherche si le comportement AI_THIEF est utilise.


                      Mais d'apres quelques infos trouvees sur le web (notamment sur le developpement de Nuvie), la reponse est non :-(


                      La question a 100 balles: serait-ce a cause de ce bug que les developpeurs ont decide de ne pas inclure de personnages voleurs ?

                      seg_1e0f.zip

                      Commentaire


                      • #12
                        C'est passionnant ! La suite, la suite !!!!!
                        PAF !

                        Commentaire


                        • #13
                          Envoyé par ergonomy_joe Voir le message
                          La question a 100 balles: serait-ce a cause de ce bug que les developpeurs ont decide de ne pas inclure de personnages voleurs ?
                          Ou le contraire ? Le choix a été fait de ne pas inclure de persos voleurs sur le moment, juste de prévoir le code pour une évolution possible et l'affectation servirait à shunter le test. Mais bon, difficile de savoir.

                          En tout cas, beau projet, bravo !

                          Commentaire


                          • #14
                            @Rag'd

                            Et oui, le code tout seul ne nous permet malheureusement pas de repondre a la question.
                            Peut-etre si quelqu'un se sentait de mettre la main sur un des developpeurs originaux et de le harceler ;-)


                            @darkbeber

                            merci ! :-)
                            Et comme c'est demande gentiment, passons tout de suite au ...


                            Billet #05

                            Alors aujourd'hui, nous allons nous interesser au point d'entree du programme, la fonction main.
                            Ou plus generalement au module qui la contient: seg_0903.c
                            Alors ca n'est pas super-super-interessant ce qui se passe dans ce module, mais on n'y trouve centralisees quelques fonctionalites essentielles du jeu.

                            Par exemple, la gestion memoire. Dans le cas d'Ultima 6, elle est assez simple; mis a part la memoire allouee au chargement de l'exe (autrement dit le code, les donnes initialisees, non initialisees, et la pile),
                            le programme va s'allouer la totalite de la memoire restante en appellant les fonctions de la librairie standard: farecorleft et farmalloc.
                            C'est la fonction __MemAlloc qui va s'occuper de la "morceller".


                            Ensuite, le chargement des ressources, par la fonctions LoadFile; le flag IsZFile permet de preciser si la resource doit en plus etre decompressee.
                            Parlons-en de la decompression; elle est de type LZ et la fonction principale ressemble etrangement a du code qu'on peut trouver sur internet par un certain Tom Pfau.
                            Il y a quelques differences; par exemple, le code d'Ultima 6 utilise des macros la ou celui de Tom utilise des appels de fonction.
                            N'empeche que la ressemblance est troublante. Vol ? Open source ? Licence ?

                            (Pour la petite histoire, on trouve cette fonction deja dans Ultima 5)

                            Enfin, pour l'acces au systeme de fichier, ce ne sont pas les fonctions de la librairie C qui sont utilisees, mais une librairie "maison" de Origin System (d'ou l'idee du prefixe "OSI_").
                            Cette librairie se retrouve telle quelle dans les deux autres executables: u6.exe et end.exe.
                            Elle inclus un systeme de gestion d'erreur a base de callback: en gros, la gestion est centralisee en un point du code, la fonction C_0903_0430.


                            Voila donc un module essentiel mais bien ennuyeux; finissons donc sur une petite note ludique: un BUG !

                            Ca se passe dans la fonction C_0903_0A1E. Fonction qui prepare le lancement de la cinematique de victoire en fin de jeu.
                            L'essentiel consiste a executer end.exe en lui passant en "arguments" la duree, en temps de jeu, de la partie.
                            Et c'est la que ca bloque a mon avis.
                            Nous avons donc trois variable globale, Date_Y, Date_M et Date_D qui contiennent respectivement l'annee, le mois, et le jour du mois.
                            Une annee de Britannia contient 12 mois, qui contiennent chacun 28 jours.
                            Sachant que le jeu commence le 4/7/161, c'est le bout de code suivant qui permet de calculer la difference entre la date du jour et la date originelle en jours/mois/annees:
                            Code:
                            int years,monthes,days;
                            
                            years = Date_Y - 161;
                            monthes = Date_M - 7;
                            days = Date_D - 4;
                            jusqu'ici pas de souci, il reste juste a gerer les cas ou days ou monthes sont negatifs.
                            Et c'est la que ca bloque:
                            Code:
                            if(Date_D < 0) {
                            	Date_D += 28;
                            	monthes --;
                            }
                            if(monthes < 0) {
                            	monthes += 13;
                            	years --;
                            }
                            Pourquoi ? Parce que le premier test devrait se faire sur days et non sur Date_D.
                            Ensuite, je n'arrive pas bien a comprendre pourquoi quand le nombre de mois est negatif, on ajoute 13 et pas 12 ?!?

                            Je pinaille, c'est sur, et puis je ne suis pas alle verifie plus loin que ca, mais je reste persuade que dans certains cas, le calcul va nous offrir un resultat curieux, qui ne fera heureusement pas planter le jeu, mais affichera peut-etre un resultat curieux quand Lord British vous annonce le temps qu'il vous a pris pour arriver a la victoire.

                            Et en PJ, les fichiers concernes:
                            main_20130820.zip

                            Commentaire


                            • #15
                              Envoyé par ergonomy_joe Voir le message
                              Pourquoi ? Parce que le premier test devrait se faire sur days et non sur Date_D.
                              Ensuite, je n'arrive pas bien a comprendre pourquoi quand le nombre de mois est negatif, on ajoute 13 et pas 12 ?!?
                              [ATTACH]31107[/ATTACH]
                              Surtout que Date_D ne peut pas être négatif !
                              Et pour le + 13 ... Oui, ça semble bancal tout ça.

                              C'est gros, mais, sans doute que le test est passé à coté, vu qu'on ne s'y attarde par forcément. Enfin, certain joueur certainement et là, il est facile d'avoir des retours.
                              Dernière modification par Rag'd, 20-08-2013, 21h43.

                              Commentaire

                              Chargement...
                              X