I'm new to the PEQ scene, and would like to find ways to contribute.
I thought I might get my feet wet looking into the server code by trying to figure out some of the unknown fields in Spawn_Struct like haircolor, beardcolor, etc.
I found the place in SoF.cpp where such tinkering should go, inside ENCODE(OP_ZoneSpawns) starting around line 757, and knew there needed to be a better way to test things than coding values into random unknown fields and re-compiling for each test.
I put together a little bit of hack code that allows you to push test values into any of the unknown fields of the Spawn_Struct struct
at run-time using a test NPC's lastname.
Code:
//Hack Test for finding more fields in the Struct:
if (emu->lastName[0] == '*') // Test NPC!
{
char code = emu->lastName[1];
char* sep = (char*)memchr(&emu->lastName[2], '=', 10);
uint32 ofs;
uint32 val;
uint8 rnd = rand() & 0x0F;
if (sep == NULL)
{
ofs = 0;
if ((emu->lastName[2] < '0') || (emu->lastName[2] > '9'))
{
val = rnd;
}
else
{
val = atoi(&emu->lastName[2]);
}
}
else
{
sep[0] = NULL;
ofs = atoi(&emu->lastName[2]);
sep[0] = '=';
if ((sep[1] < '0') || (sep[1] > '9'))
{
val = rnd;
}
else
{
val = atoi(&sep[1]);
}
}
char hex[] = "0123456789ABCDEF";
size_t len = strlen(emu->lastName);
eq->lastName[len + 0] = ' ';
eq->lastName[len + 1] = code;
eq->lastName[len + 2] = '0' + ((ofs / 1000) % 10);
eq->lastName[len + 3] = '0' + ((ofs / 100) % 10);
eq->lastName[len + 4] = '0' + ((ofs / 10) % 10);
eq->lastName[len + 5] = '0' + (ofs % 10);
eq->lastName[len + 6] = '=';
eq->lastName[len + 7] = '0' + ((val / 100) % 10);
eq->lastName[len + 8] = '0' + ((val / 10) % 10);
eq->lastName[len + 9] = '0' + (val % 10);
eq->lastName[len + 10] = 0x00;
switch (code)
{
case 'a':
eq->unknown0001[ofs % 4] = val; break;
case 'b':
eq->unknown0006 = val; break;
case 'c':
eq->unknown0008 = val; break;
case 'd':
eq->unknown0011[ofs % 6] = val; break;
case 'e':
eq->unknown0009 = val; break;
case 'f':
eq->unknown0010[ofs % 4] = val; break;
case 'g':
eq->unknown0048[ofs % 4] = val; break;
case 'h':
eq->unknown0059 = val; break;
case 'i':
eq->unknown0074[ofs % 24] = val; break;
case 'j':
eq->unknown0077[ofs % 7] = val; break;
case 'k':
eq->unknown00771[ofs % 184] = val; break;
case 'l':
eq->unknown00772[ofs % 10] = val; break;
case 'm':
eq->unknown0079[ofs % 123] = val; break;
case 'n':
eq->unknown0080[ofs % 10] = val; break;
case 'o':
eq->unknown0106[ofs % 4] = val; break;
case 'p':
eq->unknown0107[ofs % 5] = val; break;
case 'q':
eq->unknown01071[ofs % 5] = val; break;
case 'r':
eq->unknown0108[ofs % 6] = val; break;
case 's':
eq->unknown01082[ofs % 6] = val; break;
case 't':
eq->unknown0110[ofs % 20] = val; break;
case 'u':
eq->unknown01101[ofs % 21] = val; break;
case 'v':
eq->unknown0154[ofs % 4] = val; break;
case 'w':
eq->unknown0155[ofs % 4] = val; break;
case 'x':
eq->unknown0156[ofs % 4] = val; break;
case 'y':
eq->unknown0157[ofs % 4] = val; break;
case 'z':
eq->unknown0158[ofs % 4] = val; break;
case 'A':
eq->unknown0159[ofs % 4] = val; break;
case 'B':
eq->unknown0160[ofs % 4] = val; break;
case 'C':
eq->unknown0161[ofs % 4] = val; break;
case 'D':
eq->unknown0162[ofs % 4] = val; break;
case 'E':
eq->unknown0163[ofs % 4] = val; break;
case 'F':
eq->unknown0263[ofs % 2] = val; break;
case 'G':
eq->unknown0281 = val; break;
case 'H':
eq->unknown0308[ofs % 4] = val; break;
case 'I':
eq->unknown0309[ofs % 11] = val; break;
case 'J':
eq->unknown442[ofs % 8] = val; break;
case 'K':
eq->unknown0760 = val; break;
case 'L':
eq->unknown0761 = val; break;
case 'M':
eq->unknown0762 = val; break;
case 'O':
eq->unknown0763 = val; break;
case 'P':
eq->unknown0764 = val; break;
case 'Q':
eq->unknown0496[ofs % 2] = val; break;
case 'X':
((uint8*)eq)[ofs % 897] = val; break;
case 'Z':
eq->size = (float)val; break; // Test w/ size.
}
To use the code, simply target any NPC in-game and change its last name to follow the special formula:
*(Code)[Optional-Offset=](Value)
I've been testing on Exterminator Valern just inside Felwithe, so I can target him and type "#npcedit lastname *Z2", then "#repop" (I've hotkeyed that one), and Valern will respawn with a size of 2, instead of his default 6!
You'll also get debug output tacked to the end of the lastname letting you know exactly what the hack-code interpreted from your formula. The debug output is in the format "(Code)(4-digit Offset)=(3-digit Value)". Because the Z code for size doesn't use an offset, the debug output for the previous example will be "Z0000=002".
If you want the code to pick a random value to put in the specified field, use any non-numeric character for the value. I generally use '?'. So, you could type "#npcedit lastname *Z?" and repop and the NPC will be a random size from 0-15 every time you repop.
For the code character, all alpha digits from a-z and A-Q map to specific sets of unknown fields in the Spawn_Struct. If you want to push the value 10 into unknown0110[3], for example, you would make the NPC's lastname "*t3=10" and repop, and there you go!
The real catch-all hack code is 'X'. If you specify X as the code character, you can push any value into any offset of Spawn_Struct. For example, looking in SoF_structs.h you see that gender is at offset 22 in the Spawn_Struct. If you specify "#npcedit lastname *X22=1", the NPC will repop as a female.
Anyway, I thought this might be useful for anyone else who feels like poking around in Spawn_Struct to try to figure out some of these many unknown fields as well!
- Shendare