

function bookcase_placer(x, y, placer, version, rotation)
    x = math.floor((x - 0.5) * 4 + 0.5) / 4
    y = math.floor((y - 0.5) * 4 + 0.5) / 4
    local c = math.cos(rotation * (math.pi / 2))
    local s = math.sin(rotation * (math.pi / 2))
    placer:static_model("base:teach_objs/bookcase",
        x + 0.5 + s * 0.3,
        0.0,
        y + 0.5 + c * 0.3,
        rotation * (math.pi / 2)
    )
    placer:placement_bound(
        x, y,
        1.0, 1.0
    )

    placer:collision_bound(
        x + math.max(0, s) * 0.75,
        y + math.max(0, c) * 0.75,
        0.25 * math.abs(s) + math.abs(c),
        0.25 * math.abs(c) + math.abs(s)
    )
    placer:selection_bound(
        x + math.max(0, s) * 0.75,
        0.0,
        y + math.max(0, c) * 0.75,
        0.25 * math.abs(s) + math.abs(c),
        0.8,
        0.25 * math.abs(c) + math.abs(s)
    )
    if not placer:contains_bound(x, y, 1.0, 1.0) then
        placer:fail("Bookcase not inside the room")
    end
    return 0
end

function skeleton_placer(x, y, placer, version, rotation)
    x = math.floor((x - 0.5) * 4 + 0.5) / 4
    y = math.floor((y - 0.5) * 4 + 0.5) / 4
    placer:static_model("base:teach_objs/skeleton_stand",
        x + 0.5,
        0.0,
        y + 0.5,
        rotation * (math.pi / 2)
    )
    placer:placement_bound(
        x + 0.25, y + 0.25,
        0.5, 0.5
    )

    placer:collision_bound(
        x + 0.25, y + 0.25,
        0.5, 0.5
    )
    placer:selection_bound(
        x, 0.0, y,
        1.0, 1.0, 1.0
    )
    if not placer:contains_bound(x + 0.25, y + 0.25, 0.5, 0.5) then
        placer:fail("Stand not inside the room")
    end
    return 0
end

function corner_computer_placer(x, y, placer, version, rotation)
    x = math.floor((x - 0.5) * 4 + 0.5) / 4
    y = math.floor((y - 0.5) * 4 + 0.5) / 4
    placer:static_model("base:teach_objs/computer_corner",
        x + 0.5,
        0.0,
        y + 0.5,
        rotation * (math.pi / 2)
    )
    placer:placement_bound(
        x + 0.25, y + 0.25,
        0.5, 0.5
    )

    placer:collision_bound(
        x + 0.25, y + 0.25,
        0.5, 0.5
    )
    placer:selection_bound(
        x, 0.0, y,
        1.0, 1.0, 1.0
    )
    if not placer:contains_bound(x + 0.25, y + 0.25, 0.5, 0.5) then
        placer:fail("Stand not inside the room")
    end
    return 0
end


function computer_desk_placer(x, y, placer, version, rotation)
    x = math.floor(x * 4 + 0.5) / 4
    y = math.floor(y * 4 + 0.5) / 4
    placer:static_model("base:teach_objs/computer_desk", x, 0.0, y, rotation * (math.pi / 2))
    local c = math.cos(rotation * (math.pi / 2))
    local s = math.sin(rotation * (math.pi / 2))
    placer:placement_bound(
        x - 0.5 - math.max(0, s) * 0.25,
        y - 0.5 - math.max(0, c) * 0.25,
        1.0 + math.abs(s) * 0.25,
        1.0 + math.abs(c) * 0.25
    )
    placer:selection_bound(x - 0.5, 0.0, y - 0.5, 1.0, 0.5, 1.0)

    placer:collision_bound(
        x - 0.5 + 0.5 * math.max(0, -s),
        y - 0.5 + 0.5 * math.max(0, -c),
        0.5 + math.abs(c) * 0.5,
        0.5 + math.abs(s) * 0.5
    )
    if not placer:contains_bound(x - 0.5, y - 0.5, 1.0, 1.0) then
        placer:fail("Desk not inside the room")
    end
    return 0
end

