示例#1
0
        public override Unity_Object CreateObject(int index)
        {
            // Get the event
            var e = AvailableEvents[index];

            // Get the commands and label offsets
            R1_EventCommandCollection cmds = null;

            ushort[] labelOffsets = null;

            // If local (non-compiled) commands are used, attempt to get them from the event info or decompile the compiled ones
            if (UsesLocalCommands)
            {
                cmds = EventCommandCompiler.Decompile(new EventCommandCompiler.CompiledEventCommandData(R1_EventCommandCollection.FromBytes(e.Commands, Context.Settings), e.LabelOffsets), e.Commands);
            }
            else if (e.Commands.Any())
            {
                cmds         = R1_EventCommandCollection.FromBytes(e.Commands, Context.Settings);
                labelOffsets = e.LabelOffsets.Any() ? e.LabelOffsets : null;
            }

            var eventData = new Unity_Object_R1(new R1_EventData()
            {
                Type         = (R1_EventType)e.Type,
                Etat         = e.Etat,
                SubEtat      = e.SubEtat,
                OffsetBX     = e.OffsetBX,
                OffsetBY     = e.OffsetBY,
                OffsetHY     = e.OffsetHY,
                FollowSprite = e.FollowSprite,
                DisplayPrio  = 0,
                HitSprite    = e.HitSprite,
                Commands     = cmds,
                LabelOffsets = labelOffsets
            }, this, ETAIndex: ETA.FindItemIndex(x => x.Name == e.ETA));

            eventData.EventData.SetFollowEnabled(Context.Settings, e.FollowEnabled);

            // We need to set the hit points after the type
            eventData.EventData.ActualHitPoints = e.HitPoints;

            // Set DES
            eventData.DESIndex = DES.FindItemIndex(x => x.Name == e.DES);

            return(eventData);
        }
