<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
  <External>null</External>
  <External>nil</External>
  <Item class="Workspace">
    <Properties>
      <string name="Name">Workspace - Bird Life: Ultimate Flight (Tayson Edition)</string>
    </Properties>
    <Item class="SpawnLocation">
      <Properties>
        <string name="Name">CreatorSpawn</string>
        <bool name="Anchored">true</bool>
        <Vector3 name="Position"><X>0</X><Y>5</Y><Z>0</Z></Vector3>
        <Vector3 name="Size"><X>12</X><Y>1</Y><Z>12</Z></Vector3>
      </Properties>
    </Item>
      <Item class="Part">
        <Properties>
          <string name="Name">MeadowFloor</string>
          <bool name="Anchored">true</bool>
          <Vector3 name="Position"><X>0</X><Y>0</Y><Z>0</Z></Vector3>
          <Vector3 name="Size"><X>600</X><Y>2</Y><Z>600</Z></Vector3>
          <Color3 name="Color"><R>0.2</R><G>0.55</G><B>0.28</B></Color3>
        </Properties>
      </Item>
      <Item class="Part">
        <Properties>
          <string name="Name">PerchBranch_OakTree1</string>
          <bool name="Anchored">true</bool>
          <Vector3 name="Position"><X>40</X><Y>28</Y><Z>-30</Z></Vector3>
          <Vector3 name="Size"><X>18</X><Y>2</Y><Z>4</Z></Vector3>
          <Color3 name="Color"><R>0.45</R><G>0.28</G><B>0.15</B></Color3>
        </Properties>
      </Item>
      <Item class="Part">
        <Properties>
          <string name="Name">PerchBranch_Redwood2</string>
          <bool name="Anchored">true</bool>
          <Vector3 name="Position"><X>-55</X><Y>42</Y><Z>25</Z></Vector3>
          <Vector3 name="Size"><X>22</X><Y>2.5</Y><Z>4.5</Z></Vector3>
          <Color3 name="Color"><R>0.42</R><G>0.25</G><B>0.14</B></Color3>
        </Properties>
      </Item>
      <Item class="Part">
        <Properties>
          <string name="Name">PerchRock_EagleCliff</string>
          <bool name="Anchored">true</bool>
          <Vector3 name="Position"><X>0</X><Y>85</Y><Z>-140</Z></Vector3>
          <Vector3 name="Size"><X>45</X><Y>90</Y><Z>35</Z></Vector3>
          <Color3 name="Color"><R>0.45</R><G>0.47</G><B>0.52</B></Color3>
        </Properties>
      </Item>
      <Item class="Part">
        <Properties>
          <string name="Name">SeedFeederZone_Parakeet</string>
          <bool name="Anchored">true</bool>
          <Vector3 name="Position"><X>18</X><Y>1.5</Y><Z>18</Z></Vector3>
          <Vector3 name="Size"><X>14</X><Y>0.5</Y><Z>14</Z></Vector3>
          <Color3 name="Color"><R>0.85</R><G>0.72</G><B>0.24</B></Color3>
        </Properties>
      </Item>
      <Item class="Part">
        <Properties>
          <string name="Name">GroundOwlBurrowZone</string>
          <bool name="Anchored">true</bool>
          <Vector3 name="Position"><X>-35</X><Y>1.2</Y><Z>-25</Z></Vector3>
          <Vector3 name="Size"><X>16</X><Y>1.0</Y><Z>16</Z></Vector3>
          <Color3 name="Color"><R>0.58</R><G>0.42</G><B>0.22</B></Color3>
        </Properties>
      </Item>
  </Item>
  <Item class="ReplicatedStorage">
    <Properties><string name="Name">ReplicatedStorage</string></Properties>
      <Item class="ModuleScript">
        <Properties>
          <string name="Name">BirdData</string>
          <ProtectedString name="Source"><![CDATA[-- BirdData.luau (ModuleScript in ReplicatedStorage)
-- Complete 7-Bird Roster + Creator Unlock Rule for Tayson
local BirdData = {}

BirdData.CreatorUserIds = {
    -- Add Tayson's Roblox UserId here; Creator Mode is also toggleable in Studio/UI
    [0] = true,
}

BirdData.GrowthStages = {
    { Name = "Egg", Scale = 0.30, CanFly = false, SpeedMult = 0.0, Duration = 60 },
    { Name = "Hatchling", Scale = 0.45, CanFly = false, SpeedMult = 0.35, Duration = 90 },
    { Name = "Juvenile", Scale = 0.65, CanFly = true, SpeedMult = 0.70, Duration = 120 },
    { Name = "Fledgling", Scale = 0.85, CanFly = true, SpeedMult = 0.90, Duration = 150 },
    { Name = "Adult", Scale = 1.00, CanFly = true, SpeedMult = 1.00, Duration = 0 },
}

BirdData.Species = {
    Pigeon = {
        Id = "Pigeon",
        DisplayName = "City & Cliff Pigeon",
        StarterFree = true,
        UnlockCost = 0,
        FlySpeed = 48,
        GlideSpeed = 42,
        WalkSpeed = 16,
        MaxStamina = 100,
        StaminaDrain = 5,
        TurnRate = 2.8,
        Diet = {"Seeds", "Crumbs", "Berries"},
        Special = "Starter Bird (Free for all players) - agile city & cliff flyer",
        Color = {148, 163, 184}
    },
    Parakeet = {
        Id = "Parakeet",
        DisplayName = "Budgie Parakeet",
        StarterFree = false,
        UnlockCost = 150,
        FlySpeed = 52,
        GlideSpeed = 45,
        WalkSpeed = 15,
        MaxStamina = 95,
        StaminaDrain = 5,
        TurnRate = 3.4,
        Diet = {"Seeds", "Millet", "Fruit"},
        JuvenileDietRule = "Juveniles eat small seeds on ground/feeders for 2x growth boost",
        Special = "Flock Chirp & Juvenile Seed Foraging Boost",
        Color = {52, 211, 153}
    },
    Crow = {
        Id = "Crow",
        DisplayName = "Raven / Crow",
        StarterFree = false,
        UnlockCost = 300,
        FlySpeed = 56,
        GlideSpeed = 50,
        WalkSpeed = 17,
        MaxStamina = 115,
        StaminaDrain = 4.5,
        TurnRate = 3.0,
        Diet = {"Seeds", "Insects", "Shiny Trinkets", "Berries"},
        Special = "Collects shiny objects & twigs 2x faster for nests",
        Color = {99, 102, 241}
    },
    Parrot = {
        Id = "Parrot",
        DisplayName = "Scarlet Macaw Parrot",
        StarterFree = false,
        UnlockCost = 500,
        FlySpeed = 60,
        GlideSpeed = 54,
        WalkSpeed = 15,
        MaxStamina = 125,
        StaminaDrain = 4.2,
        TurnRate = 3.2,
        Diet = {"Tropical Fruit", "Seeds", "Nuts"},
        Special = "Canopy Thermal Glide & Mimic Call",
        Color = {244, 63, 94}
    },
    GroundOwl = {
        Id = "GroundOwl",
        DisplayName = "Burrowing Ground Owl",
        StarterFree = false,
        UnlockCost = 650,
        FlySpeed = 54,
        GlideSpeed = 48,
        WalkSpeed = 26,
        MaxStamina = 130,
        StaminaDrain = 4.0,
        TurnRate = 3.1,
        Diet = {"Insects", "Beetles", "Small Rodents"},
        Special = "Fast Ground Sprint + Ground Burrow Nesting",
        Color = {217, 119, 6}
    },
    Owl = {
        Id = "Owl",
        DisplayName = "Great Horned Night Owl",
        StarterFree = false,
        UnlockCost = 850,
        FlySpeed = 62,
        GlideSpeed = 58,
        WalkSpeed = 14,
        MaxStamina = 140,
        StaminaDrain = 3.5,
        TurnRate = 2.9,
        Diet = {"Mice", "Insects", "Fish"},
        Special = "Silent Flight + Night Vision Thermal Sight",
        Color = {168, 85, 247}
    },
    Eagle = {
        Id = "Eagle",
        DisplayName = "Golden Apex Eagle",
        StarterFree = false,
        UnlockCost = 1200,
        FlySpeed = 78,
        GlideSpeed = 72,
        WalkSpeed = 14,
        MaxStamina = 175,
        StaminaDrain = 3.0,
        TurnRate = 2.4,
        Diet = {"Fish", "Small Game", "Meat"},
        Special = "High-Altitude Thermal Soar & Dive Swoop",
        Color = {245, 158, 11}
    },
}

function BirdData.IsUnlocked(player, speciesId, isCreatorMode)
    if isCreatorMode or BirdData.CreatorUserIds[player.UserId] then
        return true -- Creator All-Birds-Unlocked Rule
    end
    local sp = BirdData.Species[speciesId]
    return sp and sp.StarterFree or false
end

return BirdData
]]></ProtectedString>
        </Properties>
      </Item>
      <Item class="LocalScript">
        <Properties>
          <string name="Name">FlightAndPerchClient</string>
          <ProtectedString name="Source"><![CDATA[-- FlightAndPerchClient.client.luau
-- Full PC + Mobile Touch + Xbox/PlayStation Controller Flight, Gliding, Stamina & Perching
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local player = Players.LocalPlayer
local camera = workspace.CurrentCamera

local State = {
    IsFlying = false,
    IsGliding = false,
    IsPerched = false,
    Stamina = 100,
    MaxStamina = 100,
    Speed = 50,
    GlideSpeed = 44,
    Altitude = 0,
    AscendInput = 0, -- -1 descend, 0 level, +1 ascend
}

-- Perch Detection via Raycast to "Perchable" tagged branches & cliff ledges
local function tryPerch(character)
    local root = character and character:FindFirstChild("HumanoidRootPart")
    if not root then return false end
    local params = RaycastParams.new()
    params.FilterDescendantsInstances = {character}
    params.FilterType = Enum.RaycastFilterType.Exclude
    local result = workspace:Raycast(root.Position, Vector3.new(0, -6, 0), params)
    if result and result.Instance and (result.Instance:GetAttribute("Perchable") or result.Instance.Name:find("Perch") or result.Instance.Name:find("Branch") or result.Instance.Name:find("Rock")) then
        State.IsPerched = true
        State.IsFlying = false
        State.IsGliding = false
        root.Anchored = true
        root.CFrame = CFrame.new(result.Position + Vector3.new(0, 2, 0), result.Position + Vector3.new(0, 2, 0) + camera.CFrame.LookVector)
        return true
    end
    return false
end

-- Cross-Platform Input (PC Keyboard, Xbox/PS Gamepad, Mobile Touch Buttons)
UserInputService.InputBegan:Connect(function(input, processed)
    if processed then return end
    local char = player.Character
    local root = char and char:FindFirstChild("HumanoidRootPart")
    if not root then return end

    -- Space / ButtonA: Take off or Flap Up
    if input.KeyCode == Enum.KeyCode.Space or input.KeyCode == Enum.KeyCode.ButtonA then
        if State.IsPerched then
            State.IsPerched = false
            root.Anchored = false
        end
        if not State.IsFlying and State.Stamina > 10 then
            State.IsFlying = true
            State.IsGliding = false
        end
        State.AscendInput = 1
    -- LeftShift / ButtonB: Toggle Glide
    elseif input.KeyCode == Enum.KeyCode.LeftShift or input.KeyCode == Enum.KeyCode.ButtonB then
        if State.IsFlying then
            State.IsGliding = not State.IsGliding
        end
    -- E / ButtonX: Perch or Land
    elseif input.KeyCode == Enum.KeyCode.E or input.KeyCode == Enum.KeyCode.ButtonX then
        if State.IsPerched then
            State.IsPerched = false
            root.Anchored = false
        else
            if not tryPerch(char) then
                State.IsFlying = false
                State.IsGliding = false
            end
        end
    -- Q / LeftTrigger: Dive / Descend
    elseif input.KeyCode == Enum.KeyCode.Q or input.KeyCode == Enum.KeyCode.ButtonL2 then
        State.AscendInput = -1
    end
end)

UserInputService.InputEnded:Connect(function(input)
    if input.KeyCode == Enum.KeyCode.Space or input.KeyCode == Enum.KeyCode.ButtonA or input.KeyCode == Enum.KeyCode.Q or input.KeyCode == Enum.KeyCode.ButtonL2 then
        State.AscendInput = 0
    end
end)

RunService.RenderStepped:Connect(function(dt)
    local char = player.Character
    local root = char and char:FindFirstChild("HumanoidRootPart")
    local hum = char and char:FindFirstChildOfClass("Humanoid")
    if not root or not hum then return end

    if State.IsPerched then
        State.Stamina = math.min(State.MaxStamina, State.Stamina + 18 * dt)
        return
    end

    if State.IsFlying then
        local drain = State.IsGliding and 1.2 or 5.0
        State.Stamina = math.max(0, State.Stamina - drain * dt)
        if State.Stamina <= 0 then
            State.IsGliding = true
            State.AscendInput = -0.6
        end
        local spd = State.IsGliding and State.GlideSpeed or State.Speed
        local look = camera.CFrame.LookVector
        local vertical = Vector3.new(0, State.AscendInput * 22 - (State.IsGliding and 3.5 or 0), 0)
        root.AssemblyLinearVelocity = look * spd + vertical
        root.CFrame = CFrame.lookAt(root.Position, root.Position + look)
    else
        State.Stamina = math.min(State.MaxStamina, State.Stamina + 12 * dt)
    end
end)
]]></ProtectedString>
        </Properties>
      </Item>
      <Item class="Script">
        <Properties>
          <string name="Name">NestAndLifecycleServer</string>
          <ProtectedString name="Source"><![CDATA[-- NestAndLifecycleServer.server.luau
-- Nest Building, 5-Egg Limit, 60-Second Incubation Timer, Family Invites & 5 Growth Stages
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local BirdData = require(ReplicatedStorage:WaitForChild("BirdData"))

local MAX_EGGS_PER_NEST = 5
local INCUBATION_SECONDS = 60

local PlayerNests = {} -- [userId] = { NestModel, Eggs = {}, FamilyMembers = {} }

local function createNest(player, position, isGroundBurrow)
    if PlayerNests[player.UserId] and PlayerNests[player.UserId].NestModel then
        PlayerNests[player.UserId].NestModel:Destroy()
    end

    local nest = Instance.new("Model")
    nest.Name = player.Name .. "_Nest"

    local base = Instance.new("Part")
    base.Name = "NestBase"
    base.Anchored = true
    base.Size = isGroundBurrow and Vector3.new(6, 1.2, 6) or Vector3.new(5, 1, 5)
    base.Position = position
    base.BrickColor = isGroundBurrow and BrickColor.new("Dirt brown") or BrickColor.new("Reddish brown")
    base.Material = isGroundBurrow and Enum.Material.Ground or Enum.Material.Wood
    base.Parent = nest
    nest.PrimaryPart = base
    nest.Parent = workspace

    PlayerNests[player.UserId] = {
        NestModel = nest,
        Eggs = {},
        FamilyMembers = { [player.UserId] = "Parent" }
    }
    return nest
end

local function layEgg(player)
    local data = PlayerNests[player.UserId]
    if not data or not data.NestModel then
        return false, "Build a nest first!"
    end
    if #data.Eggs >= MAX_EGGS_PER_NEST then
        return false, "Max 5 eggs per nest reached!"
    end

    local eggIndex = #data.Eggs + 1
    local egg = Instance.new("Part")
    egg.Name = "Egg_" .. eggIndex
    egg.Shape = Enum.PartType.Ball
    egg.Size = Vector3.new(1.1, 1.4, 1.1)
    egg.Anchored = true
    egg.Material = Enum.Material.SmoothPlastic
    egg.BrickColor = BrickColor.new("Institutional white")
    local angle = (eggIndex / MAX_EGGS_PER_NEST) * math.pi * 2
    egg.Position = data.NestModel.PrimaryPart.Position + Vector3.new(math.cos(angle) * 1.3, 0.9, math.sin(angle) * 1.3)
    egg.Parent = data.NestModel

    local eggRecord = {
        Part = egg,
        RemainingSeconds = INCUBATION_SECONDS,
        Hatched = false
    }
    table.insert(data.Eggs, eggRecord)

    -- 60-Second Incubation Countdown
    task.spawn(function()
        while eggRecord.RemainingSeconds > 0 and egg.Parent do
            task.wait(1)
            eggRecord.RemainingSeconds -= 1
            egg:SetAttribute("IncubationRemaining", eggRecord.RemainingSeconds)
        end
        if egg.Parent then
            eggRecord.Hatched = true
            egg.BrickColor = BrickColor.new("Bright yellow")
            egg:SetAttribute("Stage", "Hatchling")
        end
    end)

    return true, "Egg laid! Incubating for 60 seconds (" .. #data.Eggs .. "/" .. MAX_EGGS_PER_NEST .. ")"
end

return {
    CreateNest = createNest,
    LayEgg = layEgg,
    PlayerNests = PlayerNests
}
]]></ProtectedString>
        </Properties>
      </Item>
  </Item>
</roblox>