function science_desk_placer(x, y, placer, version, rotation)
    x = math.floor(x * 4 + 0.5) / 4
    y = math.floor(y * 4 + 0.5) / 4
    placer:static_model("base:teach_objs/science_desk", x, 0.0, y, rotation * (math.pi / 2))
    local c = math.cos(rotation * (math.pi / 2))
    local s = math.sin(rotation * (math.pi / 2))
    placer:placement_bound(
        x - 0.5 - math.max(0, s) * 0.25,
        y - 0.5 - math.max(0, c) * 0.25,
        1.0 + math.abs(s) * 0.25,
        1.0 + math.abs(c) * 0.25
    )
    placer:selection_bound(x - 0.5, 0.0, y - 0.5, 1.0, 0.5, 1.0)

    placer:collision_bound(
        x - 0.5 + 0.5 * math.max(0, -s),
        y - 0.5 + 0.5 * math.max(0, -c),
        0.5 + math.abs(c) * 0.5,
        0.5 + math.abs(s) * 0.5
    )
    if not placer:contains_bound(x - 0.5, y - 0.5, 1.0, 1.0) then
        placer:fail("Desk not inside the room")
    end
    return 0
end

function easel_placer(x, y, placer, version, rotation)
    x = math.floor(x * 4) / 4
    y = math.floor(y * 4) / 4
    placer:static_model("base:art/easel", x + 0.5, 0.0, y + 0.5, rotation * (math.pi / 2))
    local c = math.cos(-rotation * (math.pi / 2))
    local s = math.sin(-rotation * (math.pi / 2))
    placer:placement_bound(
        x - math.max(-s, 0) * 0.25,
        y - math.max(c, 0) * 0.25,
        1.0 + math.abs(s) * 0.25,
        1.0 + math.abs(c) * 0.25
    )

    placer:selection_bound(x, 0.0, y, 1.0, 1.0, 1.0)
    if not placer:contains_bound(x, y , 1.0, 1.0) then
        placer:fail("Easel not inside the room")
    end
    return 0
end


function globe_placer(x, y, placer, version, rotation)
    x = math.floor((x - 0.5) * 4 + 0.5) / 4
    y = math.floor((y - 0.5) * 4 + 0.5) / 4
    placer:animated_model("base:teach_objs/globe", "idle",
        x + 0.5,
        0.0,
        y + 0.5,
        rotation * (math.pi / 2)
    )
    placer:placement_bound(
        x + 0.25, y + 0.25,
        0.5, 0.5
    )

    placer:collision_bound(
        x + 0.25, y + 0.25,
        0.5, 0.5
    )
    placer:selection_bound(
        x + 0.25, 0.0, y + 0.25,
        0.5, 0.5, 0.5
    )
    if not placer:contains_bound(x + 0.25, y + 0.25, 0.5, 0.5) then
        placer:fail("Globe not inside the room")
    end
    return 0
end

function cage_sloth_placer(x, y, placer, version, rotation)
    x = math.floor((x - 0.5) * 4 + 0.5) / 4
    y = math.floor((y - 0.5) * 4 + 0.5) / 4

    local c = math.cos(-rotation * (math.pi / 2))
    local s = math.sin(-rotation * (math.pi / 2))

    placer:static_model("base:animals/cage_sloth_base",
        x + 0.5,
        0.01,
        y + 0.5,
        rotation * (math.pi / 2)
    )
    placer:animated_model("base:animals/cage_sloth", "idle",
        x + 0.5,
        0.01,
        y + 0.5,
        rotation * (math.pi / 2)
    )
    placer:placement_bound(
        x - 0.25 * math.abs(c), y - 0.25 * math.abs(s),
        1.0 + math.abs(c) * 0.5, 1.0 + math.abs(s) * 0.5
    )

    placer:collision_bound(
        x + 0.25 * math.abs(s), y + 0.25 * math.abs(c),
        0.5 + math.abs(c) * 0.5, 0.5 + math.abs(s) * 0.5
    )
    placer:selection_bound(
        x + 0.25 * math.abs(s), 0.0, y + 0.25 * math.abs(c),
        0.5 + math.abs(c) * 0.5, 1.0, 0.5 + math.abs(s) * 0.5
    )
    if not placer:contains_bound(
        x - 0.25 * math.abs(c), y - 0.25 * math.abs(s),
        1.0 + math.abs(c) * 0.5, 1.0 + math.abs(s) * 0.5
    ) then
        placer:fail("Sloth not inside the room")
    end
    return 0
end