--This script will run on all levels when mod is active.
--Modding documentation: http://teardowngame.com/modding
--API reference: http://teardowngame.com/modding/api.html

--SPECIAL THANKS:
--Huge thanks to Waffle for the DrawShapeLine code
--Huge thanks to Autumn for the Automatic Framework: https://github.com/Autumnagnificent/Automatic-Framework/blob/master/Automatic.lua

--KNOWN ISSUES:
--If any part of a shape that contains glass breaks, the glass breaks. I will not be fixing this as it is not too noticeable.
--Breaking large panes of glass will lag until script completes the shatter. ---FIXING
--Any newly spawned shapes that have glass will not be affected by the mod. ---FIXING
--Some pieces of glass will not clear when M is pressed. I will not be fixing this as again, it is not too noticeable.
--The glass will only break once by design; therefore if the script fails to shatter the glass, it will not try again. This is a rare issue and will be ignored.
--Some maps put multiple glass panes under one shape; therefore, more glass than intended will break. As far as I know, this is unfixable and will be ignored.

--TODO:
--Make throwable hammer to throw at glass(?) (if I make this it will be the last priority)
--Put glass breaking into a coroutine to stop lagging when larger bodies of glass are broken

--MOD VERSION: 1.0

#include Automatic.lua

break2 = LoadSound("MOD/snd/break_2.ogg")
break3 = LoadSound("MOD/snd/break_3.ogg")
break4 = LoadSound("MOD/snd/break_4.ogg")
breakS = LoadSound("MOD/snd/oh_no.ogg")
ouch = LoadSound("MOD/snd/glass.ogg")

function init()
    amountOfLists = 0
    listOfBroken = {}
    listOfAllGlass = {}
    amountOfGlass = 0
    isDebugMessageDisabled = GetBool("savegame.mod.betterglass.debugmessage.disabled")
    hasCleared = false

    local allShapes = FindShapes(nil,true)

    for i=1,#allShapes do --checks every shape in the level for if it contains glass and puts the shapes containing glass into a new list
        local hasGlass = DoesItHaveGlass(allShapes[i])
        if hasGlass then
            amountOfGlass = amountOfGlass+1
            listOfAllGlass[amountOfGlass] = allShapes[i]
        end
    end

    if not isDebugMessageDisabled then
        DebugPrint("You are running Better Glass v1.0. This update means that glass now shatters when it is broken!")
        DebugPrint("The keystroke option is no longer available for the time being. Have fun with version one :)")
        DebugPrint("Press [N] to clear this message and never see it again.")
    end
end

function tick(dt)
    for i=1,#listOfAllGlass do --shatters glass when it notices a shape containing glass has been broken
        local broken = IsShapeBroken(listOfAllGlass[i])
        local hasbrokenglasstag = HasTag(listOfAllGlass[i], "is_broken_glass")

        if broken and not hasbrokenglasstag then 
            local transform = GetShapeWorldTransform(listOfAllGlass[i])
            SetTag(listOfAllGlass[i], "is_broken_glass")
            GlassHandler(listOfAllGlass[i], transform.pos) 
        end
    end

    if InputDown("m") then
        RemoveAllGlass()
    end

    if InputDown("n") and not hasCleared and not isDebugMessageDisabled then --clears the debug message permanently
        SetBool("savegame.mod.betterglass.debugmessage.disabled", true) 
        for i=1,20 do
            DebugPrint(" ")
        end
        hasCleared = true
    end

    --something to check if any new shapes have been loaded? how to do so? maybe AutoTableContains?

    --resume yielded coroutines (coroutines will be stored in an array to be resumed in a for loop)
end

function GlassHandler(shape,pos) --handles the shattering of the glass
    --place below function into a coroutine that limits the # of lines per frame (or maybe count x,y, and z in diff frames, idk)
    --if a quickload happens, terminate all coroutines curently happening and reset coroutine list
    local size = { GetShapeSize(shape) }
    for x=0, size[1] - 1 do
        for y=0, size[2] - 1 do
            for z=0, size[3] - 1 do
                local material = { GetShapeMaterialAtIndex(shape, x, y, z) }
                if material[1] == "glass" and math.random() < .6 then
                    SetBrush("cube", 1, 0)
                    DrawShapeLine(shape, x, y, z, x, y, z, false, false)    
                end
            end
        end
    end

    PlayRandomSound(pos)

    newBodies = AutoSplitShapeIntoBodies(shape, false) --this list of current glass bodies resets each glass break
    amountOfLists = amountOfLists+1
    listOfBroken[amountOfLists] = newBodies --so that list needs to be stored in another list for when the player wants to delete the glass bodies

    for i=1,#newBodies do
        local bodyShape = GetBodyShapes(newBodies[i]) --sets the tag of each broken glass piece
        SetTag(bodyShape[1], "is_broken_glass")

        local posit = GetShapeWorldTransform(shape) --applies impulse on each broken piece
        local impul = Vec(0,5,0)
        ApplyBodyImpulse(newBodies[i], posit, impul)
    end
end

function RemoveAllGlass() --removes glass debris
    for i=1,amountOfLists do
        curList = listOfBroken[i]
        AutoDeleteHandles(listOfBroken[i])
    end
    amountOfLists = 0
end

function PlayRandomSound(position) --plays one of three glass shatter sounds (or 1 of 2 secret sounds) at the position of the broken glass
    local curSound = math.random(1,3)
    local randomChanceOuch = math.random(1,9999)
    local randomChanceOhno = math.random(1,9999)

    if curSound == 1 then
        PlaySound(break2, position)
    elseif curSound == 2 then
        PlaySound(break3, position)
    elseif curSound == 3 then
        PlaySound(break4, position)
    end

    if randomChanceOhno == 3 then
        PlaySound(breakS, position)
    end

    if randomChanceOuch == 4 then
        PlaySound(ouch, position)
    end
end

function DoesItHaveGlass(shape) --used to determine if the shape has glass
    local entries = GetShapePalette(shape)
    for i=1,#entries do
        local mat = GetShapeMaterial(shape,entries[i])
        if mat == "glass" then return true end
    end
    return false
end

function GetAimPos(range) --used for keystroke version, gets the position of where you're aiming when you press the shatter button
	local ct = GetCameraTransform()
	local forwardPos = TransformToParentPoint(ct, Vec(0, 0, -range))
    local direction = VecSub(forwardPos, ct.pos)
    local distance = VecLength(direction)
	local direction = VecNormalize(direction)
	local hit, hitDistance, normal, shape = QueryRaycast(ct.pos, direction, distance)
	if hit then
		forwardPos = TransformToParentPoint(ct, Vec(0, 0, -hitDistance))
	end
	return forwardPos, hit, shape
end

--below this line is code I need to fix for a future update

--if InputDown("lmb") and InputDown("ctrl") then --used if user hit the keystroke instead
--    local aimpos, hit, shape = GetAimPos(100)
--    if not hit then return end
--    GlassHandler(shape, aimpos)
--end

--local material1 = GetShapeMaterialAtPosition(shape, pos)
--local hasbrokenglasstag = HasTag(shape, "is_broken_glass")

--if material1 == "glass" and not hasbrokenglasstag then
--glasshandler code
--end