bool OpenChest(ChestEvent e, Action <bool> continuation) { Raise(new PushSceneEvent(SceneId.Inventory)); _continuation?.Invoke(false); SetMode(e); _continuation = continuation; return(true); }
/// <summary> /// Encodes in the specified stream. /// </summary> /// <param name="Stream">The stream.</param> public static void Encode(ChecksumEncoder Stream) { Stream.WriteVInt(ChestManager.Chests.Count); foreach (ChestEvent ChestEvent in ChestManager.Chests) { ChestEvent.Encode(Stream); } }
/// <summary> /// Decodes from the specified stream. /// </summary> /// <param name="Stream">The stream.</param> public static void Decode(ByteStream Stream) { int Length = Stream.ReadVInt(); for (int i = 0; i < Length; i++) { ChestEvent ChestEvent = new ChestEvent(); ChestEvent.Decode(Stream); ChestManager.Chests.Add(ChestEvent); } }
public InventoryScreen( IEvent modeEvent, PartyMemberId activeCharacter, Func <InventoryPage> getPage, Action <InventoryPage> setPage) : base(DialogPositioning.TopLeft) { if (modeEvent == null) { throw new ArgumentNullException(nameof(modeEvent)); } if (getPage == null) { throw new ArgumentNullException(nameof(getPage)); } if (setPage == null) { throw new ArgumentNullException(nameof(setPage)); } var leftPane = modeEvent switch { InventoryOpenEvent ioe => new InventoryCharacterPane(activeCharacter, getPage, setPage), MerchantEvent me => new InventoryMerchantPane(me.MerchantId), ChestEvent ce => ce.PickDifficulty == 0 ? (IUiElement) new InventoryChestPane(ce.ChestId) : new InventoryLockPane(ce), DoorEvent de => new InventoryLockPane(de), _ => throw new InvalidOperationException($"Unexpected inventory mode event {modeEvent}") }; var middlePane = new InventoryMidPane(activeCharacter); var rightPane = new InventoryRightPane(activeCharacter, modeEvent is MerchantEvent); // var frameDivider = new FrameDivider(135, 0, 4, 192); AttachChild(new UiFixedPositionElement(Base.UiBackground.Slab, UiConstants.UiExtents)); AttachChild(new FixedPosition(new Rectangle(0, 0, 135, UiConstants.ActiveAreaExtents.Height), leftPane)); AttachChild(new FixedPosition(new Rectangle(142, 0, 134, UiConstants.ActiveAreaExtents.Height), middlePane)); AttachChild(new FixedPosition(new Rectangle(280, 0, 71, UiConstants.ActiveAreaExtents.Height), rightPane)); } }
public void GenerateMesh(MapGenerator lastMap = null) { TacticsTerrainMesh mesh = GetComponent <TacticsTerrainMesh>(); // copy oldbie values if needed if (lastMap != null) { sizeInRoomsMin = lastMap.sizeInRoomsMin; sizeInRoomsMax = lastMap.sizeInRoomsMax; stairLength = lastMap.stairLength; startEventPrefab = lastMap.startEventPrefab; endEventPrefab = lastMap.endEventPrefab; impassEventPrefab = lastMap.impassEventPrefab; defaultImpassTile = lastMap.defaultImpassTile; chestEventPrefab = lastMap.chestEventPrefab; targetPrefab = lastMap.targetPrefab; finalLevel = lastMap.finalLevel; finalLevelPrefab = lastMap.finalLevelPrefab; table = lastMap.table; level = lastMap.level + 1; } if (level == finalLevel) { Map other = Instantiate(finalLevelPrefab); Global.Instance().Maps.activeMap = other; return; } // wipe what's already there mesh.ClearTiles(); foreach (MapEvent toRemove in GetComponent <Map>().GetEvents <MapEvent>()) { if (toRemove.GetComponent <PCEvent>() == null) { if (toRemove.GetComponent <BattleEvent>() && Application.isPlaying) { GetComponent <BattleController>().RemoveUnit(toRemove.GetComponent <BattleEvent>().unit); } GetComponent <Map>().RemoveEvent(toRemove, true); } } // work out some constants if (Flip()) { sizeInRooms = new Vector2Int(sizeInRoomsMin.x, sizeInRoomsMax.y); } else { sizeInRooms = new Vector2Int(sizeInRoomsMax.x, sizeInRoomsMin.y); } int seed = (int)DateTime.Now.Ticks; Random.InitState(seed); Debug.Log("using seed " + seed); rooms = new RoomInfo[sizeInRooms.x, sizeInRooms.y]; sizeInCells = sizeInRooms * 2 - new Vector2Int(1, 1); cells = new CellInfo[sizeInCells.x, sizeInCells.y]; // are we going to add the starting stairwell on the left or right? if (lastMap != null) { if (lastMap.endStairsNW) { entryRoomCoords = new Vector2Int(Random.Range(0, 2), 0); startStairsSW = false; } else { entryRoomCoords = new Vector2Int(0, Random.Range(0, 2)); startStairsSW = true; } exitRoomCoords = new Vector2Int(sizeInRooms.x - 1, sizeInRooms.y - 1); if (Flip()) { exitRoomCoords.x -= Random.Range(0, 2); } else { exitRoomCoords.y -= Random.Range(0, 2); } } else { if (entryRoomCoords == Vector2Int.zero) { startStairsSW = Flip(); } else if (entryRoomCoords.y > 0) { startStairsSW = true; } else { startStairsSW = false; } } if (exitRoomCoords == new Vector2Int(sizeInRooms.x - 1, sizeInRooms.y - 1)) { endStairsNW = Flip(); } else if (exitRoomCoords.x < sizeInRooms.x - 1) { endStairsNW = true; } else { endStairsNW = false; } // set the size and start of each room and hallway cells = new CellInfo[sizeInCells.x, sizeInCells.y]; widths = new int[sizeInCells.x]; heights = new int[sizeInCells.y]; for (int y = 0; y < sizeInCells.y; y += 1) { if (y % 2 == 1) { heights[y] = RandomHallSize(); } else { heights[y] = RandomRoomSize(); } } for (int x = 0; x < sizeInCells.x; x += 1) { if (x % 2 == 1) { widths[x] = RandomHallSize(); } else { widths[x] = RandomRoomSize(); } } for (int y = 0; y < sizeInCells.y; y += 1) { for (int x = 0; x < sizeInCells.x; x += 1) { cells[x, y] = new CellInfo(x, y, widths[x], heights[y]); if (x % 2 == 1 || y % 2 == 1) { if (x % 2 == 1 && y % 2 == 1) { cells[x, y].type = CellInfo.CellType.Pillar; } else { cells[x, y].type = CellInfo.CellType.Hall; } } else { cells[x, y].type = CellInfo.CellType.Room; } if (x == 0) { cells[x, y].startX = startStairsSW ? stairLength : 0; } else { cells[x, y].startX = cells[x - 1, y].startX + widths[x - 1]; } if (y == 0) { cells[x, y].startY = startStairsSW ? 0 : stairLength; } else { cells[x, y].startY = cells[x, y - 1].startY + heights[y - 1]; } } } // set the elevation of each room for (int diag = 0; diag < sizeInRooms.x + sizeInRooms.y; diag += 1) { int x = diag; int y = 0; while (x >= 0) { if (x < sizeInRooms.x && y < sizeInRooms.y) { float z; if (x == 0 && y == 0) { z = stairLength / 2.0f + 0.5f; } else { float oldZ; if (y == 0) { oldZ = rooms[x - 1, y].z; } else if (x == 0) { oldZ = rooms[x, y - 1].z; } else { oldZ = Math.Max(rooms[x, y - 1].z, rooms[x - 1, y].z); } z = RandomNewZ(oldZ); } rooms[x, y] = new RoomInfo(cells[x * 2, y * 2], z); } x -= 1; y += 1; } } // determine the initial passability map cells[0, 0].connected = true; UpdatePassability(); // knock down walls until all rooms are accessible List <RoomInfo> roomsToGo = new List <RoomInfo>(); for (int x = 0; x < sizeInRooms.x; x += 1) { for (int y = 0; y < sizeInRooms.y; y += 1) { roomsToGo.Add(rooms[x, y]); } } while (roomsToGo.Count > 0) { int index = Random.Range(0, roomsToGo.Count); RoomInfo room = roomsToGo[index]; if (room.cell.connected) { roomsToGo.RemoveAt(index); continue; } List <RoomInfo> adjacents = room.cell.AdjacentRooms(this); RandUtils.Shuffle(adjacents); RoomInfo adj = null; foreach (RoomInfo adj2 in adjacents) { if (!adj2.cell.connected) { continue; } CellInfo connector = cells[(room.cell.x + adj2.cell.x) / 2, (room.cell.y + adj2.cell.y) / 2]; if (Mathf.Abs(adj2.z - room.z) / (widths[connector.x] * heights[connector.y]) > 2) { continue; } adj = adj2; break; } if (adj == null) { continue; } roomsToGo.RemoveAt(index); cells[(room.cell.x + adj.cell.x) / 2, (room.cell.y + adj.cell.y) / 2].connected = true; room.cell.connected = adj.cell.connected; if (adj.cell.connected) { UpdatePassability(); } } // convert halls into stairways as needed for (int y = 0; y < sizeInCells.y; y += 1) { for (int x = 0; x < sizeInCells.x; x += 1) { CellInfo cell = cells[x, y]; if (cell.type == CellInfo.CellType.Hall && cell.connected) { List <RoomInfo> rooms = cell.AdjacentRooms(this); float deltaZ = Math.Abs(rooms[0].z - rooms[1].z); if (deltaZ > 1) { if (deltaZ > cell.sizeX / 2 && deltaZ > cell.sizeY / 2) { cell.type = CellInfo.CellType.Switchback; } else { cell.type = CellInfo.CellType.Stairway; } } } } } // set pillar heights to sane values for (int y = 0; y < sizeInCells.y; y += 1) { for (int x = 0; x < sizeInCells.x; x += 1) { CellInfo cell = cells[x, y]; if (cell.type == CellInfo.CellType.Pillar) { CellInfo n = cells[x, y + 1]; CellInfo e = cells[x + 1, y]; CellInfo s = cells[x, y - 1]; CellInfo w = cells[x - 1, y]; if (n.type == CellInfo.CellType.Stairway && e.type == CellInfo.CellType.Stairway) { cell.pillarZ = rooms[(x + 1) / 2, (y + 1) / 2].z; e.stairAnchoredHigh = true; n.stairAnchoredHigh = true; } else if (s.type == CellInfo.CellType.Switchback && s.sizeX > 1) { cell.pillarZ = (s.AdjacentRooms(this)[0].z + s.AdjacentRooms(this)[1].z) / 2.0f; } else if (w.type == CellInfo.CellType.Switchback && s.sizeY > 1) { cell.pillarZ = (w.AdjacentRooms(this)[0].z + w.AdjacentRooms(this)[1].z) / 2.0f; } else { cell.pillarZ = rooms[(x - 1) / 2, (y - 1) / 2].z; } } } } // decorator fringe RoomInfo cornerRoom = rooms[sizeInRooms.x - 1, sizeInRooms.y - 1]; int wallX = cornerRoom.cell.startX + cornerRoom.cell.sizeX + 1; int wallY = cornerRoom.cell.startY + cornerRoom.cell.sizeY + 1; mesh.Resize(new Vector2Int( wallX + (endStairsNW ? 0 : stairLength), wallY + (endStairsNW ? stairLength : 0)), 0.0f); for (int x = startStairsSW ? stairLength : 0; x < wallX; x += 1) { mesh.SetHeight(x, wallY - 1, cornerRoom.z + MaxHeightDelta + 1); mesh.SetTile(x, wallY - 1, defaultImpassTile); } for (int y = startStairsSW ? 0 : stairLength; y < wallY; y += 1) { mesh.SetHeight(wallX - 1, y, cornerRoom.z + MaxHeightDelta + 1); mesh.SetTile(wallX - 1, y, defaultImpassTile); } // render terrain for (int y = 0; y < sizeInCells.y; y += 1) { for (int x = 0; x < sizeInCells.x; x += 1) { CellInfo cell = cells[x, y]; if (cell.type != CellInfo.CellType.Pillar) { cell.RenderTerrain(this, mesh); } } } for (int y = 0; y < sizeInCells.y; y += 1) { for (int x = 0; x < sizeInCells.x; x += 1) { CellInfo cell = cells[x, y]; if (cell.type == CellInfo.CellType.Pillar) { cell.RenderTerrain(this, mesh); } } } // render rooms for (int x = 0; x < sizeInRooms.x; x += 1) { for (int y = 0; y < sizeInRooms.y; y += 1) { rooms[x, y].FillWithVault(mesh); } } // add the starter stairs RoomInfo startRoom = rooms[entryRoomCoords.x, entryRoomCoords.y]; int stairX, stairZ; if (startStairsSW) { stairX = startRoom.cell.startX - 1; stairZ = startRoom.cell.startY + (startRoom.cell.sizeY / 2); } else { stairX = startRoom.cell.startX + (startRoom.cell.sizeX / 2); stairZ = startRoom.cell.startY - 1; } float stairY = startRoom.z - 0.5f; for (int i = 0; i < stairLength; i += 1) { mesh.SetHeight(stairX, stairZ, stairY); MapEvent3D ev; if (i == 0) { ev = Instantiate(startEventPrefab); } else { ev = Instantiate(impassEventPrefab); } GetComponent <Map>().AddEvent(ev); ev.SetLocation(new Vector2Int(stairX, stairZ)); stairY -= 0.5f; stairX += startStairsSW ? -1 : 0; stairZ += startStairsSW ? 0 : -1; } // add the end stairs RoomInfo endRoom = rooms[exitRoomCoords.x, exitRoomCoords.y]; if (endStairsNW) { stairX = endRoom.cell.startX + (endRoom.cell.sizeX / 2); stairZ = endRoom.cell.startY + endRoom.cell.sizeY; } else { stairX = endRoom.cell.startX + endRoom.cell.sizeX; stairZ = endRoom.cell.startY + (endRoom.cell.sizeY / 2); } stairY = endRoom.z + 0.5f; for (int i = 0; i < stairLength; i += 1) { MapEvent3D ev; if (i == 0) { ev = Instantiate(endEventPrefab); } else { ev = Instantiate(impassEventPrefab); } GetComponent <Map>().AddEvent(ev); ev.SetLocation(new Vector2Int(stairX, stairZ)); mesh.SetHeight(stairX, stairZ, stairY); stairY += 0.5f; stairX += endStairsNW ? 0 : 1; stairZ += endStairsNW ? 1 : 0; } // add the zoom target MapEvent3D zoom = Instantiate(targetPrefab); GetComponent <Map>().AddEvent(zoom); zoom.SetLocation(new Vector2Int(mesh.size.x / 2, mesh.size.y / 2)); // generate the encounters List <RoomInfo> validRooms = new List <RoomInfo>(); for (int x = 0; x < sizeInRooms.x; x += 1) { for (int y = 0; y < sizeInRooms.y; y += 1) { if (x != entryRoomCoords.x || y != entryRoomCoords.y) { validRooms.Add(rooms[x, y]); } } } List <Encounter> encounters = table.GenerateEncounters(level); foreach (Encounter encounter in encounters) { RoomInfo room = validRooms[Random.Range(0, validRooms.Count)]; Vector2Int loc = new Vector2Int( Random.Range(room.cell.startX, room.cell.startX + room.cell.sizeX), Random.Range(room.cell.startY, room.cell.startY + room.cell.sizeY)); encounter.PlaceAt(GetComponent <Map>(), loc); } // generate the chests List <Item> items = table.GenerateItems(level); foreach (Item item in items) { ChestEvent chest = Instantiate(chestEventPrefab); GetComponent <Map>().AddEvent(chest.GetComponent <MapEvent>()); Vector2Int loc; do { RoomInfo room = validRooms[Random.Range(0, validRooms.Count)]; loc = new Vector2Int( Random.Range(room.cell.startX + 1, room.cell.startX + room.cell.sizeX - 1), Random.Range(room.cell.startY + 1, room.cell.startY + room.cell.sizeY - 1)); } while (!chest.GetComponent <MapEvent>().CanPassAt(loc)); chest.PopulateAndPlace(item, loc); } mesh.Rebuild(true); }
static Event ParseEvent(IDataReader dataReader) { Event @event; var type = (EventType)dataReader.ReadByte(); switch (type) { case EventType.Teleport: { // 1. byte is the x coordinate // 2. byte is the y coordinate // 3. byte is the character direction // Then 1 unknown byte // Then 1 byte for the transtion type (0-5) // Then a word for the map index // Then 2 unknown bytes (seem to be 00 FF) uint x = dataReader.ReadByte(); uint y = dataReader.ReadByte(); var direction = (CharacterDirection)dataReader.ReadByte(); var unknown1 = dataReader.ReadByte(); var transition = (TeleportEvent.TransitionType)dataReader.ReadByte(); uint mapIndex = dataReader.ReadWord(); var unknown2 = dataReader.ReadBytes(2); @event = new TeleportEvent { MapIndex = mapIndex, X = x, Y = y, Direction = direction, Transition = transition, Unknown1 = unknown1, Unknown2 = unknown2, }; break; } case EventType.Door: { // 1. byte is a lockpicking chance reduction (0: already open, 100: can't open via lockpicking) // 2. byte is the door index (used for unlock bits in savegame) // 3. byte is an optional text index that is shown initially (0xff = no text) // 4. byte is an optional text index if the door was unlocked (0xff = no text) // 5. byte is unknown (always 0) // word at position 6 is the key index if a key must unlock it // last word is the event index (0-based) of the event that is called when unlocking fails var lockpickingChanceReduction = dataReader.ReadByte(); var doorIndex = dataReader.ReadByte(); var textIndex = dataReader.ReadByte(); var unlockTextIndex = dataReader.ReadByte(); var unused = dataReader.ReadByte(); uint keyIndex = dataReader.ReadWord(); var unlockFailEventIndex = dataReader.ReadWord(); @event = new DoorEvent { LockpickingChanceReduction = lockpickingChanceReduction, DoorIndex = doorIndex, TextIndex = textIndex, UnlockTextIndex = unlockTextIndex, Unused = unused, KeyIndex = keyIndex, UnlockFailedEventIndex = unlockFailEventIndex }; break; } case EventType.Chest: { // 1. byte is a lockpicking chance reduction (0: already open, 100: can't open via lockpicking) // 2. byte are the chest flags // 3. byte is an optional text index (0xff = no text) // 4. byte is the chest index (0-based) // 5. byte (0 = chest, 1 = pile/removable loot or item) or "remove if empty" // word at position 6 is the key index if a key must unlock it // last word is the event index (0-based) of the event that is called when unlocking fails var lockpickingChanceReduction = dataReader.ReadByte(); var flags = (ChestEvent.ChestFlags)dataReader.ReadByte(); var textIndex = dataReader.ReadByte(); uint chestIndex = dataReader.ReadByte(); bool removeWhenEmpty = dataReader.ReadByte() != 0; uint keyIndex = dataReader.ReadWord(); var unlockFailEventIndex = dataReader.ReadWord(); @event = new ChestEvent { LockpickingChanceReduction = lockpickingChanceReduction, Flags = flags, TextIndex = textIndex, ChestIndex = chestIndex, RemoveWhenEmpty = removeWhenEmpty, KeyIndex = keyIndex, UnlockFailedEventIndex = unlockFailEventIndex }; break; } case EventType.PopupText: { // event image index (0xff = no image) // trigger (1 = move, 2 = eye cursor, 3 = both) // unknown boolean // map text index as word // 4 unknown bytes var eventImageIndex = dataReader.ReadByte(); var popupTrigger = (EventTrigger)dataReader.ReadByte(); var unknownBool = dataReader.ReadByte() != 0; var textIndex = dataReader.ReadWord(); var unknown = dataReader.ReadBytes(4); @event = new PopupTextEvent { EventImageIndex = eventImageIndex, PopupTrigger = popupTrigger, TextIndex = textIndex, UnknownBool = unknownBool, Unknown = unknown }; break; } case EventType.Spinner: { var direction = (CharacterDirection)dataReader.ReadByte(); var unused = dataReader.ReadBytes(8); @event = new SpinnerEvent { Direction = direction, Unused = unused }; break; } case EventType.Trap: { var ailment = (TrapEvent.TrapAilment)dataReader.ReadByte(); var target = (TrapEvent.TrapTarget)dataReader.ReadByte(); var affectedGenders = (GenderFlag)dataReader.ReadByte(); var baseDamage = dataReader.ReadByte(); var unused = dataReader.ReadBytes(5); // unused @event = new TrapEvent { Ailment = ailment, Target = target, AffectedGenders = affectedGenders, BaseDamage = baseDamage, Unused = unused }; break; } case EventType.RemoveBuffs: { byte affectedBuffs = dataReader.ReadByte(); var unused = dataReader.ReadBytes(8); @event = new RemoveBuffsEvent { AffectedBuff = affectedBuffs == 0 ? (ActiveSpellType?)null: (ActiveSpellType)(affectedBuffs - 1), Unused = unused }; break; } case EventType.Riddlemouth: { var introTextIndex = dataReader.ReadByte(); var solutionTextIndex = dataReader.ReadByte(); var unused = dataReader.ReadBytes(3); var correctAnswerTextIndex1 = dataReader.ReadWord(); var correctAnswerTextIndex2 = dataReader.ReadWord(); @event = new RiddlemouthEvent { RiddleTextIndex = introTextIndex, SolutionTextIndex = solutionTextIndex, CorrectAnswerDictionaryIndex1 = correctAnswerTextIndex1, CorrectAnswerDictionaryIndex2 = correctAnswerTextIndex2, Unused = unused }; break; } case EventType.Award: { var awardType = (AwardEvent.AwardType)dataReader.ReadByte(); var awardOperation = (AwardEvent.AwardOperation)dataReader.ReadByte(); var random = dataReader.ReadByte() != 0; var awardTarget = (AwardEvent.AwardTarget)dataReader.ReadByte(); var unknown = dataReader.ReadByte(); var awardTypeValue = dataReader.ReadWord(); var value = dataReader.ReadWord(); @event = new AwardEvent { TypeOfAward = awardType, Operation = awardOperation, Random = random, Target = awardTarget, AwardTypeValue = awardTypeValue, Value = value, Unknown = unknown }; break; } case EventType.ChangeTile: { var x = dataReader.ReadByte(); var y = dataReader.ReadByte(); var unknown = dataReader.ReadBytes(3); var frontTileIndex = dataReader.ReadWord(); // also wall/object index in lower byte var mapIndex = dataReader.ReadWord(); @event = new ChangeTileEvent { X = x, Y = y, FrontTileIndex = frontTileIndex, MapIndex = mapIndex, Unknown = unknown }; break; } case EventType.StartBattle: { var unknown1 = dataReader.ReadBytes(6); var monsterGroupIndex = dataReader.ReadByte(); var unknown2 = dataReader.ReadBytes(2); @event = new StartBattleEvent { MonsterGroupIndex = monsterGroupIndex, Unknown1 = unknown1, Unknown2 = unknown2 }; break; } case EventType.EnterPlace: { // map text index when closed (0xff is default message) // place type (see PlaceType) // opening hour // closing hour // text index for using the place (sleep, train, buy, etc) // place index (1-based, word) // 2 unknown bytes var textIndexWhenClosed = dataReader.ReadByte(); var placeType = (PlaceType)dataReader.ReadByte(); var openingHour = dataReader.ReadByte(); var closingHour = dataReader.ReadByte(); var usePlaceTextIndex = dataReader.ReadByte(); var placeIndex = dataReader.ReadWord(); var merchantIndex = dataReader.ReadWord(); @event = new EnterPlaceEvent { ClosedTextIndex = textIndexWhenClosed, PlaceType = placeType, OpeningHour = openingHour, ClosingHour = closingHour, PlaceIndex = placeIndex, UsePlaceTextIndex = usePlaceTextIndex, MerchantDataIndex = merchantIndex }; break; } case EventType.Condition: { var conditionType = (ConditionEvent.ConditionType)dataReader.ReadByte(); var value = dataReader.ReadByte(); var count = dataReader.ReadByte(); var unknown1 = dataReader.ReadBytes(2); var objectIndex = dataReader.ReadWord(); var jumpToIfNotFulfilled = dataReader.ReadWord(); @event = new ConditionEvent { TypeOfCondition = conditionType, ObjectIndex = objectIndex, Value = value, Count = count, Unknown1 = unknown1, ContinueIfFalseWithMapEventIndex = jumpToIfNotFulfilled }; break; } case EventType.Action: { var actionType = (ActionEvent.ActionType)dataReader.ReadByte(); var value = dataReader.ReadByte(); var count = dataReader.ReadByte(); var unknown1 = dataReader.ReadBytes(2); var objectIndex = dataReader.ReadWord(); var unknown2 = dataReader.ReadBytes(2); @event = new ActionEvent { TypeOfAction = actionType, ObjectIndex = objectIndex, Value = value, Unknown1 = unknown1, Unknown2 = unknown2 }; break; } case EventType.Dice100Roll: { var chance = dataReader.ReadByte(); var unused = dataReader.ReadBytes(6); var jumpToIfNotFulfilled = dataReader.ReadWord(); @event = new Dice100RollEvent { Chance = chance, Unused = unused, ContinueIfFalseWithMapEventIndex = jumpToIfNotFulfilled }; break; } case EventType.Conversation: { var interaction = (ConversationEvent.InteractionType)dataReader.ReadByte(); var unused1 = dataReader.ReadBytes(4); // unused var value = dataReader.ReadWord(); var unused2 = dataReader.ReadBytes(2); // unused @event = new ConversationEvent { Interaction = interaction, Value = value, Unused1 = unused1, Unused2 = unused2 }; break; } case EventType.PrintText: { var npcTextIndex = dataReader.ReadByte(); var unused = dataReader.ReadBytes(8); // unused @event = new PrintTextEvent { NPCTextIndex = npcTextIndex, Unused = unused }; break; } case EventType.Create: { var createType = (CreateEvent.CreateType)dataReader.ReadByte(); var unused = dataReader.ReadBytes(4); var amount = dataReader.ReadWord(); var itemIndex = dataReader.ReadWord(); @event = new CreateEvent { TypeOfCreation = createType, Unused = unused, Amount = amount, ItemIndex = itemIndex }; break; } case EventType.Decision: { var textIndex = dataReader.ReadByte(); var unknown1 = dataReader.ReadBytes(6); var noEventIndex = dataReader.ReadWord(); @event = new DecisionEvent { TextIndex = textIndex, NoEventIndex = noEventIndex, Unknown1 = unknown1 }; break; } case EventType.ChangeMusic: { var musicIndex = dataReader.ReadWord(); var volume = dataReader.ReadByte(); var unknown1 = dataReader.ReadBytes(6); @event = new ChangeMusicEvent { MusicIndex = musicIndex, Volume = volume, Unknown1 = unknown1 }; break; } case EventType.Exit: { @event = new ExitEvent { Unused = dataReader.ReadBytes(9) }; break; } case EventType.Spawn: { // byte0: x // byte1: y // byte2: travel type (see TravelType) // byte3-4: unused? // byte5-6: map index // byte7-8: unused? var x = dataReader.ReadByte(); var y = dataReader.ReadByte(); var travelType = (TravelType)dataReader.ReadByte(); var unknown1 = dataReader.ReadBytes(2); // unknown var mapIndex = dataReader.ReadWord(); var unknown2 = dataReader.ReadBytes(2); // unknown @event = new SpawnEvent { X = x, Y = y, TravelType = travelType, Unknown1 = unknown1, MapIndex = mapIndex, Unknown2 = unknown2 }; break; } case EventType.Interact: { @event = new InteractEvent { Unused = dataReader.ReadBytes(9) }; break; } default: { @event = new DebugEvent { Data = dataReader.ReadBytes(9) }; break; } } @event.Type = type; return(@event); }
public void StepOrAttack(EightDir dir, Result <bool> executeResult) { MapEvent parent = me; Vector2Int vectors = me.location; Vector2Int target = vectors + dir.XY(); GetComponent <CharaEvent>().facing = dir; List <MapEvent> targetEvents = me.map.GetEventsAt(target); if (!GetComponent <BattleEvent>().CanCrossTileGradient(parent.location, target)) { executeResult.value = false; return; } List <MapEvent> toCollide = new List <MapEvent>(); bool passable = parent.CanPassAt(target); foreach (MapEvent targetEvent in targetEvents) { toCollide.Add(targetEvent); passable &= targetEvent.IsPassableBy(parent); } if (passable) { chara.PerformWhenDoneAnimating(me.StepRoutine(location, location + dir.XY(), false)); me.location = target; if (unit.Get(StatTag.MOVE) > 1) { unit.canActAgain = !unit.canActAgain; } else if (unit.Get(StatTag.MOVE) < 1) { unit.isRecovering = true; } if (GetComponent <PCEvent>() != null) { foreach (MapEvent targetEvent in toCollide) { if (targetEvent.switchEnabled) { StartCoroutine(CoUtils.RunWithCallback(targetEvent.CollideRoutine(GetComponent <PCEvent>()), () => { executeResult.value = true; })); return; } } } executeResult.value = true; return; } else { foreach (MapEvent targetEvent in toCollide) { float h1 = unit.battle.map.terrain.HeightAt(location); float h2 = unit.battle.map.terrain.HeightAt(target); //if (GetComponent<PCEvent>() != null) { // if (targetEvent.switchEnabled && !targetEvent.IsPassableBy(parent) // && Mathf.Abs(h1 - h2) <= AttackHeightMax) { // StartCoroutine(CollideRoutine(GetComponent<PCEvent>()); // } //} if (targetEvent.GetComponent <BattleEvent>() != null) { BattleEvent other = targetEvent.GetComponent <BattleEvent>(); if (unit.align != other.unit.align) { if (Mathf.Abs(h1 - h2) > AttackHeightMax) { if (GetComponent <PCEvent>() != null) { unit.battle.Log("Too high up to attack!"); } executeResult.value = false; return; } else { unit.MeleeAttack(other.unit); executeResult.value = true; return; } } } // 7drl antipattern hack alert if (GetComponent <PCEvent>() != null && targetEvent.GetComponent <ChestEvent>() != null) { ChestEvent chest = targetEvent.GetComponent <ChestEvent>(); if (!chest.opened) { StartCoroutine(CoUtils.RunWithCallback(chest.OpenRoutine(GetComponent <PCEvent>()), () => { executeResult.value = true; })); return; } } } } executeResult.value = false; }
public static IMapEvent SerdesEvent(IMapEvent e, ISerializer s, AssetId chainSource, TextId textSourceId, AssetMapping mapping) { if (s == null) { throw new ArgumentNullException(nameof(s)); } var initialPosition = s.Offset; s.Begin(); var type = s.EnumU8("Type", e?.EventType ?? MapEventType.UnkFf); e = type switch // Individual parsers handle byte range [1,9] { MapEventType.Action => ActionEvent.Serdes((ActionEvent)e, mapping, s), MapEventType.AskSurrender => AskSurrenderEvent.Serdes((AskSurrenderEvent)e, s), MapEventType.ChangeIcon => ChangeIconEvent.Serdes((ChangeIconEvent)e, mapping, s), MapEventType.ChangeUsedItem => ChangeUsedItemEvent.Serdes((ChangeUsedItemEvent)e, mapping, s), MapEventType.Chest => ChestEvent.Serdes((ChestEvent)e, mapping, s, textSourceId), MapEventType.CloneAutomap => CloneAutomapEvent.Serdes((CloneAutomapEvent)e, mapping, s), MapEventType.CreateTransport => CreateTransportEvent.Serdes((CreateTransportEvent)e, s), MapEventType.DataChange => DataChangeEvent.Serdes((DataChangeEvent)e, mapping, s), MapEventType.Door => DoorEvent.Serdes((DoorEvent)e, mapping, s, textSourceId), MapEventType.Encounter => EncounterEvent.Serdes((EncounterEvent)e, s), MapEventType.EndDialogue => EndDialogueEvent.Serdes((EndDialogueEvent)e, s), MapEventType.Execute => ExecuteEvent.Serdes((ExecuteEvent)e, s), MapEventType.MapExit => TeleportEvent.Serdes((TeleportEvent)e, mapping, s), MapEventType.Modify => ModifyEvent.BaseSerdes((ModifyEvent)e, mapping, chainSource, s), MapEventType.Offset => OffsetEvent.Serdes((OffsetEvent)e, s), MapEventType.Pause => PauseEvent.Serdes((PauseEvent)e, s), MapEventType.PlaceAction => PlaceActionEvent.Serdes((PlaceActionEvent)e, s), MapEventType.PlayAnimation => PlayAnimationEvent.Serdes((PlayAnimationEvent)e, mapping, s), MapEventType.Query => QueryEvent.Serdes((QueryEvent)e, mapping, s, textSourceId), MapEventType.RemovePartyMember => RemovePartyMemberEvent.Serdes((RemovePartyMemberEvent)e, mapping, s), MapEventType.Script => DoScriptEvent.Serdes((DoScriptEvent)e, mapping, s), MapEventType.Signal => SignalEvent.Serdes((SignalEvent)e, s), MapEventType.SimpleChest => SimpleChestEvent.Serdes((SimpleChestEvent)e, mapping, s), MapEventType.Sound => SoundEvent.Serdes((SoundEvent)e, mapping, s), MapEventType.Spinner => SpinnerEvent.Serdes((SpinnerEvent)e, s), MapEventType.StartDialogue => StartDialogueEvent.Serdes((StartDialogueEvent)e, mapping, s), MapEventType.Text => MapTextEvent.Serdes((MapTextEvent)e, mapping, s, textSourceId), MapEventType.Trap => TrapEvent.Serdes((TrapEvent)e, s), MapEventType.Wipe => WipeEvent.Serdes((WipeEvent)e, s), _ => DummyMapEvent.Serdes((DummyMapEvent)e, s, type) }; s.End(); if (e is IBranchingEvent) { s.Assert(s.Offset - initialPosition == 8, "Query events should always be 8 bytes"); } else { s.Assert(s.Offset - initialPosition == 10, "Non-query map events should always be 10 bytes"); } return(e); } /* == Binary Serialisable Event types: == * 1 Teleport (teleport 300 32 75) * 2 Door (open_door ...) * 3 Chest (open_chest ...) * 4 Text (map_text 100) * 5 Spinner (spinner ...) * 6 Trap (trap ...) * 7 ChangeUsedItem (change_used_item ...) * 8 DataChange (further subdivided by operation: min,max,?,set,add,sub,add%,sub%) * 0 Unk0 (TODO) * 2 Health (party[Tom].health += 20%) * 3 Mana (party[Sira].mana -= 5) * 5 Status (party[Rainer].status[Poisoned] = max) * 7 Language (party[Tom].language[Iskai] = max) * 8 Experience (party[Drirr].experience += 2000) * B UnkB (TODO) * C UnkC (TODO) * 13 Item (party[Tom].items[LughsShield] = 1) * 14 Gold (party[Joe].gold += 12) * 15 Food (party[Siobhan].food -= 10%) * 9 ChangeIcon (scope: rel vs abs, temp vs perm) * 0 Underlay (map.tempUnderlay[23,12] = 47) * 1 Overlay (map.permOverlay[+0,-3] = 1231) * 2 Wall (map.tempWall[10, 10] = 7) * 3 Floor (map.permFloor[64, 64] = 1) * 4 Ceiling (map.permCeiling[12, 24] = 7) * 5 NpcMovement (npc[12].permMovement = 3) * 6 NpcSprite (npc[5].tempSprite = 14) * 7 Chain (map.tempChain[10, 10] = 15) * 8 BlockHard (block_hard ...) * 9 BlockSoft (block_soft ...) * A Trigger (map.tempTrigger[96, 7] = Normal) * A Encounter (encounter ...) * B PlaceAction * 0 LearnCloseCombat * 1 Heal * 2 Cure * 3 RemoveCurse * 4 AskOpinion * 5 RestoreItemEnergy * 6 SleepInRoom * 7 Merchant * 8 OrderFood * 9 ScrollMerchant * B LearnSpells * C RepairItem * C Query (further subdivided by operation: NZ, <=, !=, ==, >=, >, <) * 0 Switch (switch[100]), (!switch[203]), (switch[KhunagMentionedSecretPassage]) * 1 Unk1 * 4 Unk4 * 5 HasPartyMember (party[Tom].isPresent) * 6 HasItem (!party.hasItem[Axe]) * 7 UsedItem (context.usedItem == Pick) * 9 PreviousActionResult (context.result) * A ScriptDebugMode (context.isDebug) * C UnkC * E NpcActive (npc[16].isActive) * F Gold (party.gold > 100) * 11 RandomChance (random(50)) * 12 Unk12 * 14 ChosenVerb (context.verb == Examine) * 15 Conscious (party[Tom].isConscious) * 1A Leader (party[Rainer].isLeader) * 1C Ticker (ticker[50] > 12) * 1D Map (context.map == Drinno3) * 1E Unk1E * 1F PromptPlayer (askYesNo(100)) * 19 Unk19 * 20 TriggerType (context.trigger == UseItem) * 21 Unk21 * 22 EventUsed (context.event[108].hasRun) * 23 DemoVersion (context.isDemo) * 29 Unk29 * 2A Unk2A * 2B PromptPlayerNumeric (askNumeric() = 1042) * D Modify * 0 Switch (switch[125] = 1) * 1 DisableEventChain (map[CantosHouse].chain[120] = 0) * 2 Unk2 * 4 NpcActive (set_npc_active ...) * 5 AddPartyMember (add_party_member ...) * 6 InventoryItem (party.item[Knife] += 3) * B Lighting (map.lighting = 5) ?? * F PartyGold (party.gold = min) * 10 PartyRations (party.rations += 12) * 12 Time (context.time += 6) * 1A Leader (party.leader = Tom) * 1C Ticker (ticker[93] = 108) * E Action (action ...) * 0 Word * 1 AskAboutItem * 2 Unk2 // Pay money? See ES156 (Garris, Gratogel sailor) * 4 Unk4 * 5 Unk5 * 6 StartDialogue * 7 FinishDialogue * 8 DialogueLine * 9 Unk9 * E Unk14 * 17 Unk23 * 2D Unk45 * 2E UseItem * 2F EquipItem * 30 UnequipItem * 36 PickupItem * 39 Unk57 * 3D Unk61 * F Signal (signal ...) * 10 CloneAutomap (clone_automap ...) * 11 Sound (sound ...) * 12 StartDialogue (start_dialogue ...) * 13 CreateTransport (???) * 14 Execute (execute) * 15 RemovePartyMember (remove_party_member ...) * 16 EndDialogue (end_dialogue) * 17 Wipe (wipe ...) * 18 PlayAnimation (play_anim ...) * 19 Offset (offset 0 0) * 1A Pause (pause 3) * 1B SimpleChest (simple_chest ...) * 1C AskSurrender (ask_surrender) * 1D Script (script 15) * FF UnkFF */ }