Some more character investigations
This commit is contained in:
@@ -598,7 +598,8 @@ Ignoring them for now, here's a first guess at a header:
|
|||||||
| 24 | 2 | ??? - varies. Seems related to character/squad position? |
|
| 24 | 2 | ??? - varies. Seems related to character/squad position? |
|
||||||
| 26 | 2 | ??? - invariant `00 04` |
|
| 26 | 2 | ??? - invariant `00 04` |
|
||||||
| 28 | 4 | ??? - varies (0 vs 5) |
|
| 28 | 4 | ??? - varies (0 vs 5) |
|
||||||
| 32 | 4 | Number of thingies (26 vs 1) |
|
| 32 | 1 | Number of thingies |
|
||||||
|
| 33 | 3 | ???. With a Lord of Change on the map, only one byte works as thingies count |
|
||||||
| 36 | 20 | Padding? |
|
| 36 | 20 | Padding? |
|
||||||
|
|
||||||
56 bytes of data is interesting because the value of that first, ignored byte is
|
56 bytes of data is interesting because the value of that first, ignored byte is
|
||||||
@@ -625,7 +626,78 @@ Generating a map with no characters at all, the trailer is 2,447 bytes, and the
|
|||||||
mission title starts at 0x3B (59). So we can say we have 20 bytes of padding as
|
mission title starts at 0x3B (59). So we can say we have 20 bytes of padding as
|
||||||
a first approximation?
|
a first approximation?
|
||||||
|
|
||||||
The "trailer trailer", for want of a better term, seems to be organised as:
|
Here's where we're at with the per-character data, going from the padding values
|
||||||
|
suggested above:
|
||||||
|
|
||||||
|
| Offset | Size | Meaning |
|
||||||
|
| ------ | ---- | ------- |
|
||||||
|
| 0 | 179 | ??? |
|
||||||
|
| 179 | 80 | Character name |
|
||||||
|
| 259 | 10 | Character attributes |
|
||||||
|
| 269 | 495 | ??? |
|
||||||
|
| 764 | 1(?) | Squad number? |
|
||||||
|
| 765 | 927 | ??? |
|
||||||
|
|
||||||
|
There's still a lot of bytes to dig through, but this allows me to load the
|
||||||
|
character names from Chapter01 correctly, with the exception of record 57 which
|
||||||
|
just contains `\x02` and is then null-terminated all the way through. Looking
|
||||||
|
at the bytes for one character record, I can easily correlate certain bytes to
|
||||||
|
various attributes; that's just done in code for the moment.
|
||||||
|
|
||||||
|
Given two characters of the same time, just in different locations, differing
|
||||||
|
values are seen at:
|
||||||
|
|
||||||
|
* `0x103 - 0x10c` (hodgepodge)
|
||||||
|
* `0x178 - 0x1be` (hodgepodge)
|
||||||
|
* `0x2fc` (0, 1) - squad number?
|
||||||
|
|
||||||
|
In Chapter01, picking a random character (Gorgon) and looking at his squadmates,
|
||||||
|
they are all in the same squad, and no other characters are in that squad, so it
|
||||||
|
looks pretty diagnostic to me. There's nothing in the UI to indicate the squad,
|
||||||
|
though.
|
||||||
|
|
||||||
|
Now let's look for position. In my 2-character map, they're at 65,50 and 70,55.
|
||||||
|
Within a character, I see those numbers repeated twice - around `0x1b{9,a}` and
|
||||||
|
`0x1b{d,e}`.
|
||||||
|
|
||||||
|
Characters don't seem to take up multiple x,y squares, but they *can* take up
|
||||||
|
multiple Z levels. So maybe this is a bounding box of some kind?
|
||||||
|
|
||||||
|
Adding a (tall) Lord of Change to the map gave me `02 36 45 00 02 37 45`, which
|
||||||
|
doesn't quite match what my eyes are telling me for Z,Y,X. In addition, the data
|
||||||
|
immediately after this offset changed into a large number of coordinate-like
|
||||||
|
sets of values - far too many for it to actually be a bounding box. However, the
|
||||||
|
first one remains good as a position specifier.
|
||||||
|
|
||||||
|
Down in `0x679` (Chaos Sorcerer) or `0x68D` (Lord of Change), the map coords for
|
||||||
|
the *other* character appears, which is downright odd. For now, just use the
|
||||||
|
first-indexed value.
|
||||||
|
|
||||||
|
How about their types? We need to be able to work out which animations to show,
|
||||||
|
so they must be uniquely identified somehow. Ultramarine captain is animation
|
||||||
|
group 12, while chaos lord is group... 14? Not certain. I don't see either of
|
||||||
|
those numbers in a promising spot, anyway.
|
||||||
|
|
||||||
|
|
||||||
|
I do see differences at around `0x170`:
|
||||||
|
|
||||||
|
```
|
||||||
|
Ultramarine Captain: 00 00 00 00 00 00 00 00 50 03 00 00 00 00 00 00
|
||||||
|
Chaos Lord: 00 00 00 00 11 01 00 00 47 03 00 04 00 00 02 00
|
||||||
|
```
|
||||||
|
|
||||||
|
Maybe they're somewhat relevant, it's hard to say.
|
||||||
|
|
||||||
|
Thingies next: these aren't decoded at all yet, and the sizes seem to be
|
||||||
|
variable.
|
||||||
|
|
||||||
|
| Offset | Size | Meaning |
|
||||||
|
| ------ | ---- | ------- |
|
||||||
|
| | | |
|
||||||
|
|
||||||
|
|
||||||
|
Finally, the "trailer trailer", for want of a better term, seems to be organised
|
||||||
|
as:
|
||||||
|
|
||||||
| Offset | Size | Meaning |
|
| Offset | Size | Meaning |
|
||||||
| ----- | ---- | ------- |
|
| ----- | ---- | ------- |
|
||||||
@@ -633,6 +705,8 @@ The "trailer trailer", for want of a better term, seems to be organised as:
|
|||||||
| 255 | 2048 | Briefing |
|
| 255 | 2048 | Briefing |
|
||||||
| 2304 | 85 | ??? - each byte is 1 or 0. Spaced so it may be partly uint32 |
|
| 2304 | 85 | ??? - each byte is 1 or 0. Spaced so it may be partly uint32 |
|
||||||
|
|
||||||
|
This duplicates the information found in the `.TXT` files. No idea what the end
|
||||||
|
data is yet.
|
||||||
|
|
||||||
## Soldiers At War
|
## Soldiers At War
|
||||||
|
|
||||||
|
@@ -102,5 +102,21 @@ func (m *Map) SpritesForCell(x, y, z int) ([]*Sprite, error) {
|
|||||||
sprites = append(sprites, sprite)
|
sprites = append(sprites, sprite)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: this just marks character positions with sprite 19 for now.
|
||||||
|
specialsObj, err := m.assets.Object("specials")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
chrSpr, err := specialsObj.Sprite(19)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, chr := range m.raw.Characters {
|
||||||
|
if chr.XPos == x && chr.YPos == y && z == 1 { // FIXME: sort out ZPos
|
||||||
|
sprites = append(sprites, chrSpr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return sprites, nil
|
return sprites, nil
|
||||||
}
|
}
|
||||||
|
@@ -67,8 +67,9 @@ type GameMap struct {
|
|||||||
TrailerUnknown3 int `struc:"uint16"`
|
TrailerUnknown3 int `struc:"uint16"`
|
||||||
TrailerUnknown4 int `struc:"uint32"`
|
TrailerUnknown4 int `struc:"uint32"`
|
||||||
|
|
||||||
NumThingies int `struc:"uint32"`
|
NumThingies int `struc:"byte"`
|
||||||
Padding1 []byte `struc:"[20]byte"`
|
TrailerUnknown5 []byte `struc:"[3]byte"`
|
||||||
|
Padding1 []byte `struc:"[20]byte"`
|
||||||
|
|
||||||
// FIXME: The rest is trash until Character & Thingy are worked out
|
// FIXME: The rest is trash until Character & Thingy are worked out
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@ type GameMap struct {
|
|||||||
Briefing string `struc:"[2048]byte"`
|
Briefing string `struc:"[2048]byte"`
|
||||||
|
|
||||||
// Maybe? each contains either 0 or 1? Hard to say
|
// Maybe? each contains either 0 or 1? Hard to say
|
||||||
TrailerUnknown5 []byte `struc:"[85]byte"`
|
TrailerUnknown6 []byte `struc:"[85]byte"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Cell struct {
|
type Cell struct {
|
||||||
@@ -98,7 +99,29 @@ type Cell struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Character struct {
|
type Character struct {
|
||||||
Unknown1 int `struc:"uint32"`
|
Unknown1 []byte `struc:"[179]byte"`
|
||||||
|
Name string `struc:"[80]byte"`
|
||||||
|
|
||||||
|
// Attributes guessed by matching up numbers. Starts at 0x103
|
||||||
|
WeaponSkill int `struc:"byte"`
|
||||||
|
BallisticSkill int `struc:"byte"`
|
||||||
|
Unknown2 byte `struc:"byte"`
|
||||||
|
Leadership int `struc:"byte"`
|
||||||
|
Toughness int `struc:"byte"`
|
||||||
|
Strength int `struc:"byte"`
|
||||||
|
ActionPoints int `struc:"byte"`
|
||||||
|
Unknown3 byte `struc:"byte"`
|
||||||
|
Unknown4 byte `struc:"byte"`
|
||||||
|
Health int `struc:"byte"`
|
||||||
|
|
||||||
|
Unknown5 []byte `struc:"[91]byte"`
|
||||||
|
Armor int `struc:"byte"`
|
||||||
|
Unknown6 []byte `struc:"[84]byte"`
|
||||||
|
YPos int `struc:"byte"` // These are actually much more complicated
|
||||||
|
XPos int `struc:"byte"`
|
||||||
|
Unknown7 []byte `struc:"[317]byte"`
|
||||||
|
SquadNumber byte `struc:"byte"`
|
||||||
|
Unknown8 []byte `struc:"[927]byte"`
|
||||||
// TODO: each character may have a fixed number of subrecords for inventory
|
// TODO: each character may have a fixed number of subrecords for inventory
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,13 +312,38 @@ func loadMapFile(filename string) (*GameMap, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Trim any trailing nulls off of the strings
|
// Trim any trailing nulls off of the strings
|
||||||
trimRight(&out.SetName)
|
nullTerminate(&out.SetName)
|
||||||
trimRight(&out.Title)
|
nullTerminate(&out.Title)
|
||||||
trimRight(&out.Briefing)
|
nullTerminate(&out.Briefing)
|
||||||
|
|
||||||
|
for i, chr := range out.Characters {
|
||||||
|
nullTerminate(&chr.Name)
|
||||||
|
fmt.Printf("Character %v: %s\n", i, chr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Mission Title: %q\n", out.Title)
|
||||||
|
fmt.Printf("Mission Briefing: %q\n", out.Briefing)
|
||||||
|
|
||||||
return &out, nil
|
return &out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimRight(s *string) {
|
func nullTerminate(s *string) {
|
||||||
*s = strings.TrimRight(*s, "\x00")
|
sCpy := *s
|
||||||
|
idx := strings.Index(sCpy, "\x00")
|
||||||
|
if idx < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
*s = sCpy[0:idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Character) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"squad=%v pos=(%v,%v) name=%q\n\t%3d %3d %3d %3d %3d\n\t%3d %3d ??? ??? %3d\n",
|
||||||
|
c.SquadNumber,
|
||||||
|
c.XPos, c.YPos,
|
||||||
|
c.Name,
|
||||||
|
c.ActionPoints, c.Health, c.Armor, c.BallisticSkill, c.WeaponSkill,
|
||||||
|
c.Strength, c.Toughness /*c.Initiative, c.Attacks,*/, c.Leadership,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user