local npcManager = require("npcManager")
local npcutils = require("npcs/npcutils")

local textFiles = require("scripts/textFiles")
local playerstun = require("playerstun")

local battleMessages,battleGeneral,battlePlayer,battleHUD,battleMap
local onlinePlay,onlinePlayNPC,onlinePlayPlayers

local battleStone = {}



battleStone.initialSpawnTime = lunatime.toTicks(8)
battleStone.respawnTime = lunatime.toTicks(4)

battleStone.scorePointTimeNeeded = 48

battleStone.statusMessageColor = Color(0.75,0.75,0.75)



battleStone.spawnedNPC = nil
battleStone.lastSpawner = nil
battleStone.spawnTimer = 0

battleStone.holdingPlayerIdx = 0
battleStone.latestHoldingPlayerIdx = 0

battleStone.scorePointTimer = 0


battleStone.grabbedEnemySound = Misc.resolveSoundFile("resources/stone_grabbed_enemy")
battleStone.grabbedSelfSound = Misc.resolveSoundFile("resources/stone_grabbed_self")

battleStone.grabSoundObj = nil


battleStone.spawnerBGOID = 952
battleStone.stoneID = 0


local solidColorShader


local stoneGrabbedCommand
local stoneSpawnCommand
local stonePointCommand
local stoneOverheatCommand

local dontDisplayDestroyedMessageTimer = 0


function battleStone.registerNPC(npcID)
	npcManager.registerEvent(npcID, battleStone, "onTickEndNPC")
    npcManager.registerEvent(npcID, battleStone, "onDrawNPC")
    
    battleStone.stoneID = npcID

	onlinePlayNPC.onlineHandlingConfig[npcID] = {
		updateFrequency = 1/30,
		getExtraData = function(v)
			local data = v.data

			if not data.initialized then
				return nil
			end

			return {
				floatTimer = data.floatTimer,
				inBubble = data.inBubble,
				animationTimer = data.animationTimer,
			}
		end,
		setExtraData = function(v,receivedData)
			local data = v.data

			if receivedData == nil or not data.initialized then
				return nil
			end

			data.floatTimer = receivedData.floatTimer
			data.inBubble = receivedData.inBubble
			data.animationTimer = receivedData.animationTimer
		end,
	}
end


local function getHoldingPlayerIndex(v)
	if v:mem(0x12C,FIELD_WORD) > 0 then
        return v:mem(0x12C,FIELD_WORD)
    end

	if v:mem(0x138,FIELD_WORD) == 6 then
		return v:mem(0x13C,FIELD_DFLOAT)
	end

    return 0
end

local function getHoldingPlayer(v)
	local holdingIdx = getHoldingPlayerIndex(v)

    if holdingIdx > 0 then
        return Player(holdingIdx)
    end
end


local function getFrame(v,data,config)
	local duration = config.frames*config.framespeed + config.highlightWaitTime
	local frame = math.max(0,(data.animationTimer % duration) - config.highlightWaitTime)/config.framespeed

	return npcutils.getFrameByFramestyle(v,{frame = frame})
end

local function spawnShockwaves(v,data,config,p)
	-- In online, don't spawn unless it's us
	if onlinePlay.currentMode ~= onlinePlay.MODE_OFFLINE then
		if p.idx ~= onlinePlay.playerIdx then
			return
		end
	end
	

	local shockwaveConfig = NPC.config[config.shockwaveID]

	for direction = -1,1,2 do
		local s = NPC.spawn(config.shockwaveID,p.x + p.width*0.5,p.y + p.height - shockwaveConfig.height*0.5,p.section,false,true)

		s.direction = direction
		s.spawnDirection = s.direction

		s:mem(0x12E,FIELD_WORD,100)
		s:mem(0x130,FIELD_WORD,p.idx)

		if onlinePlay.currentMode ~= onlinePlay.MODE_OFFLINE then
			onlinePlayNPC.tryClaimNPC(s)
		end
	end

	data.shockwaveCooldown = config.shockwaveCooldown
end


