I am trying to make an arrow that always point to the player...well it is a simpler problem than my initial one.
Just a Rinka that spins before moving, then aims at the player with the sprite in the correct direction. It wasn't as easy as finding the distance between the NPC and the player, the direction to the player then the angle to rotate the whole sprite. What I did is look at Rinka code and add something to it in related to rotating the sprite. It did work, but not very accurately. The angle scales weirdly (rotating less when the angle is larger) and caps at 75 deg (or whatever I already tested with which quadrant each belongs in).
Here's the code:
Loading the sprite:
Code: Select all
local iniNPC = function(v)
local config = NPC.config[v.id]
if not v.data.ini then
v.data.ini = true
v.data.sprite = Sprite.box{
x = v.x,
y = v.y,
width = npcutils.gfxwidth(v),
height = npcutils.gfxheight(v),
texture = Graphics.sprites.npc[v.id].img,
rotation = 0,
align = Sprite.align.CENTRE,
frames = npcutils.getTotalFramesByFramestyle(v)
}
end
end
Register events:
Code: Select all
function arrow.onInitAPI()
npcManager.registerEvent(npcID, arrow, "onTickNPC") --this is only for text debugging
npcManager.registerEvent(npcID, arrow, "onTickEndNPC")
npcManager.registerEvent(npcID, arrow, "onDrawNPC") --this is for drawing the sprite
end
local temp = 0
local angle = 0
local quadrent = 0
local PosX = 0
local PosY = 0
function arrow.onTickEndNPC(v)
--Don't act during time freeze
if Defines.levelFreeze then return end
if v:mem(0x138, FIELD_WORD) > 0 then return end
local data = v.data
--If despawned
if v.despawnTimer <= 0 then
--Reset our properties, if necessary
data.animationTimer = 0
data.animationDirection = 1
data.frame = 0
data.initialized = false
return
end
--Initialize
if not data.initialized then
v.ai1 = 0
v.ai2 = 0
data.initialized = true
end
--Execute main AI.
if not v:mem(0x136, FIELD_BOOL) and v:mem(0x12C, FIELD_WORD) == 0 and data.initialized then
arrow.arrowSourceHoming(v, data.speed or NPC.config[v.id].speed)
end
end
Main function
Code: Select all
function arrow.arrowSourceHoming(v, speed)
local targetPlayer = npcutils.getNearestPlayer(v)
local toPlayerVect = vector(targetPlayer.x - v.x, targetPlayer.y - v.y)
local speedVect = toPlayerVect:normalise() * speed --Problem probably here. Speed (of NPC) changes the scaling of result angles.
PosX = speedVect.x --But using just using toPlayerVect made it worse
PosY = speedVect.y
--this part is right as I tested myself in each quadrant
temp = math.floor(math.atan(PosY, PosX)*(180/math.pi))
if PosY > 0 then
if PosX > 0 then
angle = math.floor(temp)
quadrent = 1
else angle = math.floor(180 - temp)
quadrent = 2
end
else
if PosX > 0 then
angle = math.floor(temp)
quadrent = 4
else angle = math.floor(180 - temp)
quadrent = 3
end
end
--Rotate
iniNPC(v)
v.data.sprite.rotation = 0 --reset after each rotation so it doesn't spin crazy
v.data.sprite:rotate(angle)
end
Drawing:
Code: Select all
function arrow.onDrawNPC(v)
if v:mem(0x12A, FIELD_WORD) <= 0 then return end
local config = NPC.config[v.id]
local p = -45
if config.foreground then p = -15 end
v.data.sprite.x = v.x + v.width*0.5 + config.gfxoffsetx
v.data.sprite.y = v.y + v.height*0.5 + config.gfxoffsety
v.data.sprite:draw{priority = p - 0.1, sceneCoords = true, frame = v.animationFrame + 1}
npcutils.hideNPC(v)
end
I even lowered my expectations and start in a really simple way by not using vectors, but basic distance and angle measuring. Somehow, the atan() function just doesn't return the correct angle in the Text debug (still caps on 1.56rad) when compared to the calculator.