Esempio n. 1
0
        private void picSlider_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            int targetFrame = XtoRealFrameNumber(e.X);

            if (targetFrame < 0)
            {
                targetFrame = 0;
            }

            // Try to find animcommand under cursor
            foreach (WadAnimCommand ac in Animation.WadAnimation.AnimCommands)
            {
                if (ac.FrameBased && ac.Parameter1 == targetFrame)
                {
                    AnimCommandDoubleClick?.Invoke(this, ac);
                    return;
                }
            }

            // No animcommand found, try to create new one
            WadAnimCommand newCommand = new WadAnimCommand()
            {
                Type = WadAnimCommandType.PlaySound, Parameter1 = (short)targetFrame
            };

            AnimCommandDoubleClick?.Invoke(this, newCommand);
        }
Esempio n. 2
0
 private void MoveCommand(bool down)
 {
     if (down)
     {
         foreach (DataGridViewRow row in gridViewCommands.SelectedRows)
         {
             int index = row.Index;
             if (index + 1 > _animCommands.Count - 1)
             {
                 continue;
             }
             WadAnimCommand cmd = _animCommands[index];
             _animCommands.RemoveAt(index);
             _animCommands.Insert(index + 1, cmd);
             gridViewCommands.Rows[index].Selected     = false;
             gridViewCommands.Rows[index + 1].Selected = true;
         }
     }
     else
     {
         foreach (DataGridViewRow row in gridViewCommands.SelectedRows)
         {
             int index = row.Index;
             if (index - 1 < 0)
             {
                 continue;
             }
             WadAnimCommand cmd = _animCommands[index];
             _animCommands.RemoveAt(index);
             _animCommands.Insert(index - 1, cmd);
             gridViewCommands.Rows[index].Selected     = false;
             gridViewCommands.Rows[index - 1].Selected = true;
         }
     }
 }
Esempio n. 3
0
 private void butCopy_Click(object sender, EventArgs e)
 {
     foreach (DataGridViewRow row in gridViewCommands.SelectedRows)
     {
         int            index   = row.Index;
         WadAnimCommand cmdCopy = _animCommands[index].Clone();
         _animCommands.Insert(index + 1, cmdCopy);
     }
 }
Esempio n. 4
0
 private void GridViewCommands_SelectionChanged(object sender, EventArgs e)
 {
     if (gridViewCommands.SelectedRows.Count == 1)
     {
         int            index = gridViewCommands.SelectedRows[0].Index;
         WadAnimCommand cmd   = _animCommands[index];
         animCommandEditor.Command = cmd;
     }
     else
     {
         return;
     }
 }
Esempio n. 5
0
 internal void SelectCommand(WadAnimCommand cmd)
 {
     foreach (DataGridViewRow row in gridViewCommands.Rows)
     {
         if (_animCommands[row.Index].Equals(cmd))
         {
             row.Selected = true;
         }
         else
         {
             row.Selected = false;
         }
     }
 }
Esempio n. 6
0
        private void butAddEffect_Click(object sender, EventArgs e)
        {
            WadAnimCommand newCmd = new WadAnimCommand()
            {
                Type = WadAnimCommandType.SetPosition
            };

            _animCommands.Add(newCmd);
            for (int i = 0; i < _animCommands.Count - 1; i++)
            {
                gridViewCommands.Rows[i].Selected = false;
            }
            gridViewCommands.Rows[_animCommands.Count - 1].Selected = true;
        }
Esempio n. 7
0
        public FormReplaceAnimCommands(AnimationEditor editor, WadAnimCommand refCommand = null)
        {
            InitializeComponent();

            _editor = editor;
            aceFind.Initialize(_editor, true);
            aceReplace.Initialize(_editor, true);
            aceFind.Command = refCommand == null ? new WadAnimCommand()
            {
                Type = WadAnimCommandType.SetPosition
            } : refCommand;
            aceReplace.Command = new WadAnimCommand()
            {
                Type = WadAnimCommandType.SetPosition
            };

            // Set window property handlers
            Configuration.LoadWindowProperties(this, _editor.Tool.Configuration);
            FormClosing += new FormClosingEventHandler((s, e) => Configuration.SaveWindowProperties(this, _editor.Tool.Configuration));

            UpdateUI();
        }