local function getGravity(v,data,config)
	if config.nogravity then
		return 0
	end

	if v:mem(0x1C,FIELD_WORD) > 0 and not config.nowaterphysics then
		return Defines.npc_grav*0.2
	else
		return Defines.npc_grav
	end
end

local function playerIsTouching(v)
	for _,p in ipairs(Player.getIntersecting(v.x - 2,v.y - 2,v.x + v.width + 2,v.y + v.height + 2)) do
		if p.forcedState == FORCEDSTATE_NONE and p.deathTimer == 0 then
			return true
		end
	end

	return false
end


local function popBubble(v,data,config)
	Effect.spawn(config.bubblePopEffectID,v.x + v.width*0.5,v.y + v.height*0.5)

	v:mem(0x136,FIELD_BOOL,true)
	data.inBubble = false

	SFX.play(91)
end


local function getHolder(v)
	if v.heldIndex > 0 then
		if battlePlayer.teamsAreEnabled() then
			return battlePlayer.getTeam(v.heldIndex)
		end

		return v.heldIndex
	end

	return 0
end


local function cancelOverheatWarningSound(v)
	local data = v.data

	if data.overheatWarningSound ~= nil then
		if data.overheatWarningSound:isPlaying() then
			data.overheatWarningSound:fadeout(100)
		end

		data.overheatWarningSound = nil
	end
end

local function overheatExplosion(v)
	for i = 1,6 do
		local speed = vector(5,0):rotate((i - 1)/6*360)

		local e = Effect.spawn(10,v.x + v.width*0.5,v.y + v.height*0.5)

		e.x = e.x - e.width*0.5
		e.y = e.y - e.height*0.5
		e.speedX = speed.x*0.5
		e.speedY = speed.y
	end

	cancelOverheatWarningSound(v)

	Explosion.spawn(v.x + v.width*0.5,v.y + v.height*0.5,3)
	onlinePlayNPC.forceKillNPC(v,HARM_TYPE_VANISH)

	if v == battleStone.spawnedNPC then
		battleMessages.spawnStatusMessage(textFiles.battleMessages.stone.overheated,battleStone.statusMessageColor)
		dontDisplayDestroyedMessageTimer = 2
	end
end


local function initialise(v,data,config)
	data.initialized = true

	data.playerAirTimer = 0
	data.shockwaveCooldown = 0

	data.oldWarpCooldown = 0

	data.wasOnGround = v.collidesBlockBottom

	data.overheatTimer = 0
	data.overheatPreviousHolder = 0

	data.animationTimer = 0

	data.floatTimer = 0
	data.inBubble = true
end


