Some more character investigations

This commit is contained in:
2020-06-11 02:54:57 +01:00
parent cf624cc77b
commit f971ba320c
3 changed files with 149 additions and 11 deletions

View File

@@ -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? |
| 26 | 2 | ??? - invariant `00 04` |
| 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? |
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
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 |
| ----- | ---- | ------- |
@@ -633,6 +705,8 @@ The "trailer trailer", for want of a better term, seems to be organised as:
| 255 | 2048 | Briefing |
| 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

View File

@@ -102,5 +102,21 @@ func (m *Map) SpritesForCell(x, y, z int) ([]*Sprite, error) {
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
}

View File

@@ -67,7 +67,8 @@ type GameMap struct {
TrailerUnknown3 int `struc:"uint16"`
TrailerUnknown4 int `struc:"uint32"`
NumThingies int `struc:"uint32"`
NumThingies int `struc:"byte"`
TrailerUnknown5 []byte `struc:"[3]byte"`
Padding1 []byte `struc:"[20]byte"`
// FIXME: The rest is trash until Character & Thingy are worked out
@@ -79,7 +80,7 @@ type GameMap struct {
Briefing string `struc:"[2048]byte"`
// Maybe? each contains either 0 or 1? Hard to say
TrailerUnknown5 []byte `struc:"[85]byte"`
TrailerUnknown6 []byte `struc:"[85]byte"`
}
type Cell struct {
@@ -98,7 +99,29 @@ type Cell 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
}
@@ -289,13 +312,38 @@ func loadMapFile(filename string) (*GameMap, error) {
}
// Trim any trailing nulls off of the strings
trimRight(&out.SetName)
trimRight(&out.Title)
trimRight(&out.Briefing)
nullTerminate(&out.SetName)
nullTerminate(&out.Title)
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
}
func trimRight(s *string) {
*s = strings.TrimRight(*s, "\x00")
func nullTerminate(s *string) {
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,
)
}