Esempio n. 8
0
        private void Search(bool newSearch = true)
        {
            // Reset previous search's state...
            dgvResults.Rows.Clear();

            // Store flipeffect settings in case user later changes them
            if (newSearch)
            {
                _backupCommand = aceFind.Command.Clone();
            }

            var collectedAnims = new List <int>();

            for (int i = 0; i < _editor.Animations.Count; i++)
            {
                var anim = _editor.Animations[i];

                foreach (var ac in anim.WadAnimation.AnimCommands)
                {
                    if (WadAnimCommand.DistinctiveEquals(ac, _backupCommand, false))
                    {
                        if (!collectedAnims.Contains(i))
                        {
                            collectedAnims.Add(i);
                        }

                        string result = "Animation: " + anim.WadAnimation.Name + ", command: " + ac;
                        dgvResults.Rows.Add(false, anim.Index, result);
                    }
                }
            }

            UpdateUI();
            if (newSearch)
            {
                statusLabel.Text = "Search finished. Found " + dgvResults.Rows.Count + " match" + (dgvResults.Rows.Count == 1 ? "" : "es") + " in " + collectedAnims.Count + " animation" + (collectedAnims.Count <= 1 ? "" : "s") + ".";
            }
        }
Esempio n. 9
0
        public void UpdateUI(WadAnimCommand cmd)
        {
            if (_currentlyDoingCommandSelection)
            {
                return;
            }

            if (cmd == null)
            {
                comboCommandType.Enabled = false;
                commandControls.Visible  = false;
                return;
            }
            else
            {
                comboCommandType.Enabled = true;
                commandControls.Visible  = true;
            }

            try
            {
                _currentlyDoingCommandSelection = true;

                comboCommandType.SelectedIndex = (int)(cmd.Type) - 1;

                switch (cmd.Type)
                {
                case WadAnimCommandType.SetPosition:
                    commandControls.Visible     = true;
                    commandControls.SelectedTab = tabSetPosition;

                    tbPosX.Value = cmd.Parameter1;
                    tbPosY.Value = cmd.Parameter2;
                    tbPosZ.Value = cmd.Parameter3;
                    break;

                case WadAnimCommandType.SetJumpDistance:
                    commandControls.Visible     = true;
                    commandControls.SelectedTab = tabSetJumpDistance;

                    tbHorizontal.Value = cmd.Parameter1;
                    tbVertical.Value   = cmd.Parameter2;
                    break;

                case WadAnimCommandType.EmptyHands:
                case WadAnimCommandType.KillEntity:
                    commandControls.Visible = false;
                    break;

                case WadAnimCommandType.PlaySound:
                    commandControls.Visible     = true;
                    commandControls.SelectedTab = tabPlaySound;

                    tbPlaySoundFrame.Value = cmd.Parameter1;
                    nudSoundId.Value       = cmd.Parameter2 & 0x3FFF;

                    switch (cmd.Parameter2 & 0xC000)
                    {
                    default:
                        comboPlaySoundConditions.SelectedIndex = 0;
                        break;

                    case 0x4000:
                        comboPlaySoundConditions.SelectedIndex = 1;
                        break;

                    case 0x8000:
                        comboPlaySoundConditions.SelectedIndex = 2;
                        break;
                    }
                    break;

                case WadAnimCommandType.FlipEffect:
                    commandControls.Visible     = true;
                    commandControls.SelectedTab = tabFlipeffect;

                    tbFlipEffectFrame.Value = cmd.Parameter1;
                    tbFlipEffect.Value      = cmd.Parameter2 & 0x3FFF;

                    switch (cmd.Parameter2 & 0xC000)
                    {
                    default:
                        comboFlipeffectConditions.SelectedIndex = 0;
                        break;

                    case 0x4000:
                        comboFlipeffectConditions.SelectedIndex = 1;
                        break;

                    case 0x8000:
                        comboFlipeffectConditions.SelectedIndex = 2;
                        break;
                    }
                    break;
                }
            }
            finally
            {
                _currentlyDoingCommandSelection = false;
            }
        }