function battleStone.onTickEndNPC(v)
	if Defines.levelFreeze then return end
	
	local data = v.data
	
	if v.despawnTimer <= 0 then
		data.initialized = false
		return
	end

	local config = NPC.config[v.id]

	if not data.initialized then
		initialise(v,data,config)
	end


	if data.inBubble then
		if v:mem(0x12C,FIELD_WORD) > 0 or v:mem(0x138,FIELD_WORD) > 0 or playerIsTouching(v) then
			popBubble(v,data,config)
		else
			v.speedY = -getGravity(v,data,config) + math.cos(data.floatTimer/24)*0.4

			data.floatTimer = data.floatTimer + 1
		end
	end


	if v:mem(0x138,FIELD_WORD) == 6 then
		-- In yoshi's mouth, update position
        local p = Player(v:mem(0x13C,FIELD_DFLOAT))

        v.x = p.x + p:mem(0x6E,FIELD_WORD) - v.width*0.5 + 16
        v.y = p.y + p:mem(0x70,FIELD_WORD) - v.height*0.5 + 16
    end

	-- Limit the player holding it
	local p = getHoldingPlayer(v)

	if p ~= nil and p.isValid then
		local onGround = p:isOnGround()

		if p.forcedState == FORCEDSTATE_NONE and p.deathTimer == 0 then
			if p:mem(0x11C,FIELD_WORD) > config.playerMaxJumpForce[p.character] then
				p:mem(0x11C,FIELD_WORD,config.playerMaxJumpForce[p.character])
			end

			if p.keys.right then
				p.speedX = p.speedX - 0.02
			elseif p.keys.left then
				p.speedX = p.speedX + 0.02
			end

			p.speedX = math.clamp(p.speedX,-config.playerMaxSpeed,config.playerMaxSpeed)

			p:mem(0x00,FIELD_BOOL,false) -- disable tanooki toad's double jump
			p:mem(0x168,FIELD_FLOAT,0) -- p-speed
			p:mem(0x16C,FIELD_BOOL,false) -- has p-speed
			p:mem(0x16E,FIELD_BOOL,false) -- is flying

			if not onGround and p.speedY > 0 then
				p.speedY = p.speedY + config.playerGravity
			end

			if onGround and data.playerAirTimer >= 4 and data.shockwaveCooldown == 0 then
				spawnShockwaves(v,data,config,p)
				SFX.play(config.landSound)
			end

			if onGround then
				data.playerAirTimer = 0
			else
				data.playerAirTimer = data.playerAirTimer + 1
			end
		else
			data.playerAirTimer = 0
		end

		-- Overheating!
		local newHolder = getHolder(v)

		if data.overheatPreviousHolder > 0 and newHolder ~= data.overheatPreviousHolder then
			data.overheatTimer = math.max(0,data.overheatTimer - config.totalOverheatTime*config.overheatSwapReduction)
		end

		data.overheatPreviousHolder = newHolder

		data.overheatTimer = data.overheatTimer + 1

		if data.overheatWarningSound == nil and (data.overheatTimer >= config.totalOverheatTime - config.overheatWarningSoundTime) then
			data.overheatWarningSound = SFX.play(config.overheatWarningSound)
		end

		if data.overheatTimer >= config.totalOverheatTime and onlinePlayNPC.ownsNPC(v) then
			if onlinePlay.currentMode ~= onlinePlay.MODE_OFFLINE then
				stoneOverheatCommand:send(v,0)
			end

			overheatExplosion(v)
		end

		-- Prevent warp cheese by increasing the cooldown
		if p:mem(0x15C,FIELD_WORD) > data.oldWarpCooldown then
			p:mem(0x15C,FIELD_WORD,p:mem(0x15C,FIELD_WORD)*config.warpCooldownMultiplier)
		end

		data.oldWarpCooldown = p:mem(0x15C,FIELD_WORD)

		data.shockwaveCooldown = math.max(0,data.shockwaveCooldown - 1)
	else
		data.overheatTimer = math.max(0,data.overheatTimer - 2)

		cancelOverheatWarningSound(v)

		data.shockwaveCooldown = 0
		data.oldWarpCooldown = 0
		data.playerAirTimer = 0
	end

	--Text.print(data.overheatPreviousHolder,v.x - camera.x,v.y - camera.y)

	-- Normal behaviour
	if v:mem(0x12C,FIELD_WORD) == 0 and v:mem(0x138,FIELD_WORD) == 0 and not data.inBubble then
		-- Heavy!
		v.speedY = v.speedY + Defines.npc_grav*0.2

		-- Do a little effect when hitting the ground
		if v.collidesBlockBottom and not data.wasOnGround then
			SFX.play(config.landSound)
		end

		data.wasOnGround = v.collidesBlockBottom

		-- Deceleration
		local deceleration = 0.03
		if v.collidesBlockBottom then
			deceleration = 0.35
		end

		if v.speedX > 0 then
			v.speedX = math.max(0,v.speedX - deceleration)
		elseif v.speedX < 0 then
			v.speedX = math.min(0,v.speedX + deceleration)
		end

		-- Die in pits
		if v.y > (v.sectionObj.boundary.bottom + 64) then
			v:kill(HARM_TYPE_VANISH)
		end

		-- Hurt players when thrown
		if v:mem(0x136,FIELD_BOOL) and v:mem(0x130,FIELD_WORD) > 0 then
			for _,p in ipairs(Player.getIntersecting(v.x,v.y,v.x + v.width,v.y + v.height)) do
				if onlinePlayPlayers.ownsPlayer(p)
				and v:mem(0x130,FIELD_WORD) ~= p.idx
				and v:mem(0x132,FIELD_WORD) ~= p.idx
				and not (v:mem(0x130,FIELD_WORD) > 0 and battlePlayer.playersAreOnSameTeam(p.idx,v:mem(0x130,FIELD_WORD)))
				then
					--p:harm()

					battlePlayer.harmPlayer(p,battlePlayer.HARM_TYPE.NORMAL,{
						cause = battlePlayer.HARM_CAUSE.NPC,
						npcID = v.id,
						playerIdx = v:mem(0x132,FIELD_WORD),
					})
				end
			end
		end
	else
		data.wasOnGround = true
	end

	-- Don't get crushed
	v:mem(0x134,FIELD_WORD,0)

	-- Don't despawn
	v.despawnTimer = math.max(100,v.despawnTimer)


	v.animationFrame = getFrame(v,data,config)
	data.animationTimer = data.animationTimer + 1
