# HG changeset patch # User Dylan Holmes # Date 1333883619 18000 # Node ID ff65ee0944fe65f3266e9d89194584e31e50dfd2 # Parent 5639312a393f8eb6acf1ce370b7c7cd5dc0a7537 more progess: now rom.org tangles into hxc.clj; i'll be subdividing the code blocks as I write more. diff -r 5639312a393f -r ff65ee0944fe clojure/com/aurellem/gb/hxc.clj --- a/clojure/com/aurellem/gb/hxc.clj Sun Apr 08 01:28:39 2012 -0500 +++ b/clojure/com/aurellem/gb/hxc.clj Sun Apr 08 06:13:39 2012 -0500 @@ -1,3 +1,4 @@ + (ns com.aurellem.gb.hxc (:use (com.aurellem.gb assembly characters gb-driver util mem-util constants species)) @@ -8,8 +9,6 @@ ; ************* HANDWRITTEN CONSTANTS - - (def pkmn-types [:normal ;;0 :fighting ;;1 @@ -138,7 +137,6 @@ "(disable)" ]) - ;; ************** HARDCODED DATA (defn hxc-thunk @@ -162,7 +160,6 @@ (partial comp (partial map character-codes->str)) hxc-thunk)) - ;; -------------------------------------------------- @@ -510,6 +507,15 @@ (defn format-evo + "Parse a sequence of evolution data, returning a map. First is the +method: 0 = end-evolution-data. 1 = level-up, 2 = item, 3 = trade. Next is an item id, if the + method of evolution is by item (only stones will actually make pokemon + evolve, for some auxillary reason.) Finally, the minimum level for + evolution to occur (level 1 means no limit, which is used for trade + and item evolutions), followed by the internal id of the pokemon + into which to evolve. Hence, level up and trade evolutions are + described with 3 + bytes; item evolutions with four." [coll] (let [method (first coll)] (cond (empty? coll) [] @@ -555,7 +561,7 @@ (defn hxc-learnsets "Hardcoded map associating pokemon names to lists of pairs [lvl move] of abilities they learn as they level up. The data -exists at ROM@3400, sorted by internal order. Pointers to the data +exists at ROM@34000, sorted by internal order. Pointers to the data exist at ROM@3B1E5; see also, hxc-ptrs-evolve" ([] (hxc-learnsets com.aurellem.gb.gb-driver/original-rom)) ([rom] @@ -932,6 +938,9 @@ ;; note: 1195C-6A says ABLE#NOT ABLE#, but so does 119C0-119CE. ;; The first instance is for Machines; the second, for stones. +;; 0x251A (in indexable mem): image decompression routine seems to begin here. + + (comment (def hxc-later @@ -951,9 +960,3 @@ (map character-codes->str (take-nth 4 dex)))) ) - - - - - - diff -r 5639312a393f -r ff65ee0944fe org/rom.org --- a/org/rom.org Sun Apr 08 01:28:39 2012 -0500 +++ b/org/rom.org Sun Apr 08 06:13:39 2012 -0500 @@ -10,7 +10,9 @@ # about map headers http://datacrystal.romhacking.net/wiki/Pokemon_Red/Blue:Notes # map headers Yellow http://www.pokecommunity.com/archive/index.php/t-235311.html # pokedollar: U+20B1 -* Mapping the ROM +* Introduction + +** Mapping the ROM | ROM address (hex) | Description | Format | Example | |-------------------+-----------------+-----------------+-----------------| @@ -22,18 +24,24 @@ | 05EDB. | Which Pok\eacute{}mon to show during Prof. Oak's introduction. | A single byte, the Pok\eacute{}mon's internal id. | In Pok\eacute{}mon Yellow, it shows Pikachu during the introduction; Pikachu's internal id is 0x54. | | 06698- | ? Background music. | | | | 0822E-082F? | Pointers to background music, part I. | | | -| 0CB95- | Pointers to lists of wild pokemon to encounter in each region. These lists begin at 04D89, see above. | Each pointer is a low-byte, high-byte pair. | The first entry is 0x89 0x4D, corresponding to the address 0x4D89, the location of the first list of wild Pok\eacute{}mon (see above). | +| 0CB95- | Pointers to lists of wild pokemon to encounter in each region. These lists begin at 04D89, see above. | Each pointer is a low-byte, high-byte pair. | The first entry is 0x89 0x4D, corresponding to the address 0x4D89, the location of the first list of wild Pok\eacute{}mon (see 04D89, above). | +|-------------------+-----------------+-----------------+-----------------| +| 0DADB. | Amount of HP restored by Hyper Potion. | The HP consists of a single byte. TODO: Discover what the surrounding data does, and find the data for the amount of HP restored by other items: Fresh Water (50HP), Soda (60HP), Lemonade(80HP). | 200 | +| 0DAE0. | Amount of HP restored by Super Potion. | " | 50 | +| 0DAE3. | Amount of HP restored by Potion. | " | 20 | +|-------------------+-----------------+-----------------+-----------------| | 0DD4D-DD72 | Names of permanent stats. | Variable-length strings separated by 0x50. | #HEALTH#ATTACK#DEFENSE#SPEED#SPECIAL# | | 1195C-1196A | The two terms for being able/unable to learn a TM/HM. | Variable-length strings separated by 0x50. | ABLE#NOT ABLE# | | 119C0-119CE | The two terms for being able/unable to evolve using the current stone. | Variable-length strings separated by 0x50. | ABLE#NOT ABLE# | | 1232D-12364 | Which moves are taught by the TMs and HMs | A list of 55 move ids (50 TMs, plus 5 HMs). First, the move that will be taught by TM01; second, the move that will be taught by TM02; and so on. The last five entries are the moves taught by HMs 1-5. (See also, BC000 below) | The first few entries are (5 13 14 18 ...) corresponding to Mega Punch (TM01), Razor Wind (TM02), Swords Dance (TM03), Whirlwind (TM04), ... | | 27D99-27DFF | Names of the Pok\eacute{}mon types. | Variable-length type names (strings of character codes). Names are separated by a single 0x80 character. | NORMAL#FIGHTING#... | -| 27E77- | Trainer title names. | | YOUNGSTER#BUG CATCHER#LASS#... | -| 34000- | Evolution and learnset data. | | | -| 38000- | The basic properties and effects of moves. | Fixed-length (6 byte) continguous descriptions (no separating character): move-index, move-effect, power, move-type, accuracy, pp. | The entry for Pound, the first attack in the list, is (1 0 40 0 255 35). See below for more explanation. | +| 27E77- | Trainer title names. | Variable-length names separated by 0x80. | YOUNGSTER#BUG CATCHER#LASS#... | +| 34000- | | | | +| 38000-383DE | The basic properties and effects of moves. (165 moves total) | Fixed-length (6 byte) continguous descriptions (no separating character): move-index, move-effect, power, move-type, accuracy, pp. | The entry for Pound, the first attack in the list, is (1 0 40 0 255 35). See below for more explanation. | | 383DE- | Species data for the Pokemon, listed in Pokedex order: Pokedex number; base moves; types; learnable TMs and HMs; base HP, attack, defense, speed, special; sprite data. | | | | 39462- | The Pok\eacute{}mon cry data. | Fixed-length (3 byte) descriptions of cries. | | | 3B1E5- | Pointers to evolution/learnset data. | | | +| 3B361- | Evolution and learnset data. [fn::Evolution data consists of how to make Pok\eacute{}mon evolve, and what they evolve into. Learnset data consists of the moves that Pok\eacute{}mon learn as they level up.] | Variable-length evolution information (see below), followed by a list of level/move-id learnset pairs. | | | 40687- | Species data from the Pok\eacute{}dex: species name, height, weight, etc. | Fixed-length sequences of bytes. See below for specifics. | | | 410B1- | A conversion table between internal order and Pokedex order. | | | | 5DE10-5DE30 | Abbreviations for status ailments. | Fixed-length strings, probably[fn::Here's something strange: all of the status messages start with 0x7F and end with 0x4F \mdash{}except PAR, which ends with 0x50.]. The last entry is QUIT##. | [0x7F] *SLP* [0x4E][0x7F] *PSN* [0x4E][0x7F] *PAR* [0x50][0x7F]... | @@ -41,13 +49,1156 @@ | 7C249-7C2?? | Pointers to background music, pt II. | | | | 98000- | Dialogue | | | | B8000- | The text of each Pokemon's Pok\eacute{}dex entry. | | | -| BC000- | Move names. | | | +| BC000-BC60E | Move names. | Variable-length move names, separated by 0x80. The moves are in internal order. | POUND#KARATE CHOP#DOUBLESLAP#COMET PUNCH#... | | E8000-E876C | Names of the \ldquo{}190\rdquo{} species of Pok\eacute{}mon in memory. | Fixed length (10-letter) Pok\eacute{}mon names. Any extra space is padded with the character 0x80. The names are in \ldquo{}internal order\rdquo{}. | RHYDON####KANGASKHANNIDORAN♂#... | | | | | | | | | | | + + + +** COMMENT Getting linguistic data: names, words, etc. + +Some of the simplest data + + +One of the simplest data structures in the Pok\eacute{} ROM is an +unbroken list of strings that either (a) all have a specific length, +or (b) are all separated by the same character. + +Because lots of good data has this format, we'll start by writing a +template function to extract it: + +#+name: hxc-thunks +#+begin_src clojure :results silent +(defn hxc-thunk + "Creates a thunk (nullary fn) that grabs data in a certain region of rom and +splits it into a collection by 0x50. If rom is not supplied, uses the + original rom data." + [start length] + (fn self + ([rom] + (take-nth 2 + (partition-by #(= % 0x50) + (take length + (drop start rom))))) + ([] + (self com.aurellem.gb.gb-driver/original-rom)))) + +(def hxc-thunk-words + "Same as hxc-thunk, except it interprets the rom data as characters, + returning a collection of strings." + (comp + (partial comp (partial map character-codes->str)) + hxc-thunk)) + +#+end_src + +* Pok\eacute{}mon +** Names of each species +The names of the Pok\eacute{}mon species are stored in +ROM@E8000. This name list is interesting, for a number of reasons: +- The names are stored in [[ ][internal order]] rather than in the familiar + Pok\eacute{}dex order. This seemingly random order probably represents the order in which the authors created or + programmed in the Pok\eacute{}mon; it's used throughout the game. +- There is enough space allocated for 190 Pok\eacute{}mon. As I + understand it, there were originally going to be 190 Pok\eacute{}mon + in Generation I, but the creators decided to defer some to + Generation II. This explains why many Gen I and Gen II Pok\eacute{}mon + have the same aesthetic feel. +- The list is pockmarked with random gaps, due to the strange internal + ordering + and 39 unused spaces [fn::190 allocated spaces minus 151 true Pok\eacute{}mon]. These missing spaces are filled with the + placeholder name, =MISSINGNO.= (\ldquo{}Missing number\rdquo{}). + +Each name is exactly ten letters long (Whenever a name is too short, the extra +space is padded with the character 0x50). + + + +#+name: pokenames +#+begin_src clojure + +(defn hxc-pokenames-raw + "The hardcoded names of the 190 species in memory. List begins at +ROM@E8000. Although names in memory are padded with 0x50 to be 10 characters + long, these names are stripped of padding. See also, hxc-pokedex-names" + ([] + (hxc-pokenames-raw com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [count-species 190 + name-length 10] + (map character-codes->str + (partition name-length + (map #(if (= 0x50 %) 0x00 %) + (take (* count-species name-length) + (drop 0xE8000 + rom)))))))) +(def hxc-pokenames + (comp + (partial map format-name) + hxc-pokenames-raw)) + + + + +(defn hxc-pokedex-names + "The names of the pokemon in hardcoded pokedex order. List begins at +ROM@410B1. See also, hxc-pokenames." + ([] (hxc-pokedex-names + com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [names (hxc-pokenames rom)] + (#(mapv % + ((comp range count keys) %)) + (zipmap + (take (count names) + (drop 0x410b1 rom)) + + names))))) + +#+end_src + * Appendices ** Internal Pok\eacute{}mon IDs ** Type IDs + +#+name: type-ids +#+begin_src clojure +(def pkmn-types + [:normal ;;0 + :fighting ;;1 + :flying ;;2 + :poison ;;3 + :ground ;;4 + :rock ;;5 + :bird ;;6 + :bug ;;7 + :ghost ;;8 + :A + :B + :C + :D + :E + :F + :G + :H + :I + :J + :K + :fire ;;20 (0x14) + :water ;;21 (0x15) + :grass ;;22 (0x16) + :electric ;;23 (0x17) + :psychic ;;24 (0x18) + :ice ;;25 (0x19) + :dragon ;;26 (0x1A) + ]) +#+end_src + ** Basic effects of moves + +*** Table of basic effects + +The possible effects of moves in Pok\eacute{}mon \mdash{} for example, dealing +damage, leeching health, or potentially poisoning the opponent +\mdash{} are stored in a table. Each move has exactly one effect, and +different moves might have the same effect. + +For example, Leech Life, Mega Drain, and Absorb all have effect ID #3, which is \ldquo{}Leech half of the inflicted damage.\rdquo{} + +All the legitimate move effects are listed in the table +below. Here are some notes for reading it: + +- Whenever an effect has a chance of doing something (like a chance of + poisoning the opponent), I list the chance as a hexadecimal amount out of 256 to avoid rounding errors. To convert the hex amount into a percentage, divide by 256. +- For some effects, the description is too cumbersome to + write. Instead, I just write a move name + in parentheses, like: (leech seed). That move gives a characteristic example + of the effect. +- I use the abbreviations =atk=, =def=, =spd=, =spc=, =acr=, =evd= for + attack, defense, speed, special, accuracy, and evasion. +. + + + +| ID (hex) | Description | Notes | +|----------+-------------------------------------------------------------------------------------------------+------------------------------------------------------------------| +| 0 | normal damage | | +| 1 | no damage, just sleep | TODO: find out how many turns | +| 2 | 0x4C chance of poison | | +| 3 | leech half of inflicted damage | | +| 4 | 0x19 chance of burn | | +| 5 | 0x19 chance of freeze | | +| 6 | 0x19 chance of paralysis | | +| 7 | user faints; opponent's defense is halved during attack. | | +| 8 | leech half of inflicted damage ONLY if the opponent is asleep | | +| 9 | imitate last attack | | +| A | user atk +1 | | +| B | user def +1 | | +| C | user spd +1 | | +| D | user spc +1 | | +| E | user acr +1 | This effect is unused. | +| F | user evd +1 | | +| 10 | get post-battle money = 2 * level * uses | | +| 11 | move has 0xFE acr, regardless of battle stat modifications. | | +| 12 | opponent atk -1 | | +| 13 | opponent def -1 | | +| 14 | opponent spd -1 | | +| 15 | opponent spc -1 | | +| 16 | opponent acr -1 | | +| 17 | opponent evd -1 | | +| 18 | converts user's type to opponent's. | | +| 19 | (haze) | | +| 1A | (bide) | | +| 1B | (thrash) | | +| 1C | (teleport) | | +| 1D | (fury swipes) | | +| 1E | attacks 2-5 turns | Unused. TODO: find out what it does. | +| 1F | 0x19 chance of flinching | | +| 20 | opponent sleep for 1-7 turns | | +| 21 | 0x66 chance of poison | | +| 22 | 0x4D chance of burn | | +| 23 | 0x4D chance of freeze | | +| 24 | 0x4D chance of paralysis | | +| 25 | 0x4D chance of flinching | | +| 26 | one-hit KO | | +| 27 | charge one turn, atk next. | | +| 28 | fixed damage, leaves 1HP. | Is the fixed damage the power of the move? | +| 29 | fixed damage. | Like seismic toss, dragon rage, psywave. | +| 2A | atk 2-5 turns; opponent can't attack | The odds of attacking for /n/ turns are: (0 0x60 0x60 0x20 0x20) | +| 2B | charge one turn, atk next. (can't be hit when charging) | | +| 2C | atk hits twice. | | +| 2D | user takes 1 damage if misses. | | +| 2E | evade status-lowering effects | Caused by you or also your opponent? | +| 2F | broken: if user is slower than opponent, makes critical hit impossible, otherwise has no effect | This is the effect of Focus Energy. It's (very) broken. | +| 30 | atk causes recoil dmg = 1/4 dmg dealt | | +| 31 | confuses opponent | | +| 32 | user atk +2 | | +| 33 | user def +2 | | +| 34 | user spd +2 | | +| 35 | user spc +2 | | +| 36 | user acr +2 | This effect is unused. | +| 37 | user evd +2 | This effect is unused. | +| 38 | restores up to half of user's max hp. | | +| 39 | (transform) | | +| 3A | opponent atk -2 | | +| 3B | opponent def -2 | | +| 3C | opponent spd -2 | | +| 3D | opponent spc -2 | | +| 3E | opponent acr -2 | | +| 3F | opponent evd -2 | | +| 40 | doubles user spc when attacked | | +| 41 | doubles user def when attacked | | +| 42 | just poisons opponent | | +| 43 | just paralyzes opponent | | +| 44 | 0x19 chance opponent atk -1 | | +| 45 | 0x19 chance opponent def -1 | | +| 46 | 0x19 chance opponent spd -1 | | +| 47 | 0x4C chance opponent spc -1 | | +| 48 | 0x19 chance opponent acr -1 | | +| 49 | 0x19 chance opponent evd -1 | | +| 4A | ??? | ;; unused? no effect? | +| 4B | ??? | ;; unused? no effect? | +| 4C | 0x19 chance of confusing the opponent | | +| 4D | atk hits twice. 0x33 chance opponent poisioned. | | +| 4E | broken. crash the game after attack. | | +| 4F | (substitute) | | +| 50 | unless opponent faints, user must recharge after atk. some exceptions apply | | +| 51 | (rage) | | +| 52 | (mimic) | | +| 53 | (metronome) | | +| 54 | (leech seed) | | +| 55 | does nothing (splash) | | +| 56 | (disable) | | +#+end_src + +*** Source +#+name: move-effects +#+begin_src clojure +(def move-effects + ["normal damage" + "no damage, just opponent sleep" ;; how many turns? is atk power ignored? + "0x4C chance of poison" + "leech half of inflicted damage" + "0x19 chance of burn" + "0x19 chance of freeze" + "0x19 chance of paralyze" + "user faints; opponent defense halved during attack." + "leech half of inflicted damage ONLY if sleeping opponent." + "imitate last attack" + "user atk +1" + "user def +1" + "user spd +1" + "user spc +1" + "user acr +1" ;; unused?! + "user evd +1" + "get post-battle $ = 2*level*uses" + "0xFE acr, no matter what." + "opponent atk -1" ;; acr taken from move acr? + "opponent def -1" ;; + "opponent spd -1" ;; + "opponent spc -1" ;; + "opponent acr -1";; + "opponent evd -1" + "converts user's type to opponent's." + "(haze)" + "(bide)" + "(thrash)" + "(teleport)" + "(fury swipes)" + "attacks 2-5 turns" ;; unused? like rollout? + "0x19 chance of flinch" + "opponent sleep for 1-7 turns" + "0x66 chance of poison" + "0x4D chance of burn" + "0x4D chance of freeze" + "0x4D chance of paralyze" + "0x4D chance of flinch" + "one-hit KO" + "charge one turn, atk next." + "fixed damage, leaves 1HP." ;; how is dmg determined? + "fixed damage." ;; cf seismic toss, dragon rage, psywave. + "atk 2-5 turns; opponent can't attack" ;; unnormalized? (0 0x60 0x60 0x20 0x20) + "charge one turn, atk next. (can't be hit when charging)" + "atk hits twice." + "user takes 1 damage if misses." + "evade status-lowering effects" ;;caused by you or also your opponent? + "(broken) if user is slower than opponent, makes critical hit impossible, otherwise has no effect" + "atk causes recoil dmg = 1/4 dmg dealt" + "confuses opponent" ;; acr taken from move acr + "user atk +2" + "user def +2" + "user spd +2" + "user spc +2" + "user acr +2" ;; unused! + "user evd +2" ;; unused! + "restores up to half of user's max hp." ;; broken: fails if the difference + ;; b/w max and current hp is one less than a multiple of 256. + "(transform)" + "opponent atk -2" + "opponent def -2" + "opponent spd -2" + "opponent spc -2" + "opponent acr -2" + "opponent evd -2" + "doubles user spc when attacked" + "doubles user def when attacked" + "just poisons opponent" ;;acr taken from move acr + "just paralyzes opponent" ;; + "0x19 chance opponent atk -1" + "0x19 chance opponent def -1" + "0x19 chance opponent spd -1" + "0x4C chance opponent spc -1" ;; context suggest chance is 0x19 + "0x19 chance opponent acr -1" + "0x19 chance opponent evd -1" + "???" ;; unused? no effect? + "???" ;; unused? no effect? + "0x19 chance opponent confused" + "atk hits twice. 0x33 chance opponent poisioned." + "broken. crash the game after attack." + "(substitute)" + "unless opponent faints, user must recharge after atk. some + exceptions apply." + "(rage)" + "(mimic)" + "(metronome)" + "(leech seed)" + "does nothing (splash)" + "(disable)" + ]) +#+end_src + + ** Alphabet code + +#+begin_src clojure :tangle ../clojure/com/aurellem/gb/hxc.clj + +(ns com.aurellem.gb.hxc + (:use (com.aurellem.gb assembly characters gb-driver util mem-util + constants species)) + (:import [com.aurellem.gb.gb_driver SaveState])) + + + + +; ************* HANDWRITTEN CONSTANTS + +<> + + +;; question: when status effects claim to take +;; their accuracy from the move accuracy, does +;; this mean that the move always "hits" but the +;; status effect may not? + +<> + +;; ************** HARDCODED DATA + +<> +;; -------------------------------------------------- + + +<> + +;; http://hax.iimarck.us/topic/581/ +(defn hxc-cry + "The pokemon cry data in internal order. List begins at ROM@39462" + ([](hxc-cry com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (zipmap + (hxc-pokenames rom) + (map + (fn [[cry-id pitch length]] + {:cry-id cry-id + :pitch pitch + :length length} + ) + (partition 3 + (drop 0x39462 rom)))))) + +(defn hxc-cry-groups + ([] (hxc-cry-groups com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (map #(mapv first + (filter + (fn [[k v]] + (= % (:cry-id v))) + (hxc-cry))) + ((comp + range + count + set + (partial map :cry-id) + vals + hxc-cry) + rom)))) + + +(defn cry-conversion! + "Convert Porygon's cry in ROM to be the cry of the given pokemon." + [pkmn] + (write-rom! + (rewrite-memory + (vec(rom)) + 0x3965D + (map second + ((hxc-cry) pkmn))))) + +(def hxc-items-raw + "The hardcoded names of the items in memory. List begins at +ROM@045B7" + (hxc-thunk-words 0x45B7 870)) + +(def hxc-types + "The hardcoded type names in memory. List begins at ROM@27D99, + shortly before hxc-titles." + (hxc-thunk-words 0x27D99 102)) + +(def hxc-titles + "The hardcoded names of the trainer titles in memory. List begins at +ROM@27E77" + (hxc-thunk-words 0x27E77 196)) + + +(def hxc-pokedex-text-raw + "The hardcoded pokedex entries in memory. List begins at +ROM@B8000, shortly before move names." + (hxc-thunk-words 0xB8000 14754)) + + + +(def hxc-items + "The hardcoded names of the items in memory, presented as + keywords. List begins at ROM@045B7. See also, hxc-items-raw." + (comp (partial map format-name) hxc-items-raw)) + +(defn hxc-pokedex-text + "The hardcoded pokedex entries in memory, presented as an +associative hash map. List begins at ROM@B8000." + ([] (hxc-pokedex-text com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (zipmap + (hxc-pokedex-names rom) + (cons nil ;; for missingno. + (hxc-pokedex-text-raw rom))))) + +;; In red/blue, pokedex stats are in internal order. +;; In yellow, pokedex stats are in pokedex order. + +(defn hxc-pokedex-stats + "The hardcoded pokedex stats (species height weight) in memory. List +begins at ROM@40687" + ([] (hxc-pokedex-stats com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [pokedex-names (zipmap (range) (hxc-pokedex-names rom)) + pkmn-count (count pokedex-names) + ] + ((fn capture-stats + [n stats data] + (if (zero? n) stats + (let [[species + [_ + height-ft + height-in + weight-1 + weight-2 + _ + dex-ptr-1 + dex-ptr-2 + dex-bank + _ + & data]] + (split-with (partial not= 0x50) data)] + (recur (dec n) + (assoc stats + (pokedex-names (- pkmn-count (dec n))) + {:species + (format-name (character-codes->str species)) + :height-ft + height-ft + :height-in + height-in + :weight + (/ (low-high weight-1 weight-2) 10.) + + ;; :text + ;; (character-codes->str + ;; (take-while + ;; (partial not= 0x50) + ;; (drop + ;; (+ 0xB8000 + ;; -0x4000 + ;; (low-high dex-ptr-1 dex-ptr-2)) + ;; rom))) + }) + + data) + + + ))) + + pkmn-count + {} + (drop 0x40687 rom))) )) + + + + + + + +(def hxc-places + "The hardcoded place names in memory. List begins at +ROM@71500. [Cinnabar] Mansion seems to be dynamically calculated." + (hxc-thunk-words 0x71500 560)) + + +(defn hxc-dialog + "The hardcoded dialogue in memory, including in-game alerts. Dialog + seems to be separated by 0x57 instead of 0x50 (END). Begins at ROM@98000." + ([rom] + (map character-codes->str + (take-nth 2 + (partition-by #(= % 0x57) + (take 0x0F728 + (drop 0x98000 rom)))))) + ([] + (hxc-dialog com.aurellem.gb.gb-driver/original-rom))) + + +(def hxc-move-names + "The hardcoded move names in memory. List begins at ROM@BC000" + (hxc-thunk-words 0xBC000 1551)) + + +(defn hxc-move-data + "The hardcoded (basic (move effects)) in memory. List begins at +0x38000. Returns a map of {:name :power :accuracy :pp :fx-id + :fx-txt}. The move descriptions are handwritten, not hardcoded." + ([] + (hxc-move-data com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [names (vec (hxc-move-names rom)) + move-count (count names) + move-size 6 + types pkmn-types ;;; !! hardcoded types + ] + (zipmap (map format-name names) + (map + (fn [[idx effect power type-id accuracy pp]] + {:name (names (dec idx)) + :power power + :accuracy accuracy + :pp pp + :type (types type-id) + :fx-id effect + :fx-txt (get move-effects effect) + } + ) + + (partition move-size + (take (* move-size move-count) + (drop 0x38000 rom)))))))) + + + +(defn hxc-move-data* + "Like hxc-move-data, but reports numbers as hexadecimal symbols instead." + ([] + (hxc-move-data* com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [names (vec (hxc-move-names rom)) + move-count (count names) + move-size 6 + format-name (fn [s] + (keyword (.toLowerCase + (apply str + (map #(if (= % \space) "-" %) s))))) + ] + (zipmap (map format-name names) + (map + (fn [[idx effect power type accuracy pp]] + {:name (names (dec idx)) + :power power + :accuracy (hex accuracy) + :pp pp + :fx-id (hex effect) + :fx-txt (get move-effects effect) + } + ) + + (partition move-size + (take (* move-size move-count) + (drop 0x38000 rom)))))))) + + +(defn hxc-machines + "The hardcoded moves taught by TMs and HMs. List begins at ROM@1232D." + ([] (hxc-machines + com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [moves (hxc-move-names rom)] + (zipmap + (range) + (take-while + (comp not nil?) + (map (comp + format-name + (zipmap + (range) + moves) + dec) + (take 100 + (drop 0x1232D rom)))))))) + + + +(defn internal-id + ([rom] + (zipmap + (hxc-pokenames rom) + (range))) + ([] + (internal-id com.aurellem.gb.gb-driver/original-rom))) + + + + + +;; nidoran gender change upon levelup +;; (-> +;; @current-state +;; rom +;; vec +;; (rewrite-memory +;; (nth (hxc-ptrs-evolve) ((internal-id) :nidoran♂)) +;; [1 1 15]) +;; (rewrite-memory +;; (nth (hxc-ptrs-evolve) ((internal-id) :nidoran♀)) +;; [1 1 3]) +;; (write-rom!) + +;; ) + + + + +(defn hxc-advantage + ;; in-game multipliers are stored as 10x their effective value + ;; to allow for fractional multipliers like 1/2 + + "The hardcoded type advantages in memory, returned as tuples of + atk-type def-type multiplier. By default (i.e. if not listed here), +the multiplier is 1. List begins at 0x3E62D." + ([] (hxc-advantage com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (map + (fn [[atk def mult]] [(get pkmn-types atk (hex atk)) + (get pkmn-types def (hex def)) + (/ mult 10)]) + (partition 3 + (take-while (partial not= 0xFF) + (drop 0x3E62D rom)))))) + + + +(defn format-evo + "Parse a sequence of evolution data, returning a map. First is the +method: 0 = end-evolution-data. 1 = level-up, 2 = item, 3 = trade. Next is an item id, if the + method of evolution is by item (only stones will actually make pokemon + evolve, for some auxillary reason.) Finally, the minimum level for + evolution to occur (level 1 means no limit, which is used for trade + and item evolutions), followed by the internal id of the pokemon + into which to evolve. Hence, level up and trade evolutions are + described with 3 + bytes; item evolutions with four." + [coll] + (let [method (first coll)] + (cond (empty? coll) [] + (= 0 method) [] ;; just in case + (= 1 method) ;; level-up evolution + (conj (format-evo (drop 3 coll)) + {:method :level-up + :min-level (nth coll 1) + :into (dec (nth coll 2))}) + + (= 2 method) ;; item evolution + (conj (format-evo (drop 4 coll)) + {:method :item + :item (dec (nth coll 1)) + :min-level (nth coll 2) + :into (dec (nth coll 3))}) + + (= 3 method) ;; trade evolution + (conj (format-evo (drop 3 coll)) + {:method :trade + :min-level (nth coll 1) ;; always 1 for trade. + :into (dec (nth coll 2))})))) + + +(defn hxc-ptrs-evolve + "A hardcoded collection of 190 pointers to alternating evolution/learnset data, +in internal order." + ([] + (hxc-ptrs-evolve com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [ + pkmn-count (count (hxc-pokenames-raw)) ;; 190 + ptrs + (map (fn [[a b]] (low-high a b)) + (partition 2 + (take (* 2 pkmn-count) + (drop 0x3b1e5 rom))))] + (map (partial + 0x34000) ptrs) + + ))) + + +(defn hxc-learnsets + "Hardcoded map associating pokemon names to lists of pairs [lvl + move] of abilities they learn as they level up. The data +exists at ROM@34000, sorted by internal order. Pointers to the data + exist at ROM@3B1E5; see also, hxc-ptrs-evolve" + ([] (hxc-learnsets com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (apply assoc + {} + (interleave + (hxc-pokenames rom) + (map (comp + (partial map + (fn [[lvl mv]] [lvl (dec mv)])) + (partial partition 2) + ;; keep the learnset data + (partial take-while (comp not zero?)) + ;; skip the evolution data + rest + (partial drop-while (comp not zero?))) + (map #(drop % rom) + (hxc-ptrs-evolve rom))))))) + +(defn hxc-learnsets-pretty + "Live hxc-learnsets except it reports the name of each move --- as +it appears in rom --- rather than the move index." + ([] (hxc-learnsets-pretty com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [moves (vec(map format-name (hxc-move-names)))] + (into {} + (map (fn [[pkmn learnset]] + [pkmn (map (fn [[lvl mv]] [lvl (moves mv)]) + learnset)]) + (hxc-learnsets rom)))))) + + + + +(defn hxc-evolution + "Hardcoded evolution data in memory. The data exists at ROM@34000, + sorted by internal order. Pointers to the data exist at ROM@3B1E5; see also, hxc-ptrs-evolve." + ([] (hxc-evolution com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (apply assoc {} + (interleave + (hxc-pokenames rom) + (map + (comp + format-evo + (partial take-while (comp not zero?)) + #(drop % rom)) + (hxc-ptrs-evolve rom) + ))))) + +(defn hxc-evolution-pretty + "Like hxc-evolution, except it uses the names of items and pokemon +--- grabbed from ROM --- rather than their numerical identifiers." + ([] (hxc-evolution-pretty com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let + [poke-names (vec (hxc-pokenames rom)) + item-names (vec (hxc-items rom)) + use-names + (fn [m] + (loop [ks (keys m) new-map m] + (let [k (first ks)] + (cond (nil? ks) new-map + (= k :into) + (recur + (next ks) + (assoc new-map + :into + (poke-names + (:into + new-map)))) + (= k :item) + (recur + (next ks) + (assoc new-map + :item + (item-names + (:item new-map)))) + :else + (recur + (next ks) + new-map) + ))))] + + (into {} + (map (fn [[pkmn evo-coll]] + [pkmn (map use-names evo-coll)]) + (hxc-evolution rom)))))) + + +(defn hxc-pokemon-base + ([] (hxc-pokemon-base com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [entry-size 28 + pkmn-count (count (hxc-pokedex-text rom)) + pokemon (rest (hxc-pokedex-names)) + types (apply assoc {} + (interleave + (range) + pkmn-types)) ;;!! softcoded + moves (apply assoc {} + (interleave + (range) + (map format-name + (hxc-move-names rom)))) + machines (hxc-machines) + ] + (zipmap + pokemon + (map + (fn [[n + rating-hp + rating-atk + rating-def + rating-speed + rating-special + type-1 + type-2 + rarity + rating-xp + pic-dimensions ;; tile_width|tile_height (8px/tile) + ptr-pic-obverse-1 + ptr-pic-obverse-2 + ptr-pic-reverse-1 + ptr-pic-reverse-2 + move-1 + move-2 + move-3 + move-4 + growth-rate + & + TMs|HMs]] + (let + [base-moves + (mapv moves + ((comp + ;; since the game uses zero as a delimiter, + ;; it must also increment all move indices by 1. + ;; heren we decrement to correct this. + (partial map dec) + (partial take-while (comp not zero?))) + [move-1 move-2 move-3 move-4])) + + types + (set (list (types type-1) + (types type-2))) + TMs|HMs + (map + (comp + (partial map first) + (partial remove (comp zero? second))) + (split-at + 50 + (map vector + (rest(range)) + (reduce concat + (map + #(take 8 + (concat (bit-list %) + (repeat 0))) + + TMs|HMs))))) + + TMs (vec (first TMs|HMs)) + HMs (take 5 (map (partial + -50) (vec (second TMs|HMs)))) + + + ] + + + {:dex# n + :base-moves base-moves + :types types + :TMs TMs + :HMs HMs + :base-hp rating-hp + :base-atk rating-atk + :base-def rating-def + :base-speed rating-speed + :base-special rating-special + :o0 pic-dimensions + :o1 ptr-pic-obverse-1 + :o2 ptr-pic-obverse-2 + })) + + (partition entry-size + (take (* entry-size pkmn-count) + (drop 0x383DE + rom)))))))) + + +(defn hxc-intro-pkmn + "The hardcoded pokemon to display in Prof. Oak's introduction; the pokemon's +internal id is stored at ROM@5EDB." + ([] (hxc-intro-pkmn + com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (nth (hxc-pokenames rom) (nth rom 0x5EDB)))) + +(defn sxc-intro-pkmn! + "Set the hardcoded pokemon to display in Prof. Oak's introduction." + [pokemon] + (write-rom! + (rewrite-rom 0x5EDB + [ + (inc + ((zipmap + (hxc-pokenames) + (range)) + pokemon))]))) + + +(defn hxc-item-prices + "The hardcoded list of item prices in memory. List begins at ROM@4495" + ([] (hxc-item-prices com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [items (hxc-items rom) + price-size 3] + (zipmap items + (map (comp + ;; zero-cost items are "priceless" + #(if (zero? %) :priceless %) + decode-bcd butlast) + (partition price-size + (take (* price-size (count items)) + (drop 0x4495 rom)))))))) + +(defn hxc-shops + ([] (hxc-shops com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [items (zipmap (range) (hxc-items rom)) + + ;; temporarily softcode the TM items + items (into + items + (map (juxt identity + (comp keyword + (partial str "tm-") + (partial + 1 -200) + )) + (take 200 (drop 200 (range))))) + + ] + + ((fn parse-shop [coll [num-items & items-etc]] + (let [inventory (take-while + (partial not= 0xFF) + items-etc) + [separator & items-etc] (drop num-items (rest items-etc))] + (if (= separator 0x50) + (map (partial mapv (comp items dec)) (conj coll inventory)) + (recur (conj coll inventory) items-etc) + ) + )) + + '() + (drop 0x233C rom)) + + + ))) + + + + + +(defn hxc-ptrs-wild + "A list of the hardcoded wild encounter data in memory. Pointers + begin at ROM@0CB95; data begins at ROM@0x04D89" + ([] (hxc-ptrs-wild com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [ptrs + (map (fn [[a b]] (+ a (* 0x100 b))) + (take-while (partial not= (list 0xFF 0xFF)) + (partition 2 (drop 0xCB95 rom))))] + ptrs))) + + + +(defn hxc-wilds + "A list of the hardcoded wild encounter data in memory. Pointers + begin at ROM@0CB95; data begins at ROM@0x04D89" + ([] (hxc-wilds com.aurellem.gb.gb-driver/original-rom)) + ([rom] + (let [pokenames (zipmap (range) (hxc-pokenames rom))] + (map + (partial map (fn [[a b]] {:species (pokenames (dec b)) :level + a})) + (partition 10 + + (take-while (comp (partial not= 1) + first) + (partition 2 + (drop 0xCD8C rom)) + + )))))) + + + + + + + + + + + + + + +;; ********************** MANIPULATION FNS + + +(defn same-type + ([pkmn move] + (same-type + com.aurellem.gb.gb-driver/original-rom pkmn move)) + ([rom pkmn move] + (((comp :types (hxc-pokemon-base rom)) pkmn) + ((comp :type (hxc-move-data rom)) move)))) + + + + +(defn submap? + "Compares the two maps. Returns true if map-big has the same associations as map-small, otherwise false." + [map-small map-big] + (cond (empty? map-small) true + (and + (contains? map-big (ffirst map-small)) + (= (get map-big (ffirst map-small)) + (second (first map-small)))) + (recur (next map-small) map-big) + + :else false)) + + +(defn search-map [proto-map maps] + "Returns all the maps that make the same associations as proto-map." + (some (partial submap? proto-map) maps)) + +(defn filter-vals + "Returns a map consisting of all the pairs [key val] for + which (pred key) returns true." + [pred map] + (reduce (partial apply assoc) {} + (filter (fn [[k v]] (pred v)) map))) + + +(defn search-moves + "Returns a subcollection of all hardcoded moves with the + given attributes. Attributes consist of :name :power + :accuracy :pp :fx-id + (and also :fx-txt, but it contains the same information + as :fx-id)" + ([attribute-map] + (search-moves + com.aurellem.gb.gb-driver/original-rom attribute-map)) + ([rom attribute-map] + (filter-vals (partial submap? attribute-map) + (hxc-move-data rom)))) + + + + + +;; note: 0x2f31 contains the names "TM" "HM"? + +;; note for later: credits start at F1290 + +;; note: DADB hyper-potion-hp _ _ _ super-potion-hp _ _ _ potion-hp ?? + +;; note: DD4D spells out pokemon vital stat names ("speed", etc.) + +;; note: 1195C-6A says ABLE#NOT ABLE#, but so does 119C0-119CE. +;; The first instance is for Machines; the second, for stones. + +;; 0x251A (in indexable mem): image decompression routine seems to begin here. + + +(comment + +(def hxc-later + "Running this code produces, e.g. hardcoded names NPCs give +their pokemon. Will sort through it later." +(print (character-codes->str(take 10000 + (drop 0x71597 + (rom (root))))))) + +(let [dex + (partition-by #(= 0x50 %) + (take 2540 + (drop 0x40687 + (rom (root)))))] + (def dex dex) + (def hxc-species + (map character-codes->str + (take-nth 4 dex)))) +) + + +#+end_src +