Esempio n. 10
0
        private void ReplaceOrDelete(bool delete = false)
        {
            // Enlist all animations which are pending for replacement
            var animsToUndo = new List <AnimationNode>();

            for (int i = 0; i < dgvResults.Rows.Count; i++)
            {
                var process = (bool)dgvResults.Rows[i].Cells[0].Value;
                var number  = (int)dgvResults.Rows[i].Cells[1].Value;

                if (process && !animsToUndo.Any(anim => anim.Index == number))
                {
                    animsToUndo.Add(_editor.Animations[number]);
                }
            }

            // Undo
            _editor.Tool.UndoManager.PushAnimationChanged(_editor, animsToUndo);

            int  count        = 0;
            int  animCount    = 0;
            int  actionCount  = 0;
            bool alreadyFound = false;

            for (int i = 0; i < _editor.Animations.Count; i++)
            {
                // Collect animcommand indices to remove later in reverse order
                // with RemoveAt function.
                List <int> indicesToDelete = new List <int>();

                for (int j = 0; j < _editor.Animations[i].WadAnimation.AnimCommands.Count; j++)
                {
                    alreadyFound = false;

                    var ac = _editor.Animations[i].WadAnimation.AnimCommands[j];
                    if (WadAnimCommand.DistinctiveEquals(ac, _backupCommand, false))
                    {
                        if ((bool)dgvResults.Rows[count].Cells[0].Value == true)
                        {
                            if (!alreadyFound) // Increase counter for statistics
                            {
                                animCount++;
                                alreadyFound = true;
                            }

                            if (delete)
                            {
                                indicesToDelete.Add(j);
                            }
                            else
                            {
                                var preparedCommand = aceReplace.Command.Clone();

                                // Preserve frame number in frame-based animcommands
                                if (preparedCommand.FrameBased && ac.FrameBased)
                                {
                                    preparedCommand.Parameter1 = ac.Parameter1;
                                }

                                _editor.Animations[i].WadAnimation.AnimCommands[j] = preparedCommand;
                            }
                            actionCount++; // Increase counter for statistics
                        }
                        count++;
                    }
                }
                // Remove previously collected indices in reverse order.
                if (indicesToDelete.Count > 0)
                {
                    indicesToDelete
                    .OrderByDescending(a => a)
                    .ToList()
                    .ForEach(item => _editor.Animations[i].WadAnimation.AnimCommands.RemoveAt(item));
                }
            }

            UpdateUI();
            EditingWasDone = true;
            animsToUndo.ForEach(anim => _editor.Tool.AnimationEditorAnimationChanged(anim, false));

            if (delete)
            {
                statusLabel.Text = "Deleted " + actionCount + " animcommand" + (actionCount == 1 ? "" : "s") + " in " + animCount + " animation" + (animCount == 1 ? "" : "s") + ".";
            }
            else
            {
                statusLabel.Text = "Replacement finished. Made " + actionCount + " replacement" + (actionCount == 1 ? "" : "s") + " in " + animCount + " animation" + (animCount == 1 ? "" : "s") + ".";
            }

            // Run one more extra pass to show deselected results
            Search(false);
        }
Esempio n. 11
0
 public void ReplaceAnimCommands(WadAnimCommand oldCommand, WadAnimCommand newCommand) =>
 Animations.ForEach(a => a.WadAnimation.AnimCommands.ForEach(ac => { if (ac == oldCommand)
                                                                     {
                                                                         ac = newCommand.Clone();
                                                                     }
                                                             }));