end


function battleStone.onDrawNPC(v)
	if v.despawnTimer <= 0 or v:mem(0x138,FIELD_WORD) == 6 then
		return
	end

	local texture = Graphics.sprites.npc[v.id].img

	if texture == nil then
		return
	end


	local config = NPC.config[v.id]
	local data = v.data

	if not data.initialized then
		initialise(v,data,config)
	end


	local priority = -45
	if v:mem(0x12C,FIELD_WORD) > 0 then
		local p = Player(v:mem(0x12C,FIELD_WORD))

		if p.forcedState == FORCEDSTATE_PIPE then
			priority = -69.9 -- just in front of the player
		elseif p.character == CHARACTER_TOAD or p.character == CHARACTER_PEACH then
			priority = -24.9 -- just in front of the player
		else
			priority = -30
		end
	elseif config.foreground then
		priority = -15
	end

	local overheatExtremity = math.invlerp(config.totalOverheatTime - config.overheatEffectTime,config.totalOverheatTime,data.overheatTimer)
	local shader,uniforms

	if overheatExtremity > 0 then
		local flashValue = math.abs(math.sin(lunatime.tick()*0.075*math.pi))

		if solidColorShader == nil then
			-- Compile the shader if we haven't already
			solidColorShader = Shader.fromFile(nil,"resources/solidColor.frag")
		end

		shader = solidColorShader
		uniforms = {
			color = config.overheatFlashColorA:lerp(config.overheatFlashColorB,flashValue),
			extremity = math.min(1,overheatExtremity),
		}
	end


	local width = config.gfxwidth
	local height = config.gfxheight

	local x = v.x + v.width*0.5 + config.gfxoffsetx
	local y = v.y + v.height - height*0.5 + config.gfxoffsety

	Graphics.drawBox{
		texture = texture,priority = priority,
		centred = true,sceneCoords = true,

		x = x,
		y = y,
		sourceWidth = width,
		sourceHeight = height,
		sourceY = v.animationFrame*height,

		shader = shader,uniforms = uniforms,
	}

	if data.inBubble then
		local bubbleImage = config.bubbleImage

		local bubbleStretch = math.sin(lunatime.tick()/12)*0.1
		local width = bubbleImage.width*(1 + bubbleStretch)
		local height = bubbleImage.height*(1 - bubbleStretch)

		Graphics.drawBox{
			texture = config.bubbleImage,priority = priority,
			centred = true,sceneCoords = true,

			x = x,y = y,width = width,height = height,
		}
	end

	npcutils.hideNPC(v)
end


