public override void InitObjects(Unity_Level level) { foreach (var obj in level.EventData.Cast <Unity_Object_GBARRR>().Where(x => x.Object.ObjectType == GBARRR_ObjectType.Gate || x.Object.ObjectType == GBARRR_ObjectType.DoorTrigger)) { obj.LinkIndex = level.EventData.Cast <Unity_Object_GBARRR>().FindItemIndex(x => x.Object.RuntimeStateIndex == obj.Object.RuntimeStateIndex && x != obj && (x.Object.ObjectType == GBARRR_ObjectType.Gate || x.Object.ObjectType == GBARRR_ObjectType.DoorTrigger)); } }
public async UniTask<Unity_Level> LoadAsync(Context context, bool loadTextures) { Controller.DetailedState = $"Loading data"; await Controller.WaitIfNecessary(); // Read the rom var rom = FileFactory.Read<SNES_Proto_ROM>(GetROMFilePath, context); // Get the map var map = rom.MapData; var maps = new Unity_Map[] { new Unity_Map() { // Set the dimensions Width = map.Width, Height = map.Height, // Create the tile arrays TileSet = new Unity_MapTileMap[1], MapTiles = map.Tiles.Select(x => new Unity_Tile(x)).ToArray(), TileSetWidth = 1 } }; // Convert levelData to common level format Unity_Level level = new Unity_Level(maps, new Unity_ObjectManager(context), getCollisionTypeGraphicFunc: x => ((R1Jaguar_TileCollisionType)x).GetCollisionTypeGraphic()); Controller.DetailedState = $"Loading tile set"; await Controller.WaitIfNecessary(); // Load tile set and treat black as transparent level.Maps[0].TileSet[0] = GetTileSet(context, rom); return level; }
public UniTask SaveLevelAsync(Context context, Unity_Level level) => throw new NotImplementedException();
public virtual void InitObjects(Unity_Level level) { }
/// <summary> /// Randomizes the events in a level based on the flags /// </summary> /// <param name="level">The level</param> /// <param name="flags">The flags</param> /// <param name="seed">An optional seed to use</param> /// <param name="map">The map index</param> public static void Randomize(Unity_Level level, RandomizerFlags flags, int?seed, int map) { var random = seed != null ? new Random(seed.Value) : new Random(); var maxX = level.Maps[map].Width * Settings.CellSize; var maxY = level.Maps[map].Height * Settings.CellSize; // Enumerate every event foreach (var eventData in level.EventData .Select(eventData => new { obj = (Unity_Object_R1)eventData, isAlways = eventData.IsAlways, isEditor = eventData.IsEditor }) .Where(x => !x.isAlways && !x.isEditor) .Where(x => x.obj.EventData.Type != R1_EventType.TYPE_RAY_POS && x.obj.EventData.Type != R1_EventType.TYPE_PANCARTE && x.obj.EventData.Type != R1_EventType.TYPE_SIGNPOST) .Select(x => x.obj)) { if (flags.HasFlag(RandomizerFlags.Pos)) { eventData.XPosition = (short)random.Next(0, maxX); eventData.YPosition = (short)random.Next(0, maxY); } if (flags.HasFlag(RandomizerFlags.Des)) { eventData.DESIndex = random.Next(1, ((Unity_ObjectManager_R1)level.ObjManager).DES.Length - 2); // One less for BigRay } if (flags.HasFlag(RandomizerFlags.Eta)) { eventData.ETAIndex = random.Next(0, ((Unity_ObjectManager_R1)level.ObjManager).ETA.Length - 2); // One less for BigRay } if (flags.HasFlag(RandomizerFlags.CommandOrder)) { int n = eventData.EventData.Commands.Commands.Length - 1; while (n > 1) { n--; int k = random.Next(n + 1); var value = eventData.EventData.Commands.Commands[k]; eventData.EventData.Commands.Commands[k] = eventData.EventData.Commands.Commands[n]; eventData.EventData.Commands.Commands[n] = value; } } if (flags.HasFlag(RandomizerFlags.Follow)) { eventData.EventData.SetFollowEnabled(level.ObjManager.Context.Settings, random.Next(0, 1) == 1); eventData.EventData.OffsetHY = (byte)random.Next(0, 10); } if (flags.HasFlag(RandomizerFlags.States)) { eventData.EventData.Etat = (byte)random.Next(0, ((Unity_ObjectManager_R1)level.ObjManager).ETA[eventData.ETAIndex].Data.Length - 1); eventData.EventData.SubEtat = (byte)random.Next(0, ((Unity_ObjectManager_R1)level.ObjManager).ETA[eventData.ETAIndex].Data[eventData.EventData.Etat].Length - 1); } if (flags.HasFlag(RandomizerFlags.Type)) { eventData.EventData.Type = (R1_EventType)random.Next(0, 255); } } }
/// <summary> /// Saves the specified level /// </summary> /// <param name="context">The serialization context</param> /// <param name="lvl">The level</param> public override UniTask SaveLevelAsync(Context context, Unity_Level lvl) { // Menu levels can't be saved if (context.Settings.R1_World == R1_World.Menu) { return(UniTask.CompletedTask); } // Get the level file path var lvlPath = GetLevelFilePath(context.Settings); // Get the level data var lvlData = context.GetMainFileObject <R1_PS1_LevFile>(lvlPath); // Get the object manager var objManager = (Unity_ObjectManager_R1)lvl.ObjManager; // Update the tiles for (int y = 0; y < lvlData.MapData.Height; y++) { for (int x = 0; x < lvlData.MapData.Width; x++) { // Set the tile lvlData.MapData.Tiles[y * lvlData.MapData.Width + x] = lvl.Maps[0].MapTiles[y * lvlData.MapData.Width + x].Data; } } var newEvents = lvl.EventData.Cast <Unity_Object_R1>().Select(e => { var ed = e.EventData; if (ed.PS1Demo_Unk1 == null) { ed.PS1Demo_Unk1 = new byte[40]; } if (ed.Unk_98 == null) { ed.Unk_98 = new byte[5]; } // TODO: Do this in the Unity_Object instead ed.ImageDescriptorCount = (ushort)objManager.DES[e.DESIndex].Data.ImageDescriptors.Length; ed.AnimDescriptorCount = (byte)objManager.DES[e.DESIndex].Data.Graphics.Animations.Count; // TODO: Get from DESData in obj manager instead? ed.ImageDescriptors = FileFactory.Read <ObjectArray <R1_ImageDescriptor> >(ed.ImageDescriptorsPointer, context, (s, o) => o.Length = ed.ImageDescriptorCount).Value; ed.AnimDescriptors = FileFactory.Read <ObjectArray <R1_PS1_AnimationDescriptor> >(ed.AnimDescriptorsPointer, context, (s, o) => o.Length = ed.AnimDescriptorCount).Value; ed.ETA = context.Cache.FromOffset <R1_PS1_ETA>(ed.ETAPointer); // TODO: Update this //ed.ImageBuffer = des.ImageBuffer; return(ed); }).ToArray(); var newEventLinkTable = objManager.LinkTable.Select(x => (byte)x).ToArray(); // Relocate pointers to a new block of data we append to the level file UpdateAndFillDataBlock(lvlData.Offset + lvlData.FileSize, lvlData.EventData, newEvents, newEventLinkTable, context.Settings); // TODO: When writing make sure that ONLY the level file gets recreated - do not touch the other files (ignore DoAt if the file needs to be switched based on some setting?) // Save the file FileFactory.Write <R1_PS1_LevFile>(lvlPath, context); // Create ISO for the modified data CreateISO(context); return(UniTask.CompletedTask); }
/// <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> /// 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) { await Controller.WaitIfNecessary(); Controller.DetailedState = $"Loading files"; uint baseAddress = 0x80018000; var fixDTAPath = FixDataPath; var fixGRPPath = FixGraphicsPath; var sprPLSPath = SpritePalettesPath; var levelDTAPath = GetLevelDataPath(context.Settings); var levelSPRPath = GetLevelImageDescriptorsPath(context.Settings); var levelGRPPath = GetLevelGraphicsPath(context.Settings); baseAddress += await LoadFile(context, fixDTAPath, baseAddress); baseAddress -= 94; // FIX.DTA header size Pointer fixDTAHeader = new Pointer(baseAddress, context.FilePointer(fixDTAPath).file); R1_R2AllfixFooter footer = null; context.Deserializer.DoAt(fixDTAHeader, () => footer = context.Deserializer.SerializeObject <R1_R2AllfixFooter>(null, name: "AllfixFooter")); await LoadFile(context, fixGRPPath, 0); await LoadFile(context, sprPLSPath, 0); baseAddress += await LoadFile(context, levelSPRPath, baseAddress); baseAddress += await LoadFile(context, levelDTAPath, baseAddress); await LoadFile(context, levelGRPPath, 0); // Load every map for (int i = 0; i < 4; i++) { await LoadFile(context, GetSubMapPalettePath(i), 0); await LoadFile(context, GetSubMapTilesetPath(i), 0); await LoadFile(context, GetSubMapPath(i), 0); } await Controller.WaitIfNecessary(); Controller.DetailedState = $"Loading level data"; // Read the level data var lvlData = FileFactory.Read <R1_R2LevDataFile>(levelDTAPath, context); // Read the map blocks var maps = Enumerable.Range(0, MapCount).Select(x => FileFactory.Read <MapData>(GetSubMapPath(x), context)).ToArray(); await Controller.WaitIfNecessary(); Controller.DetailedState = $"Loading sprite data"; var commonEvents = new List <Unity_Object>(); if (loadTextures) { // Get the v-ram FillVRAM(context, VRAMMode.Level); } // Create the global design list var lvlImgDescriptors = FileFactory.Read <ObjectArray <R1_ImageDescriptor> >(levelSPRPath, context, onPreSerialize: (s, a) => a.Length = s.CurrentLength / 0xC).Value; var imgDescriptors = lvlData.FixImageDescriptors.Concat(lvlImgDescriptors).ToArray(); // Get every sprite var globalDesigns = imgDescriptors.Select(img => GetSpriteTexture(context, null, img)).Select(tex => tex == null ? null : tex.CreateSprite()).ToArray(); // Get the events var events = lvlData.Events.Concat(lvlData.AlwaysEvents).ToArray(); Controller.DetailedState = $"Loading animations"; await Controller.WaitIfNecessary(); // Get the animation groups var r2AnimGroups = events.Select(x => x.AnimGroup).Append(footer.RaymanAnimGroup).Where(x => x != null).Distinct().ToArray(); Unity_ObjectManager_R2.AnimGroup[] animGroups = new Unity_ObjectManager_R2.AnimGroup[r2AnimGroups.Length]; for (int i = 0; i < animGroups.Length; i++) { animGroups[i] = await getGroupAsync(r2AnimGroups[i]); await Controller.WaitIfNecessary(); } async UniTask <Unity_ObjectManager_R2.AnimGroup> getGroupAsync(R1_R2EventAnimGroup animGroup) { await UniTask.CompletedTask; // Add DES and ETA return(new Unity_ObjectManager_R2.AnimGroup( pointer: animGroup?.Offset, eta: animGroup?.ETA.EventStates ?? new R1_EventState[0][], animations: animGroup?.AnimationDecriptors?.Select(x => x.ToCommonAnimation()).ToArray(), filePath: animGroup?.AnimationDescriptorsPointer?.file.filePath)); } var objManager = new Unity_ObjectManager_R2( context: context, linkTable: lvlData.EventLinkTable, animGroups: animGroups, sprites: globalDesigns, imageDescriptors: imgDescriptors, levData: lvlData); Controller.DetailedState = $"Loading events"; await Controller.WaitIfNecessary(); // Add every event foreach (var e in events) { // Add the event commonEvents.Add(new Unity_Object_R2(e, objManager) { IsAlwaysEvent = lvlData.AlwaysEvents.Contains(e) }); } await Controller.WaitIfNecessary(); Controller.DetailedState = $"Loading tiles"; var levelMaps = maps.Select((x, i) => new Unity_Map() { Type = Unity_Map.MapType.Graphics | Unity_Map.MapType.Collision, Layer = i < 3 ? Unity_Map.MapLayer.Back : Unity_Map.MapLayer.Middle, // Set the dimensions Width = x.Width, Height = x.Height, // Create the tile array TileSet = new Unity_TileSet[1], TileSetWidth = TileSetWidth }).ToArray(); // Convert levelData to common level format Unity_Level level = new Unity_Level( maps: levelMaps, objManager: objManager, eventData: commonEvents, rayman: new Unity_Object_R2(R1_R2EventData.GetRayman(events.FirstOrDefault(x => x.EventType == R1_R2EventType.RaymanPosition), footer), objManager), getCollisionTypeNameFunc: x => ((R2_TileCollisionType)x).ToString(), getCollisionTypeGraphicFunc: x => ((R2_TileCollisionType)x).GetCollisionTypeGraphic()); await Controller.WaitIfNecessary(); // Load maps for (int i = 0; i < MapCount; i++) { // Get the tile set Unity_TileSet tileSet = GetTileSet(context, i); level.Maps[i].TileSet[0] = tileSet; // Set the tiles level.Maps[i].MapTiles = maps[i].Tiles.Select(x => new Unity_Tile(x)).ToArray(); } // Return the level return(level); }
public override UniTask SaveLevelAsync(Context context, Unity_Level lvl) { return(SaveLevelAsync(context, lvl, true)); }
public override void InitObjects(Unity_Level level) { // Init 3D objects var all3DObjects = level.EventData.OfType <Unity_Object_GBAIsometricSpyro>().ToArray(); foreach (var obj in all3DObjects) { if (obj.Object.ObjectType != 0) { GBAIsometric_Spyro_ObjInit.GetInitFunc(Context.Settings, obj.ObjType?.Data?.InitFunctionPointer?.AbsoluteOffset ?? 0)?.Invoke(obj, all3DObjects); if (obj.AnimSetIndex == -1 && !obj.IsEditorObj && obj.ObjType?.Data?.InitFunctionPointer != null) { Debug.LogWarning($"Type {obj.Object.ObjectType} is not implemented with init function at 0x{obj.ObjType?.Data?.InitFunctionPointer}"); } } } // Init 2D objects var all2DObjects = level.EventData.OfType <Unity_Object_GBAIsometricSpyro2_2D>().ToArray(); foreach (var obj in all2DObjects) { // Set link if (obj.CanBeLinked && obj.Object.LinkedObjectID != -1) { var linkIndex = all2DObjects.FindItemIndex(x => x.Object.ID == obj.Object.LinkedObjectID && x.Object.Category == GBAIsometric_Spyro2_Object2D.ObjCategory.Door); if (linkIndex != -1) { obj.LinkIndex = linkIndex; } } // Set position if (obj.LinkIndex != -1) { // If linked we use that position instead obj.XPosition = all2DObjects[obj.LinkIndex].XPosition; obj.YPosition = all2DObjects[obj.LinkIndex].YPosition; } else { obj.XPosition = obj.Object.MinX; obj.YPosition = obj.Object.MinY; } // Set graphics index based off of type switch (obj.Object.Category) { case GBAIsometric_Spyro2_Object2D.ObjCategory.Door: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x86); switch (Context.Settings.Level) { case 0: obj.AnimationGroupIndex = 0x03; break; case 1: obj.AnimationGroupIndex = 0x05; break; case 2: obj.AnimationGroupIndex = 0x01; break; case 3: obj.AnimationGroupIndex = 0x07; break; } break; case GBAIsometric_Spyro2_Object2D.ObjCategory.Character: switch (obj.Object.ObjType) { // Enemies case 0: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x92); obj.AnimationGroupIndex = 0x05; break; case 1: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x91); obj.AnimationGroupIndex = 0x03; break; case 2: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x8A); obj.AnimationGroupIndex = 0x02; break; case 3: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x84); obj.AnimationGroupIndex = 0x0B; break; case 4: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x82); obj.AnimationGroupIndex = 0x01; break; // No graphics case 5: // Exit case 6: case 10: case 12: obj.IsEditorObj = true; obj.AnimSetIndex = -1; break; // Mine case 7: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x8E); obj.AnimationGroupIndex = 0x00; break; // Floating platforms case 8: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x90); obj.AnimationGroupIndex = 0x02; break; case 9: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x90); obj.AnimationGroupIndex = 0x00; break; // Checkpoint fairy case 11: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x88); obj.AnimationGroupIndex = 0x00; break; // Agent 9 case 13: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x80); obj.AnimationGroupIndex = 0x08; break; // Firefly case 14: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x7C); obj.AnimationGroupIndex = 0x03; break; // Sign case 15: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x96); obj.AnimationGroupIndex = 0x00; break; default: obj.AnimSetIndex = -1; break; } break; case GBAIsometric_Spyro2_Object2D.ObjCategory.Collectible: switch (obj.Object.ObjType) { // Gem case 0: // Red obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x89); obj.AnimationGroupIndex = 0x03; break; case 1: // Diamond obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x89); obj.AnimationGroupIndex = 0x00; break; case 2: // Gold obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x89); obj.AnimationGroupIndex = 0x05; break; case 3: // Green obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x89); obj.AnimationGroupIndex = 0x01; break; case 4: // Purple obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x89); obj.AnimationGroupIndex = 0x02; break; // Gem container case 16: case 17: case 18: case 19: case 20: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x7E); obj.AnimationGroupIndex = 0x05; break; case 32: case 33: case 34: case 35: case 36: obj.AnimSetIndex = GBAIsometric_Spyro_ObjInit.ConvertAnimSetIndex(Context.Settings.GameModeSelection, 0x7A); obj.AnimationGroupIndex = 0x04; break; default: obj.AnimSetIndex = -1; break; } break; default: obj.AnimSetIndex = -1; break; } if (obj.AnimSetIndex == -1 && !obj.IsEditorObj) { Debug.LogWarning($"Type {obj.Object.ObjType} with category {obj.Object.Category} has no graphics"); } } }
public override void InitObjects(Unity_Level level) { // Hard-code event animations for the different Rayman types Unity_ObjGraphics rayDes = null; var rayEvent = (Unity_Object_R1)level.Rayman ?? level.EventData.Cast <Unity_Object_R1>().FirstOrDefault(x => x.EventData.Type == R1_EventType.TYPE_RAY_POS); if (rayEvent != null) { rayDes = DES.ElementAtOrDefault(rayEvent.DESIndex)?.Data.Graphics; } if (rayDes == null) { return; } var miniRay = level.EventData.Cast <Unity_Object_R1>().FirstOrDefault(x => x.EventData.Type == R1_EventType.TYPE_DEMI_RAYMAN); var miniRayDESIndex = miniRay?.DESIndex; if (miniRayDESIndex == null && EventTemplates.ContainsKey(WldObjType.RayLittle)) { miniRayDESIndex = UsesPointers ? DESLookup.TryGetItem(EventTemplates[WldObjType.RayLittle].ImageDescriptorsPointer?.AbsoluteOffset ?? 0, -1) : (int)EventTemplates[WldObjType.RayLittle].PC_ImageDescriptorsIndex; } if (miniRayDESIndex != null) { var miniRayDes = DES.ElementAtOrDefault(miniRayDESIndex.Value)?.Data.Graphics; if (miniRayDes != null) { miniRayDes.Animations = rayDes.Animations.Select(anim => { var newAnim = new Unity_ObjAnimation { Frames = anim.Frames.Select(x => new Unity_ObjAnimationFrame(x.SpriteLayers.Select(l => new Unity_ObjAnimationPart() { ImageIndex = l.ImageIndex, XPosition = l.XPosition / 2, YPosition = l.YPosition / 2, IsFlippedHorizontally = l.IsFlippedHorizontally, IsFlippedVertically = l.IsFlippedVertically, }).ToArray())).ToArray() }; return(newAnim); }).ToList(); } } var badRay = level.EventData.Cast <Unity_Object_R1>().FirstOrDefault(x => x.EventData.Type == R1_EventType.TYPE_BLACK_RAY); if (badRay != null) { var badRayDes = DES.ElementAtOrDefault(badRay.DESIndex)?.Data.Graphics; if (badRayDes != null) { badRayDes.Animations = rayDes.Animations; } } // Set frames for linked events for (int i = 0; i < level.EventData.Count; i++) { // Recreated from allocateOtherPosts var baseEvent = (Unity_Object_R1)level.EventData[i]; var linkedIndex = LinkTable[i]; if (baseEvent.EventData.Type.UsesRandomFrameLinks() && i != linkedIndex) { var index = 0; do { index++; var e = (Unity_Object_R1)level.EventData[linkedIndex]; e.ForceFrame = (byte)((baseEvent.ForceFrame + index) % (e.CurrentAnimation?.Frames.Length ?? 1)); e.XPosition = (short)(baseEvent.EventData.XPosition + 32 * index * (baseEvent.EventData.HitPoints - 2)); e.YPosition = baseEvent.YPosition; linkedIndex = LinkTable[linkedIndex]; } while (i != linkedIndex); } } // Set DES and ETA for worldmap if (Context.Settings.R1_World == R1_World.Menu) { var mapObj = EventTemplates[WldObjType.MapObj]; var rayman = level.Rayman as Unity_Object_R1; // Change Rayman to small Rayman if (miniRayDESIndex != null && rayman != null) { rayman.DESIndex = miniRayDESIndex.Value; rayman.EventData.OffsetBX = (byte)(rayman.EventData.OffsetBX / 2); rayman.EventData.OffsetBY = (byte)(rayman.EventData.OffsetBY / 2); } // Set Rayman's properties if (rayman != null) { rayman.EventData.Etat = rayman.EventData.InitialEtat = 0; rayman.EventData.SubEtat = rayman.EventData.InitialSubEtat = 0; if (Context.Settings.EngineVersion == EngineVersion.R1_PC_Kit) { // ? rayman.XPosition = (short)(level.EventData[0].XPosition + 42 - rayman.EventData.OffsetBX); rayman.YPosition = (short)(level.EventData[0].YPosition + 48 - rayman.EventData.OffsetBY); } else if (Context.Settings.EngineVersion == EngineVersion.R1_PC_Edu || Context.Settings.EngineVersion == EngineVersion.R1_PS1_Edu) { // ? rayman.XPosition = (short)(level.EventData[0].XPosition + 42 + 44 - rayman.EventData.OffsetBX); rayman.YPosition = (short)(level.EventData[0].YPosition + 48 + 24 - rayman.EventData.OffsetBY); rayman.EventData.PC_Flags |= R1_EventData.PC_EventFlags.IsFlipped; } else { rayman.XPosition = (short)(level.EventData[0].XPosition + 70 - rayman.EventData.OffsetBX + 9); // The game does +4 instead of 9 - why? rayman.YPosition = (short)(level.EventData[0].YPosition + 64 - rayman.EventData.OffsetBY + 8); // Is this correct? } } foreach (var e in level.EventData.Cast <Unity_Object_R1>().Select(x => x.EventData)) { e.ImageDescriptorsPointer = mapObj.ImageDescriptorsPointer; e.ImageBufferPointer = mapObj.ImageBufferPointer; e.AnimDescriptorsPointer = mapObj.AnimDescriptorsPointer; e.ETAPointer = mapObj.ETAPointer; e.PC_ImageDescriptorsIndex = mapObj.PC_ImageDescriptorsIndex; e.PC_ImageBufferIndex = mapObj.PC_ImageBufferIndex; e.PC_AnimationDescriptorsIndex = mapObj.PC_AnimationDescriptorsIndex; e.PC_ETAIndex = mapObj.PC_ETAIndex; } } }
public override void InitObjects(Unity_Level level) { var objects = level.EventData.Cast <Unity_Object_BaseGBAVVIsometric>().ToArray(); float minHeight = Mathf.Min(0, MapData.CollisionMap.Min(c => MapData.CollisionTiles[c].Height.AsFloat)); const float scale = 64f / 12; for (int i = 0; i < objects.Length; i++) { var obj = objects[i]; obj.UpdateAnimIndex(); if (obj is Unity_Object_GBAVVIsometric_TargetObjTarget) { continue; } float yScaled = obj.YPos * scale; float xScaled = obj.XPos * scale; var collY = Mathf.FloorToInt(yScaled); var collX = Mathf.FloorToInt(xScaled); /*var collY = (((long)obj.YPos.Value) * 0x15555556) >> 0x2a; * var collX = (((long)obj.XPos.Value) * 0x15555556) >> 0x2a;*/ var tile = MapData.CollisionTiles[MapData.CollisionMap[collY * MapData.CollisionWidth + collX]]; obj.Height = (tile.Height - minHeight) / scale; int baseType = (int)Unity_IsometricCollisionTile.CollisionType.GBAVV_Solid_0; var type = (Unity_IsometricCollisionTile.CollisionType)(baseType + GetCommonCollisionTypeFunc(tile.TypeIndex)); switch (type) { case Unity_IsometricCollisionTile.CollisionType.GBAVV_Corner_12: case Unity_IsometricCollisionTile.CollisionType.GBAVV_Corner_17: case Unity_IsometricCollisionTile.CollisionType.GBAVV_Corner_19: case Unity_IsometricCollisionTile.CollisionType.GBAVV_Corner_21: case Unity_IsometricCollisionTile.CollisionType.GBAVV_Corner_22: case Unity_IsometricCollisionTile.CollisionType.GBAVV_Corner_24: case Unity_IsometricCollisionTile.CollisionType.GBAVV_Corner_26: case Unity_IsometricCollisionTile.CollisionType.GBAVV_Corner_28: case Unity_IsometricCollisionTile.CollisionType.GBAVV_Corner_32: case Unity_IsometricCollisionTile.CollisionType.GBAVV_Corner_35: // Diagonal bool isHigh = false; switch (tile.Shape % 4) { case 0: // bottom is higher (high x & y) isHigh = (yScaled - collY) + (xScaled - collX) >= 1f; break; case 1: // left is higher (high y, low x) isHigh = (yScaled - collY) + (1f - (xScaled - collX)) >= 1f; break; case 2: // top is higher (low x & y) isHigh = (1f - (yScaled - collY)) + (1f - (xScaled - collX)) >= 1f; break; case 3: // right is higher (low y, high x) isHigh = (1f - (yScaled - collY)) + (xScaled - collX) >= 1f; break; } if (isHigh) { obj.Height += MapData.CollisionTypes[tile.TypeIndex].AdditionalHeight / scale; } break; case Unity_IsometricCollisionTile.CollisionType.GBAVV_Ramp_1: // Ramp float x = (xScaled - collX); float y = (yScaled - collY); float rampHeight = 0.1875f; switch (tile.Shape % 4) { case 0: // down left is higher (high y) obj.Height += (1 - Mathf.Sqrt(1 - y * y)) * rampHeight / scale; break; case 1: // up left is higher (low x) obj.Height += (1 - Mathf.Sqrt(1 - (1 - x) * (1 - x))) * rampHeight / scale; break; case 2: // up right is higher (low y) obj.Height += (1 - Mathf.Sqrt(1 - (1 - y) * (1 - y))) * rampHeight / scale; break; case 3: // down right is higher (high x) obj.Height += (1 - Mathf.Sqrt(1 - x * x)) * rampHeight / scale; break; } break; default: break; } if (obj is Unity_Object_GBAVVIsometric_TargetObj t) { objects[t.LinkIndex].Height = t.Height; } if (i == 0 || !(obj is Unity_Object_GBAVVIsometric_Obj)) { continue; } var prevObjIndex = i - 1; var prevObj = objects[prevObjIndex]; if (obj.XPos.Value == prevObj.XPos.Value && obj.YPos.Value == prevObj.YPos.Value) { while (true) { obj.XPos.Value += 0x100; obj.YPos.Value += 0x100; obj.Height += ((0x1000 / (float)(1 << 16))) / scale; if (prevObj.XPos.Value != objects[prevObjIndex - 1].XPos.Value || prevObj.YPos.Value != objects[prevObjIndex - 1].YPos.Value) { break; } prevObjIndex--; prevObj = objects[prevObjIndex]; } } } //foreach (Unity_Object_BaseGBAVVIsometric obj in objects) //{ // obj.XPos.Value += obj.GraphicsData?.CrashAnim?.XPos?.Value / 2 ?? 0; // obj.YPos.Value += obj.GraphicsData?.CrashAnim?.YPos?.Value / 2 ?? 0; //} }