Esempio n. 12
0
        internal static WadMoveable ConvertTr4MoveableToWadMoveable(Wad2 wad, Tr4Wad oldWad, int moveableIndex,
                                                                    Dictionary <int, WadTexture> textures)
        {
            wad_moveable oldMoveable = oldWad.Moveables[moveableIndex];
            var          newId       = new WadMoveableId(oldMoveable.ObjectID);

            // A workaround to find out duplicated item IDs produced by StrPix unpatched for v130 wads.
            // In such case, a legacy name is used to guess real item ID, if this fails - broken item is filtered out.
            if (wad.Moveables.ContainsKey(newId))
            {
                var message = "Duplicated moveable ID " + oldMoveable.ObjectID + " was identified while loading " + oldWad.BaseName + ". ";
                if (oldWad.LegacyNames.Count - oldWad.Statics.Count < moveableIndex)
                {
                    logger.Warn(message + "Can't restore real ID by name, name table is too short. Ignoring moveable.");
                    return(null);
                }

                bool isMoveable;
                var  guessedId = TrCatalog.GetItemIndex(TRVersion.Game.TR4, oldWad.LegacyNames[moveableIndex], out isMoveable);

                if (isMoveable && guessedId.HasValue)
                {
                    newId = new WadMoveableId(guessedId.Value);
                    if (wad.Moveables.ContainsKey(newId))
                    {
                        logger.Warn(message + "Can't restore real ID by name, name table is inconsistent. Ignoring moveable.");
                        return(null);
                    }
                    else
                    {
                        logger.Warn(message + "Successfully restored real ID by name.");
                    }
                }
                else
                {
                    logger.Warn(message + "Can't find provided name in catalog. Ignoring moveable.");
                    return(null);
                }
            }

            WadMoveable newMoveable = new WadMoveable(newId);
            var         frameBases  = new Dictionary <WadAnimation, ushort[]>();

            // Load meshes
            var meshes = new List <WadMesh>();

            for (int j = 0; j < oldMoveable.NumPointers; j++)
            {
                meshes.Add(ConvertTr4MeshToWadMesh(wad, oldWad, textures,
                                                   oldWad.Meshes[(int)oldWad.RealPointers[oldMoveable.PointerIndex + j]],
                                                   (int)oldMoveable.ObjectID));
            }

            // Build the skeleton
            var root = new WadBone();

            root.Name        = "bone_0_root";
            root.Parent      = null;
            root.Translation = Vector3.Zero;
            root.Mesh        = meshes[0];

            newMoveable.Bones.Add(root);

            for (int j = 0; j < oldMoveable.NumPointers - 1; j++)
            {
                WadBone bone = new WadBone();
                bone.Name        = "bone_" + (j + 1).ToString();
                bone.Parent      = null;
                bone.Translation = Vector3.Zero;
                bone.Mesh        = meshes[j + 1];
                newMoveable.Bones.Add(bone);
            }

            for (int mi = 0; mi < (oldMoveable.NumPointers - 1); mi++)
            {
                int j = mi + 1;

                var opcode = (WadLinkOpcode)oldWad.Links[(int)(oldMoveable.LinksIndex + mi * 4)];
                int linkX  = oldWad.Links[(int)(oldMoveable.LinksIndex + mi * 4) + 1];
                int linkY  = -oldWad.Links[(int)(oldMoveable.LinksIndex + mi * 4) + 2];
                int linkZ  = oldWad.Links[(int)(oldMoveable.LinksIndex + mi * 4) + 3];

                newMoveable.Bones[j].OpCode      = opcode;
                newMoveable.Bones[j].Translation = new Vector3(linkX, linkY, linkZ);
            }

            // Convert animations
            int numAnimations = 0;
            int nextMoveable  = oldWad.GetNextMoveableWithAnimations(moveableIndex);

            if (nextMoveable == -1)
            {
                numAnimations = oldWad.Animations.Count - oldMoveable.AnimationIndex;
            }
            else
            {
                numAnimations = oldWad.Moveables[nextMoveable].AnimationIndex - oldMoveable.AnimationIndex;
            }

            for (int j = 0; j < numAnimations; j++)
            {
                if (oldMoveable.AnimationIndex == -1)
                {
                    break;
                }

                wad_animation oldAnimation = oldWad.Animations[j + oldMoveable.AnimationIndex];
                WadAnimation  newAnimation = new WadAnimation();
                newAnimation.StateId       = oldAnimation.StateId;
                newAnimation.FrameRate     = oldAnimation.FrameDuration;
                newAnimation.NextAnimation = (ushort)(oldAnimation.NextAnimation - oldMoveable.AnimationIndex);
                newAnimation.NextFrame     = oldAnimation.NextFrame;
                newAnimation.Name          = TrCatalog.GetAnimationName(TRVersion.Game.TR4, oldMoveable.ObjectID, (uint)j);

                // Fix wadmerger/wad format bug with inverted frame start/end on single-frame anims
                ushort newFrameStart = oldAnimation.FrameStart < oldAnimation.FrameEnd ? oldAnimation.FrameStart : oldAnimation.FrameEnd;
                ushort newFrameEnd   = oldAnimation.FrameStart < oldAnimation.FrameEnd ? oldAnimation.FrameEnd : newFrameStart;
                newAnimation.EndFrame = (ushort)(newFrameEnd - newFrameStart);

                for (int k = 0; k < oldAnimation.NumStateChanges; k++)
                {
                    WadStateChange   sc    = new WadStateChange();
                    wad_state_change wadSc = oldWad.Changes[(int)oldAnimation.ChangesIndex + k];
                    sc.StateId = (ushort)wadSc.StateId;

                    for (int n = 0; n < wadSc.NumDispatches; n++)
                    {
                        WadAnimDispatch   ad    = new WadAnimDispatch();
                        wad_anim_dispatch wadAd = oldWad.Dispatches[(int)wadSc.DispatchesIndex + n];

                        ad.InFrame       = (ushort)(wadAd.Low - newFrameStart);
                        ad.OutFrame      = (ushort)(wadAd.High - newFrameStart);
                        ad.NextAnimation = (ushort)((wadAd.NextAnimation - oldMoveable.AnimationIndex) % numAnimations);
                        ad.NextFrame     = (ushort)wadAd.NextFrame;

                        sc.Dispatches.Add(ad);
                    }

                    newAnimation.StateChanges.Add(sc);
                }

                if (oldAnimation.NumCommands < oldWad.Commands.Count)
                {
                    int lastCommand = oldAnimation.CommandOffset;

                    for (int k = 0; k < oldAnimation.NumCommands; k++)
                    {
                        short commandType = oldWad.Commands[lastCommand];

                        WadAnimCommand command = new WadAnimCommand();
                        command.Type = (WadAnimCommandType)commandType;
                        switch (command.Type)
                        {
                        case WadAnimCommandType.SetPosition:
                            command.Parameter1 = (short)oldWad.Commands[lastCommand + 1];
                            command.Parameter2 = (short)oldWad.Commands[lastCommand + 2];
                            command.Parameter3 = (short)oldWad.Commands[lastCommand + 3];
                            lastCommand       += 4;
                            break;

                        case WadAnimCommandType.SetJumpDistance:
                            command.Parameter1 = (short)oldWad.Commands[lastCommand + 1];
                            command.Parameter2 = (short)oldWad.Commands[lastCommand + 2];
                            lastCommand       += 3;
                            break;

                        case WadAnimCommandType.EmptyHands:
                        case WadAnimCommandType.KillEntity:
                            lastCommand += 1;
                            break;

                        case WadAnimCommandType.PlaySound:
                        case WadAnimCommandType.FlipEffect:
                            command.Parameter1 = (short)(oldWad.Commands[lastCommand + 1] - newFrameStart);
                            command.Parameter2 = (short)oldWad.Commands[lastCommand + 2];

                            // For single-frame anims, clamp frame number to first frame (another fix for WM/wad format range inversion bug)
                            if (newAnimation.EndFrame == 0 && command.Parameter1 > 0)
                            {
                                command.Parameter1 = 0;
                            }

                            lastCommand += 3;
                            break;

                        default:     // Ignore invalid anim commands (see for example karnak.wad)
                            logger.Warn("Invalid anim command " + commandType);
                            goto ExitForLoop;
                        }

                        newAnimation.AnimCommands.Add(command);
                    }
ExitForLoop:
                    ;
                }

                int  frames    = (int)oldAnimation.KeyFrameOffset / 2;
                uint numFrames = 0;
                if (oldAnimation.KeyFrameSize != 0)
                {
                    if ((j + oldMoveable.AnimationIndex) == (oldWad.Animations.Count - 1))
                    {
                        numFrames = ((uint)(2 * oldWad.KeyFrames.Count) - oldAnimation.KeyFrameOffset) / (uint)(2 * oldAnimation.KeyFrameSize);
                    }
                    else
                    {
                        numFrames = (oldWad.Animations[oldMoveable.AnimationIndex + j + 1].KeyFrameOffset - oldAnimation.KeyFrameOffset) / (uint)(2 * oldAnimation.KeyFrameSize);
                    }
                }

                for (int f = 0; f < numFrames; f++)
                {
                    WadKeyFrame frame        = new WadKeyFrame();
                    int         startOfFrame = frames;

                    frame.BoundingBox = new BoundingBox(new Vector3(oldWad.KeyFrames[frames],
                                                                    -oldWad.KeyFrames[frames + 2],
                                                                    oldWad.KeyFrames[frames + 4]),
                                                        new Vector3(oldWad.KeyFrames[frames + 1],
                                                                    -oldWad.KeyFrames[frames + 3],
                                                                    oldWad.KeyFrames[frames + 5]));
                    frames += 6;

                    frame.Offset = new Vector3(oldWad.KeyFrames[frames],
                                               (short)(-oldWad.KeyFrames[frames + 1]),
                                               oldWad.KeyFrames[frames + 2]);
                    frames += 3;

                    for (int n = 0; n < oldMoveable.NumPointers; n++)
                    {
                        frame.Angles.Add(WadKeyFrameRotation.FromTrAngle(ref frames, oldWad.KeyFrames, false, true));
                    }
                    if ((frames - startOfFrame) < oldAnimation.KeyFrameSize)
                    {
                        frames += ((int)oldAnimation.KeyFrameSize - (frames - startOfFrame));
                    }

                    newAnimation.KeyFrames.Add(frame);
                }

                // New velocities
                float acceleration = oldAnimation.Accel / 65536.0f;
                newAnimation.StartVelocity = oldAnimation.Speed / 65536.0f;

                float lateralAcceleration = oldAnimation.AccelLateral / 65536.0f;
                newAnimation.StartLateralVelocity = oldAnimation.SpeedLateral / 65536.0f;

                if (newAnimation.KeyFrames.Count != 0 && newAnimation.FrameRate != 0)
                {
                    newAnimation.EndVelocity = newAnimation.StartVelocity + acceleration *
                                               (newAnimation.KeyFrames.Count - 1) * newAnimation.FrameRate;
                    newAnimation.EndLateralVelocity = newAnimation.StartLateralVelocity + lateralAcceleration *
                                                      (newAnimation.KeyFrames.Count - 1) * newAnimation.FrameRate;
                }
                else
                {
                    // Basic foolproofness for potentially broken animations
                    newAnimation.EndVelocity        = newAnimation.StartVelocity;
                    newAnimation.EndLateralVelocity = newAnimation.StartLateralVelocity;
                }

                // Deduce real maximum frame number, based on interpolation and keyframes.
                // We need to refer this value in NextFrame-related fixes (below) because of epic WadMerger bug,
                // which incorrectly calculates NextFrame and "steals" last frame from every custom animation.
                ushort maxFrameCount = (ushort)((newAnimation.FrameRate == 1 || numFrames <= 2) ? numFrames : ((numFrames - 1) * newAnimation.FrameRate) + 1);

                // Also correct animation out-point
                if (newAnimation.EndFrame >= maxFrameCount)
                {
                    newAnimation.EndFrame = (ushort)(maxFrameCount - 1);
                }

                frameBases.Add(newAnimation, new ushort[] { newFrameStart, (ushort)(maxFrameCount - 1) });
                newMoveable.Animations.Add(newAnimation);
            }

            for (int i = 0; i < newMoveable.Animations.Count; i++)
            {
                var animation = newMoveable.Animations[i];

                if (animation.KeyFrames.Count == 0)
                {
                    animation.EndFrame = 0;
                }

                // HACK: this fixes some invalid NextAnimations values
                animation.NextAnimation %= (ushort)newMoveable.Animations.Count;

                newMoveable.Animations[i] = animation;
            }

            for (int i = 0; i < newMoveable.Animations.Count; i++)
            {
                var animation = newMoveable.Animations[i];

                // HACK: this fixes some invalid NextFrame values
                if (frameBases[newMoveable.Animations[animation.NextAnimation]][0] != 0)
                {
                    animation.NextFrame -= frameBases[newMoveable.Animations[animation.NextAnimation]][0];
                    if (animation.NextFrame > frameBases[newMoveable.Animations[animation.NextAnimation]][1])
                    {
                        animation.NextFrame = frameBases[newMoveable.Animations[animation.NextAnimation]][1];
                    }
                }

                foreach (var stateChange in animation.StateChanges)
                {
                    for (int j = 0; j < stateChange.Dispatches.Count; ++j)
                    {
                        WadAnimDispatch animDispatch = stateChange.Dispatches[j];

                        // HACK: Probably WadMerger's bug
                        if (animDispatch.NextAnimation > 32767)
                        {
                            animDispatch.NextAnimation = 0;
                            animDispatch.NextFrame     = 0;
                            continue;
                        }

                        if (frameBases[newMoveable.Animations[animDispatch.NextAnimation]][0] != 0)
                        {
                            // HACK: In some cases dispatches have invalid NextFrame.
                            // From tests it seems that's ok to make NextFrame equal to max frame number.
                            animDispatch.NextFrame -= frameBases[newMoveable.Animations[animDispatch.NextAnimation]][0];
                            if (animDispatch.NextFrame > frameBases[newMoveable.Animations[animDispatch.NextAnimation]][1])
                            {
                                animDispatch.NextFrame = frameBases[newMoveable.Animations[animDispatch.NextAnimation]][1];
                            }
                        }
                        stateChange.Dispatches[j] = animDispatch;
                    }
                }
            }

            wad.Moveables.Add(newMoveable.Id, newMoveable);
            return(newMoveable);
        }