示例#2
0
        /// <summary>
        /// Loads the specified level for the editor
        /// </summary>
        /// <param name="context">The serialization context</param>
        /// <param name="loadTextures">Indicates if textures should be loaded</param>
        /// <returns>The level</returns>
        public override async UniTask <Unity_Level> LoadAsync(Context context, bool loadTextures)
        {
            // Get the level folder path
            var basePath = await GetLevelFolderPath(context);

            // TODO: Parse .ini file to get background etc.
            // Get file paths
            var mapPath        = GetMapFilePath(basePath);
            var mapEventsPath  = GetMapEventsFilePath(basePath);
            var saveEventsPath = GetSaveEventsFilePath(basePath);
            var pcxPath        = GetPCXFilePath(context.Settings);
            var eventsCsvPath  = GetEventManfiestFilePath(context.Settings.R1_World);

            // Load files to context
            await AddFile(context, mapPath);
            await AddFile(context, mapEventsPath, endianness : BinaryFile.Endian.Big); // Big endian just like on Jaguar
            await AddFile(context, pcxPath);

            // Read the files

            Controller.DetailedState = $"Loading map data";
            await Controller.WaitIfNecessary();

            var mapData = FileFactory.Read <MapData>(mapPath, context);

            Controller.DetailedState = $"Loading event data";
            await Controller.WaitIfNecessary();

            var mapEvents  = FileFactory.Read <R1Jaguar_MapEvents>(mapEventsPath, context);
            var saveEvents = FileFactory.ReadText <R1_Mapper_SaveEvents>(saveEventsPath, context);
            var csv        = FileFactory.ReadText <R1_Mapper_EventManifest>(eventsCsvPath, context);

            Controller.DetailedState = $"Loading tileset";
            await Controller.WaitIfNecessary();

            var pcx = FileFactory.Read <PCX>(pcxPath, context);

            // Get the palette from the PCX file
            var vgaPalette = pcx.VGAPalette;

            // Load the sprites
            var eventDesigns = loadTextures ? await LoadSpritesAsync(context, vgaPalette) : new Unity_ObjectManager_R1.DESData[0];

            // Read the world data
            var worldData = FileFactory.Read <R1_PC_WorldFile>(GetWorldFilePath(context.Settings), context);

            var maps = new Unity_Map[]
            {
                new Unity_Map()
                {
                    // Set the dimensions
                    Width  = mapData.Width,
                    Height = mapData.Height,

                    // Create the tile arrays
                    TileSet      = new Unity_MapTileMap[1],
                    MapTiles     = mapData.Tiles.Select(x => new Unity_Tile(x)).ToArray(),
                    TileSetWidth = 1
                }
            };

            var allEventInstances = saveEvents.SaveEventInstances.SelectMany(x => x).ToArray();

            // Create a linking table
            var linkTable = new ushort[allEventInstances.Length];

            // Handle each event link group
            foreach (var linkedEvents in allEventInstances.Select((x, i) => new
            {
                Index = i,
                Data = x
            }).GroupBy(x => x.Data.LinkID))
            {
                // Get the group
                var group = linkedEvents.ToArray();

                // Handle every event
                for (int i = 0; i < group.Length; i++)
                {
                    // Get the item
                    var item = group[i];

                    if (group.Length == i + 1)
                    {
                        linkTable[item.Index] = (ushort)group[0].Index;
                    }
                    else
                    {
                        linkTable[item.Index] = (ushort)group[i + 1].Index;
                    }
                }
            }

            // Create the object manager
            var objManager = new Unity_ObjectManager_R1(
                context: context,
                des: eventDesigns.Select((x, i) => new Unity_ObjectManager_R1.DataContainer <Unity_ObjectManager_R1.DESData>(x, i, worldData.DESFileNames?.ElementAtOrDefault(i))).ToArray(),
                eta: GetCurrentEventStates(context).Select((x, i) => new Unity_ObjectManager_R1.DataContainer <R1_EventState[][]>(x.States, i, worldData.ETAFileNames?.ElementAtOrDefault(i))).ToArray(),
                linkTable: linkTable,
                usesPointers: false,
                hasDefinedDesEtaNames: true);

            Controller.DetailedState = $"Loading events";
            await Controller.WaitIfNecessary();

            var levelEvents = new List <Unity_Object>();

            // Create events
            for (var i = 0; i < saveEvents.SaveEventInstances.Length; i++)
            {
                // Get the map base position, based on the event map
                var mapPos = mapEvents.EventIndexMap.FindItemIndex(z => z == i + 1);

                // Get the x and y positions
                var mapY = (uint)Math.Floor(mapPos / (double)(mapEvents.Width));
                var mapX = (uint)(mapPos - (mapY * mapEvents.Width));

                // Calculate the actual position on the map
                mapX *= 4 * (uint)Settings.CellSize;
                mapY *= 4 * (uint)Settings.CellSize;

                // Add every instance
                foreach (var instance in saveEvents.SaveEventInstances[i])
                {
                    // Get the definition
                    var def = csv.EventDefinitions.FirstOrDefault(x => x.Name == instance.EventDefinitionKey);

                    if (def == null)
                    {
                        throw new Exception($"No matching event definition found for {instance.EventDefinitionKey}");
                    }

                    var ed = new R1_EventData()
                    {
                        Type            = def.Type,
                        Etat            = def.Etat,
                        SubEtat         = def.SubEtat,
                        XPosition       = (short)(mapX + instance.OffsetX),
                        YPosition       = (short)(mapY + instance.OffsetY),
                        OffsetBX        = def.OffsetBX,
                        OffsetBY        = def.OffsetBY,
                        OffsetHY        = def.OffsetHY,
                        FollowSprite    = def.FollowSprite,
                        ActualHitPoints = (uint)instance.HitPoints,
                        DisplayPrio     = instance.DisplayPrio,
                        HitSprite       = def.HitSprite,

                        PS1Demo_Unk1 = new byte[40],
                        Unk_98       = new byte[5],

                        LabelOffsets = new ushort[0],
                        Commands     = R1_EventCommandCollection.FromBytes(def.EventCommands, context.Settings),
                    };

                    ed.SetFollowEnabled(context.Settings, def.FollowEnabled > 0);

                    // Add the event
                    levelEvents.Add(new Unity_Object_R1(
                                        eventData: ed,
                                        objManager: objManager,
                                        ETAIndex: worldData.ETAFileNames.FindItemIndex(x => x == def.ETAFile))
                    {
                        DESIndex = worldData.DESFileNames.FindItemIndex(x => x.Length > 4 && x.Substring(0, x.Length - 4) == def.DESFile)
                    });
                }
            }

            // Convert levelData to common level format
            var level = new Unity_Level(maps, objManager, levelEvents, rayman: new Unity_Object_R1(R1_EventData.GetRayman(context, levelEvents.Cast <Unity_Object_R1>().FirstOrDefault(x => x.EventData.Type == R1_EventType.TYPE_RAY_POS)?.EventData), objManager));

            Controller.DetailedState = $"Creating tileset";
            await Controller.WaitIfNecessary();

            // Load the tile set
            level.Maps[0].TileSet[0] = LoadTileSet(pcx);

            // Return the level
            return(level);
        }
 /// <summary>
 /// Default constructor
 /// </summary>
 /// <param name="commands">The event commands</param>
 /// <param name="labelOffsets">The label offsets</param>
 public CompiledEventCommandData(R1_EventCommandCollection commands, ushort[] labelOffsets)
 {
     Commands     = commands;
     LabelOffsets = labelOffsets;
 }
        /// <summary>
        /// Compiles the commands
        /// </summary>
        /// <param name="commands">The commands</param>
        /// <param name="commandBytes">The command bytes</param>
        /// <returns>The compiled the command data</returns>
        public static CompiledEventCommandData Compile(R1_EventCommandCollection commands, byte[] commandBytes)
        {
            // Create the lists to compile to
            var compiledCommands = new List <R1_EventCommand>();
            var labelOffsets     = new List <ushort>();

            // Keep track of the command byte index
            var commandByteIndex = 0;
            var commandIndex     = 0;

            foreach (var command in commands.Commands)
            {
                // Increment the index for the command declaration
                commandByteIndex++;

                R1_EventCommandType GetCompiledVersion()
                {
                    switch (command.Command)
                    {
                    case R1_EventCommandType.GO_GOSUB:
                        return(R1_EventCommandType.RESERVED_GO_GOSUB);

                    case R1_EventCommandType.GO_GOTO:
                        return(R1_EventCommandType.RESERVED_GO_GOTO);

                    case R1_EventCommandType.GO_BRANCHTRUE:
                        return(R1_EventCommandType.RESERVED_GO_GOTOT);

                    case R1_EventCommandType.GO_BRANCHFALSE:
                        return(R1_EventCommandType.RESERVED_GO_GOTOF);

                    case R1_EventCommandType.GO_SKIP:
                        return(R1_EventCommandType.RESERVED_GO_SKIP);

                    case R1_EventCommandType.GO_SKIPTRUE:
                        return(R1_EventCommandType.RESERVED_GO_SKIPT);

                    case R1_EventCommandType.GO_SKIPFALSE:
                        return(R1_EventCommandType.RESERVED_GO_SKIPF);

                    default:
                        throw new ArgumentOutOfRangeException(nameof(command.Command), command.Command, null);
                    }
                }

                switch (command.Command)
                {
                // Handle commands with offsets
                case R1_EventCommandType.GO_GOSUB:
                case R1_EventCommandType.GO_GOTO:
                case R1_EventCommandType.GO_BRANCHTRUE:
                case R1_EventCommandType.GO_BRANCHFALSE:

                    // Get the label
                    var label = command.Arguments[0];

                    // Find the matching commands
                    var labelCmdIndex = commands.Commands.FindItemIndex(x => x.Command == R1_EventCommandType.GO_LABEL && x.Arguments.FirstOrDefault() == label);

                    // Get the offset
                    var offset = commands.Commands.Take(labelCmdIndex).Sum(x => x.Length) + 1;

                    // Add the offset to the label offsets
                    labelOffsets.Add((ushort)offset);

                    // Add the command
                    compiledCommands.Add(new R1_EventCommand()
                    {
                        Command   = GetCompiledVersion(),
                        Arguments = new byte[]
                        {
                            (byte)(labelOffsets.Count - 1)
                        }
                    });

                    break;

                // Handle commands with offsets based on current position
                case R1_EventCommandType.GO_SKIP:
                case R1_EventCommandType.GO_SKIPTRUE:
                case R1_EventCommandType.GO_SKIPFALSE:

                    // Get the number of cmds to skip
                    var toSkip = command.Arguments[0];

                    // Get the offset to skip to, starting at the next command index
                    var skipToOffset = commandByteIndex + 1;

                    int curCmd = commandIndex + 1;
                    for (int i = 0; i < toSkip; i++)
                    {
                        var cmd = commands.Commands[curCmd % commands.Commands.Length];
                        skipToOffset += cmd.Length;
                        if (cmd.Command == R1_EventCommandType.INVALID_CMD)
                        {
                            curCmd       = 0;
                            skipToOffset = commands.Commands[curCmd].Length;
                        }
                        curCmd++;
                    }

                    if (commands.Commands[curCmd % commands.Commands.Length].Command == R1_EventCommandType.GO_LABEL)
                    {
                        skipToOffset += 2;
                        curCmd++;
                    }

                    skipToOffset -= 1;

                    // Overflow
                    skipToOffset %= commands.Commands.Sum(x => x.Length);

                    // Add the offset to the label offsets
                    labelOffsets.Add((byte)skipToOffset);

                    // Add the command
                    compiledCommands.Add(new R1_EventCommand()
                    {
                        Command   = GetCompiledVersion(),
                        Arguments = new byte[]
                        {
                            (byte)(labelOffsets.Count - 1)
                        }
                    });

                    break;

                // Handle others
                default:

                    // Copy the command
                    compiledCommands.Add(new R1_EventCommand()
                    {
                        Command   = command.Command,
                        Arguments = command.Arguments.ToArray()
                    });

                    break;
                }

                // Increment the index for every argument
                commandByteIndex += command.Arguments.Length;
                commandIndex++;
            }

            // Return the compiled commands
            return(new CompiledEventCommandData(new R1_EventCommandCollection()
            {
                Commands = compiledCommands.ToArray()
            }, labelOffsets.ToArray()));
        }