local function getKnockedDirection(v,reason,culprit)
	if reason == HARM_TYPE_SWORD then
		for _,p in ipairs(Player.get()) do
			if Colliders.slash(p,v) then
				if (p.x + p.width*0.5) < (v.x + v.width*0.5) then
					return p.idx,DIR_RIGHT
				else
					return p.idx,DIR_LEFT
				end
			end
		end
	end

	if reason == HARM_TYPE_TAIL then
		if type(culprit) == "Player" then
			if (culprit.x + culprit.width*0.5) < (v.x + v.width*0.5) then
				return culprit.idx,DIR_RIGHT
			else
				return culprit.idx,DIR_LEFT
			end
		end
	end

	if reason == HARM_TYPE_FROMBELOW then
		return 0,0
	end
end

function battleStone.onNPCHarm(eventObj,v,reason,culprit)
	if v.id ~= battleStone.stoneID then
		return
	end

	-- Get bounced by stuff
	local ownerIdx,direction = getKnockedDirection(v,reason,culprit)

	if direction ~= nil then
		v.speedX = direction*4
		v.speedY = -6

		v:mem(0x132,FIELD_WORD,ownerIdx)
		v:mem(0x136,FIELD_BOOL,true)

		SFX.play(9)

		eventObj.cancelled = true
		return
	end
end

function battleStone.onPostNPCKill(v,reason)
    if battleStone.spawnedNPC == v and dontDisplayDestroyedMessageTimer == 0 then
        battleMessages.spawnStatusMessage(textFiles.battleMessages.stone.killed,battleStone.statusMessageColor)
    end
end


local function canSpawnStone()
    return (battleGeneral.mode == battleGeneral.gameMode.STONE and not battleMessages.victoryActive and onlinePlay.currentMode ~= onlinePlay.MODE_CLIENT)
end

local function findSpawnPosition()
    local spawners = {}

    for _,v in BGO.iterate(battleStone.spawnerBGOID) do
        if not v.isHidden and battleStone.lastSpawner ~= v then
            table.insert(spawners,v)
        end
    end

    if #spawners == 0 then
        for _,v in BGO.iterate(battleStone.spawnerBGOID) do
            if not v.isHidden then
                return v
            end
        end
    end

    return RNG.irandomEntry(spawners)
end

local function spawnStar()
    local spawner = findSpawnPosition()
    if spawner == nil then
        return
    end

    if battleStone.stoneID <= 0 then
        return
    end

    local v = NPC.spawn(battleStone.stoneID,spawner.x + spawner.width*0.5,spawner.y + spawner.height*0.5,nil,false,true)

    v.layerName = "Spawned NPCs"

    v.direction = DIR_LEFT
    v.spawnDirection = v.direction

    battleStone.lastSpawner = spawner
    battleStone.spawnedNPC = v

    -- Spawn an effect
    local e = Effect.spawn(10,v.x + v.width*0.5,v.y + v.height*0.5)

    e.x = e.x - e.width *0.5
    e.y = e.y - e.height*0.5

    -- Send a message 
    if onlinePlay.currentMode == onlinePlay.MODE_HOST then
        local data = onlinePlayNPC.getData(v)

        onlinePlayNPC.tryClaimNPC(v)

        stoneSpawnCommand:send(0, data.onlineUID)
    end

    battleMessages.spawnStatusMessage(textFiles.battleMessages.stone.spawn,battleStone.statusMessageColor)

    SFX.play(20)
end


local function playGrabSound(newHolderIdx)
    if battleStone.grabSoundObj ~= nil then
        battleStone.grabSoundObj:stop()
    end

    if newHolderIdx ~= 0 and (onlinePlay.currentMode == onlinePlay.MODE_OFFLINE or newHolderIdx == onlinePlay.playerIdx) then
        battleStone.grabSoundObj = SFX.play(battleStone.grabbedSelfSound)
    else
        battleStone.grabSoundObj = SFX.play(battleStone.grabbedEnemySound)
    end
end

local function changeHoldingPlayer(holderIdx)
    if holderIdx == battleStone.holdingPlayerIdx then
        return
    end

    if holderIdx > 0 then
        if onlinePlay.currentMode ~= onlinePlay.MODE_OFFLINE then
            local name = battlePlayer.getName(holderIdx)
            
            battleMessages.spawnStatusMessage(textFiles.funcs.replace(textFiles.battleMessages.stone.grabbed,{NAME = name}),holderIdx)
        end

        battleStone.latestHoldingPlayerIdx = holderIdx

        playGrabSound(holderIdx)
    end

    battleStone.holdingPlayerIdx = holderIdx