Esempio n. 13
0
        public static WadMoveable ConvertTrLevelMoveableToWadMoveable(Wad2 wad, TrLevel oldLevel, int moveableIndex,
                                                                      TextureArea[] objectTextures)
        {
            Console.WriteLine("Converting Moveable " + moveableIndex);

            var         oldMoveable = oldLevel.Moveables[moveableIndex];
            WadMoveable newMoveable = new WadMoveable(new WadMoveableId(oldMoveable.ObjectID));

            // First a list of meshes for this moveable is built
            var oldMeshes = new List <tr_mesh>();

            for (int j = 0; j < oldMoveable.NumMeshes; j++)
            {
                oldMeshes.Add(oldLevel.Meshes[(int)oldLevel.RealPointers[(int)(oldMoveable.StartingMesh + j)]]);
            }

            // Convert the WadMesh
            var newMeshes = new List <WadMesh>();

            foreach (var oldMesh in oldMeshes)
            {
                newMeshes.Add(ConvertTrLevelMeshToWadMesh(wad, oldLevel, oldMesh, objectTextures));
            }

            // Build the skeleton
            var root = new WadBone();

            root.Name        = "bone_0_root";
            root.Parent      = null;
            root.Translation = Vector3.Zero;
            root.Mesh        = newMeshes[0];

            newMoveable.Bones.Add(root);

            for (int j = 0; j < oldMoveable.NumMeshes - 1; j++)
            {
                var bone = new WadBone();
                bone.Name        = "bone_" + (j + 1).ToString();
                bone.Parent      = null;
                bone.Translation = Vector3.Zero;
                bone.Mesh        = newMeshes[j + 1];
                newMoveable.Bones.Add(bone);
            }

            for (int mi = 0; mi < (oldMeshes.Count - 1); mi++)
            {
                int j = mi + 1;

                var opcode = (WadLinkOpcode)oldLevel.MeshTrees[(int)(oldMoveable.MeshTree + mi * 4)];
                int linkX  = oldLevel.MeshTrees[(int)(oldMoveable.MeshTree + mi * 4) + 1];
                int linkY  = -oldLevel.MeshTrees[(int)(oldMoveable.MeshTree + mi * 4) + 2];
                int linkZ  = oldLevel.MeshTrees[(int)(oldMoveable.MeshTree + mi * 4) + 3];

                newMoveable.Bones[j].OpCode      = opcode;
                newMoveable.Bones[j].Translation = new Vector3(linkX, linkY, linkZ);
            }

            // Convert animations
            int numAnimations = 0;
            int nextMoveable  = oldLevel.GetNextMoveableWithAnimations(moveableIndex);

            if (nextMoveable == -1)
            {
                numAnimations = oldLevel.Animations.Count - oldMoveable.Animation;
            }
            else
            {
                numAnimations = oldLevel.Moveables[nextMoveable].Animation - oldMoveable.Animation;
            }

            var frameBases = new Dictionary <WadAnimation, ushort>();

            for (int j = 0; j < numAnimations; j++)
            {
                if (oldMoveable.Animation == -1)
                {
                    break;
                }

                WadAnimation newAnimation = new WadAnimation();
                var          oldAnimation = oldLevel.Animations[j + oldMoveable.Animation];
                newAnimation.FrameRate     = oldAnimation.FrameRate;
                newAnimation.NextAnimation = (ushort)(oldAnimation.NextAnimation - oldMoveable.Animation);
                newAnimation.NextFrame     = oldAnimation.NextFrame;
                newAnimation.StateId       = oldAnimation.StateID;
                newAnimation.EndFrame      = (ushort)(oldAnimation.FrameEnd - oldAnimation.FrameStart);
                newAnimation.Name          = TrCatalog.GetAnimationName(oldLevel.Version, oldMoveable.ObjectID, (uint)j);

                for (int k = 0; k < oldAnimation.NumStateChanges; k++)
                {
                    WadStateChange sc    = new WadStateChange();
                    var            wadSc = oldLevel.StateChanges[(int)oldAnimation.StateChangeOffset + k];
                    sc.StateId = wadSc.StateID;

                    for (int n = 0; n < wadSc.NumAnimDispatches; n++)
                    {
                        WadAnimDispatch ad    = new WadAnimDispatch();
                        var             wadAd = oldLevel.AnimDispatches[(int)wadSc.AnimDispatch + n];

                        ad.InFrame       = (ushort)(wadAd.Low - oldAnimation.FrameStart);
                        ad.OutFrame      = (ushort)(wadAd.High - oldAnimation.FrameStart);
                        ad.NextAnimation = (ushort)((wadAd.NextAnimation - oldMoveable.Animation) % numAnimations);
                        ad.NextFrame     = (ushort)wadAd.NextFrame;

                        sc.Dispatches.Add(ad);
                    }

                    newAnimation.StateChanges.Add(sc);
                }

                if (oldAnimation.NumAnimCommands < oldLevel.AnimCommands.Count)
                {
                    int lastCommand = oldAnimation.AnimCommand;

                    for (int k = 0; k < oldAnimation.NumAnimCommands; k++)
                    {
                        short commandType = oldLevel.AnimCommands[lastCommand + 0];

                        WadAnimCommand command = new WadAnimCommand {
                            Type = (WadAnimCommandType)commandType
                        };
                        switch (commandType)
                        {
                        case 1:
                            command.Parameter1 = (short)oldLevel.AnimCommands[lastCommand + 1];
                            command.Parameter2 = (short)oldLevel.AnimCommands[lastCommand + 2];
                            command.Parameter3 = (short)oldLevel.AnimCommands[lastCommand + 3];

                            lastCommand += 4;
                            break;

                        case 2:
                            command.Parameter1 = (short)oldLevel.AnimCommands[lastCommand + 1];
                            command.Parameter2 = (short)oldLevel.AnimCommands[lastCommand + 2];

                            lastCommand += 3;
                            break;

                        case 3:
                            lastCommand += 1;
                            break;

                        case 4:
                            lastCommand += 1;
                            break;

                        case 5:
                            command.Parameter1 = (short)(oldLevel.AnimCommands[lastCommand + 1] - oldAnimation.FrameStart);
                            command.Parameter2 = (short)oldLevel.AnimCommands[lastCommand + 2];
                            lastCommand       += 3;
                            break;

                        case 6:
                            command.Parameter1 = (short)(oldLevel.AnimCommands[lastCommand + 1] - oldAnimation.FrameStart);
                            command.Parameter2 = (short)oldLevel.AnimCommands[lastCommand + 2];
                            lastCommand       += 3;
                            break;

                        default:     // Ignore invalid anim commands (see for example karnak.wad)
                            logger.Warn("Unknown anim command " + commandType);
                            goto ExitForLoop;
                        }

                        newAnimation.AnimCommands.Add(command);
                    }
ExitForLoop:
                    ;
                }

                int  frames = (int)oldAnimation.FrameOffset / 2;
                uint numFrames;

                if (j + oldMoveable.Animation == oldLevel.Animations.Count - 1)
                {
                    if (oldAnimation.FrameSize == 0)
                    {
                        numFrames = oldLevel.Version == TRVersion.Game.TR1 ? (uint)((newAnimation.EndFrame + 1) / newAnimation.FrameRate) : 0;
                    }
                    else
                    {
                        numFrames = ((uint)(2 * oldLevel.Frames.Count) - oldAnimation.FrameOffset) / (uint)(2 * oldAnimation.FrameSize);
                    }
                }
                else
                {
                    if (oldAnimation.FrameSize == 0)
                    {
                        numFrames = oldLevel.Version == TRVersion.Game.TR1 ? (uint)((newAnimation.EndFrame + 1) / newAnimation.FrameRate) : 0;
                    }
                    else
                    {
                        numFrames = (oldLevel.Animations[oldMoveable.Animation + j + 1].FrameOffset - oldAnimation.FrameOffset) / (uint)(2 * oldAnimation.FrameSize);
                    }
                }

                for (int f = 0; f < numFrames; f++)
                {
                    WadKeyFrame frame        = new WadKeyFrame();
                    int         startOfFrame = frames;

                    frame.BoundingBox = new BoundingBox(new Vector3(oldLevel.Frames[frames],
                                                                    -oldLevel.Frames[frames + 2],
                                                                    oldLevel.Frames[frames + 4]),
                                                        new Vector3(oldLevel.Frames[frames + 1],
                                                                    -oldLevel.Frames[frames + 3],
                                                                    oldLevel.Frames[frames + 5]));

                    frames += 6;

                    frame.Offset = new Vector3(oldLevel.Frames[frames],
                                               (short)(-oldLevel.Frames[frames + 1]),
                                               oldLevel.Frames[frames + 2]);

                    frames += 3;

                    // TR1 has also the number of angles to follow
                    if (oldLevel.Version == TRVersion.Game.TR1)
                    {
                        frames++;
                    }

                    for (int n = 0; n < oldMoveable.NumMeshes; n++)
                    {
                        frame.Angles.Add(
                            WadKeyFrameRotation.FromTrAngle(ref frames, oldLevel.Frames,
                                                            oldLevel.Version == TRVersion.Game.TR1,
                                                            oldLevel.Version == TRVersion.Game.TR4 || oldLevel.Version == TRVersion.Game.TR5));
                    }

                    if ((frames - startOfFrame) < oldAnimation.FrameSize)
                    {
                        frames += ((int)oldAnimation.FrameSize - (frames - startOfFrame));
                    }

                    newAnimation.KeyFrames.Add(frame);
                }

                frameBases.Add(newAnimation, oldAnimation.FrameStart);

                // New velocities
                float acceleration = oldAnimation.Accel / 65536.0f;
                newAnimation.StartVelocity = oldAnimation.Speed / 65536.0f;

                float lateralAcceleration = oldAnimation.AccelLateral / 65536.0f;
                newAnimation.StartLateralVelocity = oldAnimation.SpeedLateral / 65536.0f;

                if (newAnimation.KeyFrames.Count != 0 && newAnimation.FrameRate != 0)
                {
                    newAnimation.EndVelocity = newAnimation.StartVelocity + acceleration *
                                               (newAnimation.KeyFrames.Count - 1) * newAnimation.FrameRate;
                    newAnimation.EndLateralVelocity = newAnimation.StartLateralVelocity + lateralAcceleration *
                                                      (newAnimation.KeyFrames.Count - 1) * newAnimation.FrameRate;
                }

                newMoveable.Animations.Add(newAnimation);
            }

            for (int i = 0; i < newMoveable.Animations.Count; i++)
            {
                var animation = newMoveable.Animations[i];

                if (animation.KeyFrames.Count == 0)
                {
                    animation.EndFrame = 0;
                }

                // HACK: this fixes some invalid NextAnimations values
                animation.NextAnimation %= (ushort)newMoveable.Animations.Count;

                newMoveable.Animations[i] = animation;
            }

            for (int i = 0; i < newMoveable.Animations.Count; i++)
            {
                var animation = newMoveable.Animations[i];

                // HACK: this fixes some invalid NextFrame values
                if (frameBases[newMoveable.Animations[animation.NextAnimation]] != 0)
                {
                    animation.NextFrame %= frameBases[newMoveable.Animations[animation.NextAnimation]];
                }

                foreach (var stateChange in animation.StateChanges)
                {
                    for (int J = 0; J < stateChange.Dispatches.Count; ++J)
                    {
                        WadAnimDispatch animDispatch = stateChange.Dispatches[J];
                        if (frameBases[newMoveable.Animations[animDispatch.NextAnimation]] != 0)
                        {
                            ushort newFrame = (ushort)(animDispatch.NextFrame % frameBases[newMoveable.Animations[animDispatch.NextAnimation]]);

                            // In some cases dispatches have invalid NextFrame.
                            // From tests it seems that's ok to delete the dispatch or put the NextFrame equal to zero.
                            if (newFrame > newMoveable.Animations[animDispatch.NextAnimation].EndFrame)
                            {
                                newFrame = 0;
                            }

                            animDispatch.NextFrame = newFrame;
                        }
                        stateChange.Dispatches[J] = animDispatch;
                    }
                }

                newMoveable.Animations[i] = animation;
            }

            return(newMoveable);
        }