This page will serve as an in-depth guide to a pet's appearance genetics, known internally as the Looks Chromosome. These traits can be viewed in a human-friendly way via my tool, GenePoolz, but if you're looking to actually edit said traits, this guide will give you the knowledge to do so.
The Looks Chromosome
The Looks Chromosome is a data structure that makes up one part of a pet's genome.
You can identify the beginning of the genome via the following byte pattern:
That's the start of the first Sprite Chromosome. Currently not sure what that does yet, so it will be excluded from this guide. Starting from the 0x29, move forward 957 bytes and we will find the first Looks Chromosome.
This is the layout:
Coat Color 1
Coat Color 2
Coat Color 3
Coat Color 4
Coat Color 5
Marking Factor 2
Marking Factor 1
NOTE: Marking Factor ordering is 2 first, then 1.
Self-explanatory. In the Looks Chromosome, breed ID is the value used to identify body parts.
This is how overwrite breeding works.
These affect various sizes of your pet, and it's the mechanic behind so-called "minis" (low default scale). A Chihuahua with a Default Scale of 100 will look fine, but a Great Dane with that same scale will appear tiny due to the fact that their LNZ was intended for a larger scale. Likewise, a Chihuahua with a Default Scale of 150 will appear huge and bug-eyed, because their LNZ is being stretched to unintended proportions.
Each pet has two "slots" for potential markings. These include things such as dalmatian spots and mutt patches. Like body parts, markings are denoted by breed ID. The Marking Factor controls how much of the marking is showing (fun fact, you can put a [Marking Factor] section in a breedfile LNZ with a number between 0-100, and all pets that come out of the AC will have that marking factor). A low marking factor causes things like spotlacking dalmatians. Because there are only two slots, a dog CANNOT simultaneously display a combination such as mutt markings, dalmatian spots, and a dane face patch without brexing.
Pets carry five coat colors, one eye color, and one eyelid color per genetic set. The order of the coat colors dictates the order in which those colors will present themselves. For example, color 1 is the base coat color, while color 2 might be the color of the dog's halfie mark/poodle socks/etc. These correspond to the "FurColorTrait" values from the [Fur Color Areas] section in the breed's LNZ.
In addition, only certain colors can pass down for each color category.
However, the genome does NOT use LNZ numbers for color; rather, they have their own numbering system.
I'll try to make this organized, just know that I'm a programmer, not a web designer. (lol)
0 - 1
2 - 4
5 - 8
9 - 10
11 - 13
14 - 15
16 - 17
1 - 10
73 - 82
93 - 102
83 - 92
53 - 62
21 - 30
43 - 52
31 - 41
12 - 19
When a pet is generated via the AC, it comes with its own personal genetic thumbprint. In my screenshot above, it's the 0x69EE next to all of the body parts and markings. This is a randomly generated number, known internally as a "seed", that controls what combination of variations the pet has for its breed. For example, while a dalmatian can have many possible spots coded in the breedfile, this value controls which combination of spots an individual dalmatian will have. This allows the dalmatian to pass on its exact spot pattern genetically, and it never mutates. The same rules apply to things like sheepdog tails, meaning the seed dictates whether a sheepdog will have a poof tail or a bob tail, and will only pass that variation on genetically. Although Petz will only generate seeds between 0 and 32,767 inclusive (a signed 2-byte integer), it will actually accept seeds all the way up to 2,147,483,647 inclusive (a signed 4-byte integer). Any seeds higher than that will be forcibly set to 1. The downside is that a breed can only have a maximum of 2,147,483,647 different combinations of variations, so if your dream was to bring all 309 septillion possible dalmatians together for one big jamboree, I'm afraid that's not going to happen.
Petz uses MSVCRT.dll's rand() and srand() functions for its RNG. Here's how the seed process works:
The seed is generated via a call to rand(). A copy of it is stored in a save place, and then the value is incremented. We then seed the generator via srand() with our incremented seed value, then generate 26 random numbers -- one for each letter of the alphabet -- and store them in memory sequentially, with the last number generated being first in line. This is for later use with linked variations. Afterwards, we re-seed the generator with our original seed value and begin to calculate variations.
For normal variations
When a normal variation block is encountered, we generate a new random number (remember, our generator has been seeded with the seed value, so any subsequent calls to rand() will return the same sequence of numbers). This number is bitshifted to the right by 2, then modulo'd by the number of variations in the current block and incremented by 1. This ensures we will always end with a value between 1 and however many variations are in the current block. Whatever number this comes out to is the variation the pet will have.
For linked variations
When a linked variation block is encountered, we take the letter and determine which number in the alphabet it is. A = 0, Z = 25. We fetch the corresponding seed for that letter (remember, we generated a table of them during setup), do seed mod variation, and increment the result. Whatever number this comes out to is the variation the pet will have.