end


local function scorePoint(p)
	if battlePlayer.teamsAreEnabled() then
		local teamIdx = battlePlayer.getTeam(p.idx)

		battlePlayer.teamPoints[teamIdx] = battlePlayer.teamPoints[teamIdx] + 1

		if onlinePlay.currentMode ~= onlinePlay.MODE_OFFLINE then
			stonePointCommand:send(0, battlePlayer.teamPoints[teamIdx])
		end
	else
		local data = battlePlayer.getPlayerData(p)

		data.points = data.points + 1
		
		if onlinePlay.currentMode ~= onlinePlay.MODE_OFFLINE then
			stonePointCommand:send(0, data.points)
		end
	end
end


function battleStone.onStart()
    battleStone.spawnTimer = battleStone.initialSpawnTime
end

function battleStone.onTick()
    -- Spawning
    if canSpawnStone() then
        if battleStone.spawnedNPC ~= nil then
            if not battleStone.spawnedNPC.isValid then
                battleStone.spawnTimer = battleStone.respawnTime
                battleStone.spawnedNPC = nil

                playGrabSound(0)
            end
        else
            battleStone.spawnTimer = math.max(0,battleStone.spawnTimer - 1)
    
            if battleStone.spawnTimer <= 0 then
                spawnStar()
            end
        end
    end

    -- Handle whoever's holding it
    if battleStone.spawnedNPC ~= nil and battleStone.spawnedNPC.isValid then
        if onlinePlayNPC.ownsNPC(battleStone.spawnedNPC) then
            local holderIdx = getHoldingPlayerIndex(battleStone.spawnedNPC)

            if holderIdx ~= battleStone.holdingPlayerIdx then
                if onlinePlay.currentMode ~= onlinePlay.MODE_OFFLINE then
                    stoneGrabbedCommand:send(0, holderIdx)
                end

                changeHoldingPlayer(holderIdx)
            end
        end
    elseif battleStone.holdingPlayerIdx ~= 0 then
        changeHoldingPlayer(0)
    end

	-- Score points
	if battleStone.holdingPlayerIdx > 0 and (onlinePlay.currentMode == onlinePlay.MODE_OFFLINE or battleStone.holdingPlayerIdx == onlinePlay.playerIdx) then
		local p = Player(battleStone.holdingPlayerIdx)

		if p.forcedState == FORCEDSTATE_NONE and not battleMessages.victoryActive then
			battleStone.scorePointTimer = battleStone.scorePointTimer + 1

			if battleStone.scorePointTimer >= battleStone.scorePointTimeNeeded then
				scorePoint(p)
				battleStone.scorePointTimer = 0
			end
		end
	else
		battleStone.scorePointTimer = 0
	end


	dontDisplayDestroyedMessageTimer = math.max(0,dontDisplayDestroyedMessageTimer - 1)
end



