View Single Post
  #1  
Old 05-10-2009, 05:53 PM
Shendare
Dragon
 
Join Date: Apr 2009
Location: California
Posts: 814
Cool SoF Spawn_Struct Hacking

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
Reply With Quote