More investigation into animation
This commit is contained in:
178
internal/data/has_action.go
Normal file
178
internal/data/has_action.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/emef/bitfield"
|
||||
|
||||
"code.ur.gs/lupine/ordoor/internal/util/asciiscan"
|
||||
)
|
||||
|
||||
// AnimAction represents an animation that is stored in WarHammer.ani
|
||||
type AnimAction int
|
||||
|
||||
// CharacterType represents one of the different types of character in the game.
|
||||
//
|
||||
// TODO: can we load the list of character types anywhere or is it hardcoded in
|
||||
// the original too?
|
||||
type CharacterType int
|
||||
|
||||
const (
|
||||
AnimActionNone AnimAction = 0
|
||||
AnimActionAnim AnimAction = 1
|
||||
AnimActionWalk AnimAction = 2
|
||||
AnimActionExplosion AnimAction = 3
|
||||
AnimActionProjectile AnimAction = 4
|
||||
AnimActionSmoke AnimAction = 5
|
||||
AnimActionStandingShoot AnimAction = 6
|
||||
AnimActionStandingDeath AnimAction = 7
|
||||
AnimActionPain AnimAction = 8
|
||||
AnimActionSpellFx1 AnimAction = 9
|
||||
AnimActionSpellFx2 AnimAction = 10
|
||||
AnimActionSpellFx3 AnimAction = 11
|
||||
AnimActionSpellFx4 AnimAction = 12
|
||||
AnimActionSpellFx5 AnimAction = 13
|
||||
AnimActionRun AnimAction = 14
|
||||
AnimActionCrouch AnimAction = 15
|
||||
AnimActionStand AnimAction = 16
|
||||
AnimActionStandingRead AnimAction = 17
|
||||
AnimActionStandingUnready AnimAction = 18
|
||||
AnimActionCrouchingReady AnimAction = 19
|
||||
AnimActionCrouchingUnready AnimAction = 20
|
||||
AnimActionCrouchingShoot AnimAction = 21
|
||||
AnimActionStandingGrenade AnimAction = 22
|
||||
AnimActionCrouchingGrenade AnimAction = 23
|
||||
AnimActionDrawMelee AnimAction = 24
|
||||
AnimActionSlash AnimAction = 25
|
||||
AnimActionStab AnimAction = 26
|
||||
AnimActionBlown AnimAction = 27
|
||||
AnimActionCrouchingDeath AnimAction = 28
|
||||
AnimActionJump AnimAction = 29
|
||||
AnimActionHeal AnimAction = 30
|
||||
AnimActionTechWork AnimAction = 31
|
||||
AnimActionCast AnimAction = 32
|
||||
AnimActionShoot AnimAction = 33
|
||||
AnimActionDeath AnimAction = 34
|
||||
AnimActionFromWarp AnimAction = 35
|
||||
|
||||
AnimActionStart = AnimActionNone
|
||||
AnimActionEnd = AnimActionFromWarp
|
||||
AnimActionCount = AnimActionEnd - AnimActionStart + 1
|
||||
|
||||
// FIXME: indexed from 1, very annoying
|
||||
CharacterTypeTactical CharacterType = 1
|
||||
CharacterTypeAssault CharacterType = 2
|
||||
CharacterTypeDevastator CharacterType = 3
|
||||
CharacterTypeTerminator CharacterType = 4
|
||||
CharacterTypeApothecary CharacterType = 5
|
||||
CharacterTypeTechmarine CharacterType = 6
|
||||
CharacterTypeChaplain CharacterType = 7
|
||||
CharacterTypeLibrarian CharacterType = 8
|
||||
CharacterTypeCaptain CharacterType = 9
|
||||
CharacterTypeChaosMarine CharacterType = 10
|
||||
CharacterTypeChaosLord CharacterType = 11
|
||||
CharacterTypeChaosChaplain CharacterType = 12
|
||||
CharacterTypeChaosSorcerer CharacterType = 13
|
||||
CharacterTypeChaosTerminator CharacterType = 14
|
||||
CharacterTypeKhorneBerserker CharacterType = 15
|
||||
CharacterTypeBloodThirster CharacterType = 16
|
||||
CharacterTypeBloodLetter CharacterType = 17
|
||||
CharacterTypeFleshHound CharacterType = 18
|
||||
CharacterTypeLordOfChange CharacterType = 19
|
||||
CharacterTypeFlamer CharacterType = 20
|
||||
CharacterTypePinkHorror CharacterType = 21
|
||||
CharacterTypeBlueHorror CharacterType = 22
|
||||
CharacterTypeChaosCultist CharacterType = 23
|
||||
|
||||
CharacterTypeStart = CharacterTypeTactical
|
||||
CharacterTypeEnd = CharacterTypeChaosCultist
|
||||
CharacterTypeCount = CharacterTypeEnd - CharacterTypeStart + 1
|
||||
)
|
||||
|
||||
// HasAction tells us whether a character has an animation or not.
|
||||
type HasAction struct {
|
||||
bits bitfield.BitField
|
||||
}
|
||||
|
||||
func LoadHasAction(filename string) (*HasAction, error) {
|
||||
scanner, err := asciiscan.New(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer scanner.Close()
|
||||
|
||||
out := &HasAction{
|
||||
bits: bitfield.New(int(CharacterTypeCount) * int(AnimActionCount)),
|
||||
}
|
||||
|
||||
// Reuse this for every loop
|
||||
var actions [AnimActionCount]bool
|
||||
ptrs := make([]*bool, len(actions))
|
||||
for i, _ := range actions {
|
||||
ptrs[i] = &actions[i]
|
||||
}
|
||||
|
||||
for c := CharacterTypeStart; c <= CharacterTypeEnd; c++ {
|
||||
if err := scanner.ConsumeBoolPtrs(ptrs...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for j, value := range actions {
|
||||
a := AnimActionStart + AnimAction(j)
|
||||
|
||||
out.set(c, a, value)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (h *HasAction) Check(c CharacterType, a AnimAction) bool {
|
||||
return h.bits.Test(h.offset(c, a))
|
||||
}
|
||||
|
||||
func (h *HasAction) offset(c CharacterType, a AnimAction) uint32 {
|
||||
// Best to view this as a 2D array with CharacterTypeCount * AnimActionCount elements
|
||||
i := uint32(c - CharacterTypeStart)
|
||||
j := uint32(a - AnimActionStart)
|
||||
|
||||
return (i * uint32(AnimActionCount)) + j
|
||||
}
|
||||
|
||||
func (h *HasAction) set(c CharacterType, a AnimAction, value bool) {
|
||||
if value {
|
||||
h.bits.Set(h.offset(c, a))
|
||||
} else {
|
||||
h.bits.Clear(h.offset(c, a))
|
||||
}
|
||||
}
|
||||
|
||||
// Actions returns the list of animations that a character type has
|
||||
func (h *HasAction) Actions(c CharacterType) []AnimAction {
|
||||
var out []AnimAction
|
||||
|
||||
for j := AnimActionStart; j < AnimActionCount; j++ {
|
||||
if h.Check(c, j) {
|
||||
out = append(out, j)
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (h *HasAction) Print() {
|
||||
fmt.Println(" Tac Ass Dev Term Apo Tech Chp Lib Cpt CMar CLrd CChp CSrc CTrm Kbz BTh BL FHnd LoC Flm PHr BHr Cult")
|
||||
for a := AnimActionStart; a <= AnimActionEnd; a++ {
|
||||
fmt.Printf("%.2d", int(a))
|
||||
for c := CharacterTypeStart; c <= CharacterTypeEnd; c++ {
|
||||
if h.Check(c, a) {
|
||||
fmt.Print(" x ")
|
||||
} else {
|
||||
fmt.Print(" ")
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
}
|
||||
}
|
73
internal/idx/idx.go
Normal file
73
internal/idx/idx.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// package idx parses the Idx/WarHammer.idx file.
|
||||
//
|
||||
// No, I don't know what it's for yet.
|
||||
package idx
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
//"fmt"
|
||||
"log"
|
||||
"os"
|
||||
//"strings"
|
||||
)
|
||||
|
||||
type Type1Record struct {
|
||||
Offset1 uint32 // Where the type2 for this type1 is to be found
|
||||
Unknown1 uint32 // ???
|
||||
Offset2 uint32 // Another offset? But what to?
|
||||
}
|
||||
|
||||
type Type2Record struct {
|
||||
Unknown1 [4]byte // ???
|
||||
Offset uint32 // Where the type3 for this type2 is to be found
|
||||
Unknown2 [4]byte // ??
|
||||
}
|
||||
|
||||
type Type3Record struct {
|
||||
First20 [20]byte
|
||||
Last78 [78]byte
|
||||
}
|
||||
|
||||
type Idx struct {
|
||||
Filename string
|
||||
|
||||
Type1Records [512]Type1Record // Experimentally, there seem to be 512 of these
|
||||
Type2Records []Type2Record
|
||||
Type3Records []Type3Record
|
||||
}
|
||||
|
||||
func Load(filename string) (*Idx, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
out := &Idx{Filename: filename}
|
||||
|
||||
if err := binary.Read(f, binary.LittleEndian, &out.Type1Records); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, rec := range out.Type1Records {
|
||||
var off1diff uint32
|
||||
var off2diff uint32
|
||||
|
||||
if i > 0 && rec.Offset1 > 0 {
|
||||
lastRec := out.Type1Records[i-1]
|
||||
off1diff = rec.Offset1 - lastRec.Offset1
|
||||
off2diff = rec.Offset2 - lastRec.Offset2
|
||||
}
|
||||
|
||||
log.Printf(
|
||||
"%.3d: 0x%.6x diff=%.6d %.4d 0x%.6x diff=%.6d",
|
||||
i, rec.Offset1, off1diff, rec.Unknown1, rec.Offset2, off2diff,
|
||||
// i,
|
||||
// rec.Unknown1[0], rec.Unknown1[1], rec.Unknown1[2], rec.Unknown1[3],
|
||||
// rec.Unknown1[4], rec.Unknown1[5], rec.Unknown1[6], rec.Unknown1[7],
|
||||
)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
@@ -201,12 +201,18 @@ func loadProperties(menu *Menu, scanner *asciiscan.Scanner) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// DeBrief.mnu misspells these
|
||||
parts := strings.SplitN(strings.ToUpper(k), " ", 3)
|
||||
if len(parts) > 2 {
|
||||
k = strings.Join(parts[0:2], " ")
|
||||
}
|
||||
|
||||
switch strings.ToUpper(k) {
|
||||
case "BACKGROUND COLOR 0..255..-1 TRANS":
|
||||
case "BACKGROUND COLOR":
|
||||
menu.BackgroundColor = data.ColorPalette[vInt]
|
||||
case "HYPERTEXT COLOR 0..255":
|
||||
case "HYPERTEXT COLOR":
|
||||
menu.HypertextColor = data.ColorPalette[vInt]
|
||||
case "FONT TYPE 0..5":
|
||||
case "FONT TYPE":
|
||||
menu.FontType = vInt
|
||||
default:
|
||||
return fmt.Errorf("Unhandled menu property in %v: %q=%q", menu.Name, k, v)
|
||||
|
@@ -123,6 +123,15 @@ func (s *Scanner) ConsumeInt() (int, error) {
|
||||
return strconv.Atoi(str)
|
||||
}
|
||||
|
||||
func (s *Scanner) ConsumeBool() (bool, error) {
|
||||
integer, err := s.ConsumeInt()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return (integer > 0), nil
|
||||
}
|
||||
|
||||
// Reads a list of non-property lines, skipping any that match the given strings
|
||||
func (s *Scanner) ConsumeStringList(skip ...string) ([]string, error) {
|
||||
skipper := make(map[string]bool, len(skip))
|
||||
@@ -166,6 +175,16 @@ func (s *Scanner) ConsumeIntPtr(to *int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Scanner) ConsumeBoolPtr(to *bool) error {
|
||||
val, err := s.ConsumeBool()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*to = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Scanner) ConsumeIntPtrs(ptrs ...*int) error {
|
||||
for _, ptr := range ptrs {
|
||||
if err := s.ConsumeIntPtr(ptr); err != nil {
|
||||
@@ -175,3 +194,13 @@ func (s *Scanner) ConsumeIntPtrs(ptrs ...*int) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Scanner) ConsumeBoolPtrs(ptrs ...*bool) error {
|
||||
for _, ptr := range ptrs {
|
||||
if err := s.ConsumeBoolPtr(ptr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user