public void DoAnimCommands(SSAnimation ssAnim, int changing) { if (EngineWorld.AnimCommands.Length == 0 || ssAnim.Model == null) { return; // If no anim commands } var af = ssAnim.Model.Animations[(int) ssAnim.CurrentAnimation]; if (af.NumAnimCommands.IsBetween(0, 255, IB.aEbI)) { Assert(af.AnimCommand < EngineWorld.AnimCommands.Length); unsafe { fixed (short* tmp = &EngineWorld.AnimCommands[(int)af.AnimCommand]) { fixed (short* back = &EngineWorld.AnimCommands[EngineWorld.AnimCommands.Length - 1]) { var pointer = tmp; for (uint count = 0; count < af.NumAnimCommands; count++) { Assert(pointer <= back); var command = *pointer; ++pointer; switch ((TR_ANIMCOMMAND) command) { case TR_ANIMCOMMAND.SetPosition: // This command executes ONLY at the end of animation. pointer += 3; // Parse through 3 operands. break; case TR_ANIMCOMMAND.JumpDistance: // This command executes ONLY at the end of animation. pointer += 2; // Parse through 2 operands. break; case TR_ANIMCOMMAND.EmptyHands: // FIXME: Behaviour is yet to be discovered. break; case TR_ANIMCOMMAND.Kill: // This command executes ONLY at the end of animation. if (ssAnim.CurrentFrame == af.Frames.Count - 1) { Kill(); } break; case TR_ANIMCOMMAND.PlaySound: #if !NO_AUDIO if (ssAnim.CurrentFrame == pointer[0]) { var soundIndex = pointer[1] & 0x3FFF; // Quick workaround for TR3 quicksand. if (GetSubstanceState().IsAnyOf(Substance.QuicksandConsumed, Substance.QuicksandShallow)) { soundIndex = 18; } if (pointer[1].HasFlagSig(TR_ANIMCOMMAND_CONDITION.Water)) { if (GetSubstanceState() == Substance.WaterShallow) Audio.Send((uint)soundIndex, TR_AUDIO_EMITTER.Entity, (int)ID); } else if (pointer[1].HasFlagSig(TR_ANIMCOMMAND_CONDITION.Land)) { if (GetSubstanceState() != Substance.WaterShallow) Audio.Send((uint)soundIndex, TR_AUDIO_EMITTER.Entity, (int)ID); } else { Audio.Send((uint)soundIndex, TR_AUDIO_EMITTER.Entity, (int)ID); } } #endif pointer += 2; break; case TR_ANIMCOMMAND.PlayEffect: if (ssAnim.CurrentFrame == pointer[0]) { var effectID = pointer[1] & 0x3FFF; if (effectID > 0) EngineLua.ExecEffect(effectID, (int)ID); } pointer += 2; break; } } } } } } }
public void PerfomOnFrame(Character ent, SSAnimation ssAnim, ENTITY_ANIM state) { OnFrame(ent, ssAnim, state); }
public void AddOverrideAnim(int modelID) { var sm = EngineWorld.GetModelByID((uint)modelID); if(sm != null && sm.MeshCount == Bf.BoneTags.Count) { var ssAnim = new SSAnimation(); ssAnim.Model = sm; ssAnim.ClearOnFrame(); ssAnim.Next = Bf.Animations.Next; Bf.Animations.Next = ssAnim; // TODO: Useless assign to 0? ssAnim.FrameTime = 0.0f; ssAnim.NextState = TR_STATE.LaraWalkForward; ssAnim.Lerp = 0.0f; ssAnim.CurrentAnimation = TR_ANIMATION.LaraRun; ssAnim.CurrentFrame = 0; ssAnim.CurrentAnimation = TR_ANIMATION.LaraRun; ssAnim.NextFrame = 0; ssAnim.Period = 1.0f / TR_FRAME_RATE; } }
public void FromModel(SkeletalModel model) { HasSkin = false; BBMin = Vector3.Zero; BBMax = Vector3.Zero; Centre = Vector3.Zero; Position = Vector3.Zero; Animations = new SSAnimation(); Animations.Model = model; BoneTags.Resize(model.MeshCount, () => new SSBoneTag()); var stack = 0; var parents = new List<SSBoneTag>(); parents.Resize(BoneTags.Count, () => new SSBoneTag()); parents[0] = null; BoneTags[0].Parent = null; for (ushort i = 0; i < BoneTags.Count; i++) { BoneTags[i].Index = i; BoneTags[i].MeshBase = model.MeshTree[i].MeshBase; BoneTags[i].MeshSkin = model.MeshTree[i].MeshSkin; if (BoneTags[i].MeshSkin != null) HasSkin = true; BoneTags[i].MeshSlot = null; BoneTags[i].BodyPart = model.MeshTree[i].BodyPart; BoneTags[i].Offset = model.MeshTree[i].Offset; BoneTags[i].QRotate = new Quaternion(0, 0, 0, 0); BoneTags[i].Transform.SetIdentity(); BoneTags[i].FullTransform.SetIdentity(); if (i > 0) { BoneTags[i].Parent = BoneTags[i - 1]; if (model.MeshTree[i].Flag.HasFlagUns(0x01)) // POP { if (stack > 0) { BoneTags[i].Parent = parents[stack]; stack--; } } if (model.MeshTree[i].Flag.HasFlagUns(0x02)) // PUSH { if (stack + 1 < (short) model.MeshCount) { stack++; parents[stack] = BoneTags[i].Parent; } } } } }