function battleStone.onInitAPI()
    registerEvent(battleStone,"onNPCHarm")
    registerEvent(battleStone,"onPostNPCKill")

    registerEvent(battleStone,"onStart")
    registerEvent(battleStone,"onTick")

    battleMessages = require("scripts/battleMessages")
    battleGeneral = require("scripts/battleGeneral")
    battlePlayer = require("scripts/battlePlayer")
	battleHUD = require("scripts/battleHUD")
    battleMap = require("scripts/battleMap")
    onlinePlay = require("scripts/onlinePlay")
	onlinePlayNPC = require("scripts/onlinePlay_npc")
	onlinePlayPlayers = require("scripts/onlinePlay_players")


    stoneSpawnCommand = onlinePlay.createCommand("battle_stone_spawn",onlinePlay.IMPORTANCE_MAJOR)
    stoneGrabbedCommand = onlinePlay.createCommand("battle_stone_grab",onlinePlay.IMPORTANCE_MAJOR)
	stonePointCommand = onlinePlay.createCommand("battle_stone_point",onlinePlay.IMPORTANCE_MAJOR)
	stoneOverheatCommand = onlinePlayNPC.createNPCCommand("battle_stone_overheat",onlinePlay.IMPORTANCE_MAJOR)

    function stoneSpawnCommand.onReceive(sourcePlayerIdx, onlineUID)
        if onlinePlay.currentMode == onlinePlay.MODE_HOST or sourcePlayerIdx ~= onlinePlay.hostPlayerIdx then
            return
        end

        local npc = onlinePlayNPC.getNPCFromUID(onlineUID)

        if npc ~= nil and npc.isValid then
            -- Spawn an effect
            local e = Effect.spawn(10,npc.x + npc.width*0.5,npc.y + npc.height*0.5)
    
            e.x = e.x - e.width *0.5
            e.y = e.y - e.height*0.5

            battleStone.spawnedNPC = npc
        end

        battleMessages.spawnStatusMessage(textFiles.battleMessages.stone.spawn,battleStone.statusMessageColor)

        SFX.play(20)
    end

    function stoneGrabbedCommand.onReceive(sourcePlayerIdx, holderIdx)
        changeHoldingPlayer(holderIdx)
    end

	function stoneOverheatCommand.onReceive(npc,sourcePlayerIdx)
		overheatExplosion(npc)
	end

	function stonePointCommand.onReceive(sourcePlayerIdx, newPoints)
		local p = Player(sourcePlayerIdx)

		if battlePlayer.teamsAreEnabled() then
			local teamIdx = battlePlayer.getTeam(sourcePlayerIdx)

			battlePlayer.teamPoints[teamIdx] = newPoints
		else
			local data = battlePlayer.getPlayerData(p)

			data.points = newPoints
		end
	end


	local function releaseStone(p)
		local holdingNPC = p.holdingNPC
    
        if holdingNPC ~= nil and holdingNPC.id == battleStone.stoneID then
			if p.forcedState == FORCEDSTATE_NONE then
				playerstun.stunPlayer(p.idx,NPC.config[holdingNPC.id].droppedPlayerStunTime)
			end

			if p:mem(0x140,FIELD_WORD) > 0 then
				p:mem(0x140,FIELD_WORD,math.floor(p:mem(0x140,FIELD_WORD)*0.5))
			end

			holdingNPC.speedX = p.direction*3
			holdingNPC.speedY = -3
			holdingNPC:mem(0x12C,FIELD_WORD,0)
			holdingNPC:mem(0x12E,FIELD_WORD,Defines.npc_throwfriendlytimer)
			holdingNPC:mem(0x130,FIELD_WORD,p.idx)
			holdingNPC:mem(0x132,FIELD_WORD,p.idx)
            p:mem(0x154,FIELD_WORD,0)
        end
	end

    function battlePlayer.onPostPlayerHarmCustom(p,harmType)
        releaseStone(p)
    end

	function battlePlayer.onPlayerStomped(stompedPlayer,stompingPlayer)
		if not battlePlayer.playersAreOnSameTeam(stompedPlayer.idx,stompingPlayer.idx) then
			releaseStone(stompedPlayer)
		end
	end


	function battleMap.onMapDraw(sectionIdx,priority,opacity,mapX,mapY,mapWidth,mapHeight)
        local stone = battleStone.spawnedNPC

        if stone ~= nil and stone.isValid and stone.section == sectionIdx then
            local relativeX,relativeY = battleMap.getRelativePosition(stone.x + stone.width*0.5,stone.y + stone.height*0.5,Section(sectionIdx).boundary)

            Graphics.drawBox{
                texture = battleHUD.stoneImage,priority = priority + 0.1,centred = true,
                color = Color.white.. battleMap.iconOpacity*opacity,

                x = mapX + mapWidth*relativeX,
                y = mapY + mapHeight*relativeY,
            }
        end
    end
end


return battleStone