Example #1
0
 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);
            }
        }
Example #4
0
        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));
        }
    }
Example #5
0
    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);
    }
Example #6
0
        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);
        }
Example #7
0
    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;
    }
Example #8
0
    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
 */
}