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("") } }