Lua - World of Warcraft API Debuff frame - lua

I have the strange problem, that with this AddOn it shows a frame with the current debuff. I think it displays only the first one and if another one is applied it just overlaps the first one. How can i make it so it displays the new ones next to the old ones?
This is the code:
function WCCPlayer_OnLoad()
this:SetHeight(40)
this:SetWidth(40)
this:SetPoint("CENTER", 0, 0)
this:RegisterEvent("UNIT_AURA")
this:RegisterEvent("PLAYER_AURAS_CHANGED")
this.texture = this:CreateTexture(this, "BACKGROUND")
this.texture:SetAllPoints(this)
this.cooldown = CreateFrame("Model", "Cooldown", this, "CooldownFrameTemplate")
this.cooldown:SetAllPoints(this)
this.maxExpirationTime = 0
this:Hide()
end
function WCCPlayer_OnEvent()
local spellFound = false
for i=1, 16 do -- 16 is enough due to HARMFUL filter
local texture = UnitDebuff("player", i)
WCCTooltip:ClearLines()
WCCTooltip:SetUnitDebuff("player", i)
local buffName = WCCTooltipTextLeft1:GetText()
if spellIds[buffName] then
spellFound = true
for j=0, 31 do
local buffTexture = GetPlayerBuffTexture(j)
if texture == buffTexture then
local expirationTime = GetPlayerBuffTimeLeft(j)
this:Show()
this.texture:SetTexture(buffTexture)
this.cooldown:SetModelScale(1)
if this.maxExpirationTime <= expirationTime then
CooldownFrame_SetTimer(this.cooldown, GetTime(), expirationTime, 1)
this.maxExpirationTime = expirationTime
end
return
end
end
end
end
if spellFound == false then
this.maxExpirationTime = 0
this:Hide()
end
end
function WCCTarget_OnLoad()
end
function WCCTarget_OnEvent()
end

I think it is all about the frame position, and as you said, the new frame is displaying on top of the previous frame.
You need to have the new frame position next to the previous so each time you run your WCCPlayer_OnLoad() function it increase the X coordinate by the width of the frame.
First declare a local setPointX variable out side of the function, then increment the setPointX variable by the frame width, (in your case it is 40), each time the function is run;
local setPointX, setPointY = 0,0 -- x and y variables
function WCCPlayer_OnLoad()
this:SetHeight(40)
this:SetWidth(40)
this:SetPoint('CENTER', setPointX, setPointY) -- use variables to set the frame point
this:RegisterEvent('UNIT_AURA')
this:RegisterEvent('PLAYER_AURAS_CHANGED')
this.texture = this:CreateTexture(this, 'BACKGROUND')
this.texture:SetAllPoints(this)
this.cooldown = CreateFrame('Model', 'Cooldown', this, 'CooldownFrameTemplate')
this.cooldown:SetAllPoints(this)
this.maxExpirationTime = 0
this:Hide()
setPointX = setPointX + 40 -- increase the x variable by the width of the frame
end
I have no background in programming, (just trying to teach myself Java and Lua), so there undoubtedly will be better and more efficient/effective ways to solve your issue.

Related

Lua value changing by itself?

I'm new to using both Lua and LOVE2D. I thought that a perfect way to get familiar with the tools would be to create a snake style game. currently I have most of the things up and running. I run into a problem when adding to the snake after it eats the food. My snake class currently consists of a table of cells, each cell has a gridX and gridY variable. I also have 2 global variable called lastGridX and lastGridY that keep track of the last position of the last cell in the snake class. I update the position of the cells in reverse order and set them equal to the position of the cell with 1 less index in the table. I update the first cell by just adding a constant value called SCALE, the size of each square on the grid, to it. My problem arises when the position of the position of the first cell is the same as the position of the food. For some reason my lastGridX and lastGridY also change to the x and y positions of the food even though I never change them myself. This makes it so that every cell has the same position which is obviously not what I want to happen. Any help would be appreciated thank you. Here is some of the code below.
function love.update(dt)
-- updates the snake's position in set intervals
moveTimer = moveTimer - dt
if moveTimer <= 0 then
lastGridX = snake[#snake].gridX
lastGridY = snake[#snake].gridY
-- updates the body of the snake
for i = #snake, 2, -1 do
snake[i].gridX = snake[i - 1].gridX
snake[i].gridY = snake[i - 1].gridY
end
-- updates the head of the snake
if direction == "right" then
snake[1].gridX = snake[1].gridX + SCALE
elseif direction == "left" then
snake[1].gridX = snake[1].gridX - SCALE
elseif direction == "up" then
snake[1].gridY = snake[1].gridY - SCALE
elseif direction == "down" then
snake[1].gridY = snake[1].gridY + SCALE
end
moveTimer = MOVE_TIMER_MAX
print(lastGridX, lastGridY)
print(snake[#snake].gridX, snake[#snake].gridY)
if snake[1].gridX == newFood.gridX and snake[1].gridY == newFood.gridY then
-- moves the food to a random location on the grid if it is eaten
newFood.gridX = math.random(0, 63) * SCALE
newFood.gridY = math.random(0, 35) * SCALE
-- adds a new body part to the end of the snake
newC = cell
newC.gridX = lastGridX
newC.gridY = lastGridY
print(lastGridX, lastGridY)
print(snake[#snake].gridX, snake[#snake].gridY)
os.execute("pause")
table.insert(snake, newC)
end
end
end

Coroutine problems

so I'm trying to make a basic GUI animation system in ROBLOX, using individual frames and a loop to put them into an imagelabel.
This is the function:
local playAnimation = coroutine.create(function(anim,pos,tank)
while true do
local animBase = sp.AnimBase:Clone()
animBase.Parent = tank
animBase.Visible = true
animBase.Position = pos -- line that causes the error mentioned below.
local frame = 1
for i = 0, animations[anim]["FrameNum"] do
frame = frame + 1
animBase.Image = animations[anim]["Frames"][frame]
NewWait(.1) --this right here, the wait, interfears with the yield.
if frame >= animations[anim]["FrameNum"] then
pos,anim,tank = coroutine.yield()
break
end
end
animBase:Destroy()
end
end)
There are two main problems with this:
every time it runs, I get this error:
20:41:01.934 - Players.Player1.PlayerGui.ScreenGui.Gui-MAIN:65: bad argument #3 to 'Position' (UDim2 expected, got number)
Although this error doesn't seem to do anything. (eg. stop the script completely)
The line causing the error is marked with a comment.
I've made sure that pos is correct. I even tried printing it before setting it, it prints the correct thing which is:
{0,120},{0,65}
The other main problem is that I can't resume it after using it once. It can run this line multiple times fine:
coroutine.resume(playAnimation,"Cannon Fire",UDim2.new(0,120,0,68-25),tank.Frame)
but it won't run:
if tank2:FindFirstChild("Ammo") and isTouching(ammoFrame,tank2:GetChildren()[3]) then
local lastAmmoPos = ammoFrame.Position
ammoFrame:Destroy()
coroutine.resume(playAnimation,"Explosion",lastAmmoPos-UDim2.new(0,25-(ammoTypes[type]["Size"].X.Offset)/2,0,25),tank.Frame)
tank2:GetChildren()[3]:Destroy()
end
Yes, the if statement is working fine. ammoFrame is destroyed and so is the third child of tank2. The coroutine just won't resume.
Fixed by removing the coroutine completely and wrapping the for loop inside a spawn function.
local playAnimation = function(anim,pos,tank)
local animBase = sp.AnimBase:Clone()
animBase.Parent = tank
animBase.Visible = true
animBase.Position = pos
local frame = 1
spawn(function()
for i = 0, animations[anim]["FrameNum"] do
frame = frame + 1
animBase.Image = animations[anim]["Frames"][frame]
wait(.1) --this right here, the wait, interfears with the yield.
if frame >= animations[anim]["FrameNum"] then
break
end
end
animBase:Destroy()
end)
end

Love2D, LUA Tween resetting

I have a problem with my tweening. I am using tween.lua to move my character left or right when that button is held. On release the player returns back to the middle. Tweening works perfectly for when the character goes either left or right but, for some reason when it has to go back to the middle the character just warps there and does not tween. I suspect that either the base X is overriding it or I am not resseting at the right moment.
Here is my code:
--Local variables
local lg = love.graphics
local lk = love.keyboard
function player:load(arg) --Player load function. Called when loaded.
self.img = lg.newImage(currentPimg)
playerWidth = player.img:getWidth() --Gets player image width and sets as a variable
self.mid = width/2 - playerWidth/2
self.left = 100 - playerWidth/2
self.right = width - 100 - playerWidth/2
self.x = player.mid
self.y = height-150
self.speed = 0.04
GoMid = tween.new(player.speed , player, {x=player.mid}, 'linear')
GoLeft = tween.new(player.speed , player, {x=player.left}, 'linear')
GoRight = tween.new(player.speed , player, {x=player.right}, 'linear')
end
function player:update(dt) --Player update function. Called each frame, passes DT (delta time)
playerWidth = player.img:getWidth() --Gets player image width and sets as a variable
if LeftStarted and not isLeft then
GoLeft:reset()
LeftStarted = false
end
if RightStarted and not isRight then
GoRight:reset()
RightStarted = false
end
if MidStarted and not isMid then
GoMid:reset()
MidStarted = false
end
if isMid then --If is true then do action
GoMid:update(dt)
MidStarted = true
end
if isRight then --If is true then do action
GoRight:update(dt)
RightStarted = true
end
if isLeft then --If is true then do action
GoLeft:update(dt)
LeftStarted = true
end
if lk.isDown("left", "a") and not isRight then --this check needs to be done since the code is executed the first time. If I do not check weird stuff happens
isLeft = true
isRight, isMid = false
elseif lk.isDown("right", "d") then --checks if the button is down and returns true if it is
isRight = true
isLeft, isMid = false
else -- if nothing is down resets player to mid and all variables to false
isLeft, isRight = false
isMid = true
end
end
function player:draw(dt) --Draw function. Called each frame, passes DT (delta time)
lg.draw(player.img, player.x, player.y) --Draws player image at X and Y
end
Working version of your code
So, after messing a lot with the code, I can present the following to you:
player = { } --Required table thing
--Local variables
local lg = love.graphics
local lk = love.keyboard
function player:load(arg) --Player load function. Called when loaded.
self.img = lg.newImage(currentPimg)
playerWidth = player.img:getWidth() --Gets player image width and sets as a variable
self.mid = width/2 - playerWidth/2
self.left = 100 - playerWidth/2
self.right = width - 100 - playerWidth/2
self.x = player.mid
self.y = height-150
self.speed = 0.5
GoMid = tween.new(player.speed , player, {x=player.mid}, 'linear')
GoLeft = tween.new(player.speed , player, {x=player.left}, 'linear')
GoRight = tween.new(player.speed , player, {x=player.right}, 'linear')
end
function player:update(dt) --Player update function. Called each frame, passes DT (delta time)
playerWidth = player.img:getWidth() --Gets player image width and sets as a variable
if LeftStarted and not isLeft then
GoMid = tween.new(player.speed , player, {x=player.mid}, 'linear')
LeftNeedsReset = true
LeftStarted = false
end
if RightStarted and not isRight then
GoMid = tween.new(player.speed , player, {x=player.mid}, 'linear')
RightNeedsReset = true
RightStarted = false
end
if isMid then --If is true then do action
GoMid:update(dt)
end
if isRight then --If is true then do action
if RightNeedsReset then
GoRight:reset()
RightNeedsReset = false
end
GoRight:update(dt)
RightStarted = true
end
if isLeft then --If is true then do action
if LeftNeedsReset then
GoLeft:reset()
LeftNeedsReset = false
end
GoLeft:update(dt)
LeftStarted = true
end
if lk.isDown("left", "a") and not isRight then --this check needs to be done since the code is executed the first time. If I do not check weird stuff happens
isLeft = true
isRight, isMid = false, false
elseif lk.isDown("right", "d") then --checks if the button is down and returns true if it is
isRight = true
isLeft, isMid = false, false
else -- if nothing is down resets player to mid and all variables to false
isLeft, isRight = false, false
isMid = true
end
end
function player:draw(dt) --Draw function. Called each frame, passes DT (delta time)
lg.draw(player.img, player.x, player.y) --Draws player image at X and Y
end
This sort of achieves what I think you wanted (the player smoothly moving back to the center), but I still don't think this is a good solution.
What I did was that I made a new motion with the appropriate starting point whenever the player needs to start moving back towards the center, and I delayed the resetting of the directional motions until it was necessary, because it makes the player jump back to the starting point.
Observations
I have noticed a few things while I was working with your code.
The first one was that you tried to give a value to multiple variables. As far as I know, it doesn't work this way in Lua. In order to do that you would have to write this:
isLeft, isRight = false, false
instead of this:
isLeft, isRight = false
Also a thing that took a while for me to notice while debugging was that you have wrote the resetting parts of the directions in a different order than the updating parts. I wouldn't consider this a good habit unless you have got very strong reasons to do it this way.
Suggestions
In my opinion this library isn't really suited for this task (although I don't know it very well, this was my first time using it while debugging your code). This could be done easily with built in functionality of the language and the framework itself. You could have a variable that keeps track of the current position, and then change it continuously towards the direction of the pressed button until a limit, and then dragging it back to the center when all the keys are released.
I haven't tested this code, just written it blindly, but it might look something like this:
if love.keyboard.isDown("left") then
if currentPosition > middlePosition - movementLimit then
currentPosition = currentPosition - 20 * dt
end
elseif love.keyboard.isDown("right") then
if currentPosition < middlePosition + movementLimit then
currentPosition = currentPosition + 20 * dt
end
else
if currentPosition < middlePosition then
currentPosition = currentPosition + 20 * dt
elseif currentPosition > middlePosition then
currentPosition = currentPosition - 20 * dt
end
end
ps.:Another thing I wouldn't mind would be if you could keep the wording of your comments in the source code between the bounds of good taste.
[khm.. debugging.lua : line 7. khm..]
I hope you succeed with your project! Good luck!

move each individual object - lua

Im quite new to Lua so please pardon my ignorance but i cannot find the solution to my problem.
Whats going on
Im currently trying to move objects from A to B and once object is at B to restart at A and again move to B in a continuous cycle.
local function moveLeft(obj)
print("moving left")
local function resetObj(obj)
transition.to (obj,{ time = 10, x = obj.x + screenWidth + obj.width, onComplete=moveLeft })
end
transition.moveBy (obj,{ time = 3000, x = -screenWidth -obj.width, onComplete=resetObj })
end
and then called using
for idx1 = 1, 8 do
enemyRed = display.newImage("Images/enemyRed.png")
-- 7. Apply physics engine to the enemys, set density, friction, bounce and radius
physics.addBody(enemyRed, "dynamic", {density=0.1, friction=0.0, bounce=0, radius=9.5});
local xPositionEnemy = math.random() + math.random(1, screenWidth)
enemyRed.x = xPositionEnemy;
enemyRed.y = yPosition;
enemyRed.name = "enemyRed"..idx
moveLeft(enemyRed);
end
This is great and all objects are moving from A to B
Problem / issue
The issue here is that the onComplete is not called until ALL objects named "enemyRed" are at point B.
Question
What i want is for each individual object named "enemyRed" to reset to original position A once its reached its destination.
I can't answer the problem/issue because it is not clear (I added a comment). Re the question, you should probably add a A position field to each object, this way you can easily return to it (stylistic note: this is Lua, not c, you don't need semicolons). So In your loop do this:
enemyRed.x = xPositionEnemy
enemyRed.startPos = xPositionEnemy
then in your resetObj do this:
local function moveLeft(obj)
local function resetObj()
print("moving back")
transition.to (obj,
{ time = 10, x = obj.startPos, onComplete=function() moveLeft(obj) end })
end
print("moving left")
transition.moveBy (obj,
{ time = 3000, x = -screenWidth - obj.width, onComplete=resetObj })
end
The above also shows that when calling your moveLeft from the resetObj function, you have to give the obj to the moveLeft otherwise obj will be nil. The resetObjdoes not needobj` parameter since it is an upvalue already.

A lua script on roblox that moves a model upwards?

I have a model called door
Inside I have a BoolValue named Open
I have a model called Top that has all of the door blocks named Work Mabey Comeon and Proboblynot
And I have Block that when touched is supposed to make Top move up
Directly inside door I have this script
door = script.Parent
open = door.Open
Top = door.Top
opener = 18
speed = 100
steps = speed
startl = Top.CFrame
function MoveDoorToCFrame(cfrm,dr)
dr.Work.CFrame = cfrm
dr.Mabey.CFrame = dr.Work.CFrame * CFrame.new(0,-7.2,0)
dr.Comeon.CFrame = dr.Work.CFrame * CFrame.new(0,10.8,0)
dr.Problynot.CFrame = dr.Work.CFrame * CFrame.new(0,10.8,0)
end
function Update()
if speed/steps < 0.5 then
calc = 1-math.cos(math.rad((-90/speed)*steps*2))
else
calc = 1+math.sin(math.rad((90/speed)*((speed/2)-steps)*2))
end
MoveDoorToCFrame(startl * CFrame.new(0,(calc/2)*opener,0),Top)
end
Update()
while true do
wait()
if not open.Value and steps < speed then
steps = steps + 1
Update()
elseif open.Value and steps > 0 then
steps = steps - 1
Update()
end
end
Inside the button that is supposed to activate on touch I have
script.Parent.Touched:connect(function()
script.Parent.Parent.Open.Value = not script.Parent.Parent.Open.Value
end)
script.Parent.Parent.Open.Changed:connect(Update)
Update()
If you know how to fix this it would be gladly appreciated.
Update November 2015:
Using PrimaryPart
Since writing this post, ROBLOX has changed a lot in regards to the API. To move a model like requested, you should set the PrimaryPart property of the model to a central part inside the model. This will act as the origin for the model's movements.
You can then use model:SetPrimaryPartCFrame(cframe) to set the CFrame of the model. You can also retrieve this property by using model:GetPrimaryPartCFrame(), although I believe that is just a shortcut method for model.PrimaryPart.CFrame.
In code, it would look like this:
-- Set PrimaryPart:
MODEL.PrimaryPart = MODEL.SomeCentralPart
...
-- CFrame movement:
local movement = CFrame.new(0, 10, 0)
-- Move the model:
MODEL:SetPrimaryPartCFrame(MODEL:GetPrimaryPartCFrame() * movement)
Option A: Use Model's methods
I think you are making this much more difficult than it needs to be. Whenever you run into an issue like this, be sure to check the current APIs provided. The ROBLOX Model object contains a nifty method called 'TranslateBy' which takes a Vector3 argument to translate the model.
Using MODEL:TranslateBy(Vector3) is similar to moving a model via CFrame, since it ignores collisions.
Another alternative is MODEL:MoveTo(Vector3) which moves a whole model to the given Vector3 world position. The downside to this is that it does collide.
One way to get the same MoveTo effect but without collisions can be done with the TranslateBy method:
MODEL:TranslateBy(Vector3Position - MODEL:GetModelCFrame().p)
Option B: Write a custom function to manipulate the model's CFrame
Another alternative would be to manipulate the whole model's CFrame entirely. To do this, you can write a clever function that will move a whole model relative to an 'origin' point. This is similar to moving shapes on a grid given their points and an origin, except in three dimensions. Using ROBLOX's built-in functions, this is much easier though.
A good way to do this would be to write a function that lets you actually assign a CFrame value to a whole model. Another way would be to allow a translation via CFrame too.
Here's an example:
function ModelCFrameAPI(model)
local parts = {} -- Hold all BasePart objects
local cf = {} -- API for CFrame manipulation
do
-- Recurse to get all parts:
local function Scan(parent)
for k,v in pairs(parent:GetChildren()) do
if (v:IsA("BasePart")) then
table.insert(parts, v)
end
Scan(v)
end
end
Scan(model)
end
-- Set the model's CFrame
-- NOTE: 'GetModelCFrame()' will return the model's CFrame
-- based on the given PrimaryPart. If no PrimaryPart is provided
-- (which by default is true), ROBLOX will try to determine
-- the center CFrame of the model and return that.
function cf:SetCFrame(cf)
local originInverse = model:GetModelCFrame():inverse()
for _,v in pairs(parts) do
v.CFrame = (cf * (originInverse * v.CFrame))
end
end
-- Translate the model's CFrame
function cf:TranslateCFrame(deltaCf)
local cf = (model:GetModelCFrame() * deltaCf)
self:SetCFrame(cf)
end
return cf
end
-- Usage:
local myModel = game.Workspace.SOME_MODEL
local myModelCF = ModelCFrameAPI(myModel)
-- Move to 10,10,10 and rotate Y-axis by 180 degrees:
myModelCF:SetCFrame(CFrame.new(10, 10, 10) * CFrame.Angles(0, math.pi, 0))
-- Translate by 30,0,-10 and rotate Y-axis by 90 degrees
myModelCF:TranslateCFrame(CFrame.new(30, 0, -10) * CFrame.Angles(0, math.pi/2, 0))
This might be hard.
You might want to look to free models for this one unless the people above get it to work.
I, however, do have a script to move a model:
game.Workspace.Model:MoveTo(Vector3.new(0,0,0))
Your code indeed needs fixing.
You should NOT use a never-ending loop to make your stuff work (unless that is the only way).
You should rather base actions on events.
Consider to use this:
Structure:
Door [Model]
DoorScript [Script]
Button [Part]
DoorOpen [BoolValue]
Top [Model]
Mabey [Part]
Comeon [Part]
Problynot [Part]
DoorScript:
local Model = script.Parent
local Door = Model.Top
local Button = Model.Button
local DoorOpen = Model.DoorOpen
local Offset = 0
local ToOffset = 100
local Direction = 1
local StepLength = 0.1
local Moving = false
function StartMoving()
if Moving then return end
Moving = true
while (DoorOpen.Value and Offset ~= ToOffset) or (not DoorOpen.Value and Offset ~= 0) do
local Change = Offset
Offset = math.max(0,math.min(ToOffset,Offset + StepLength * (DoorOpen.Value and 1 or -1)))
Change = Offset - Change
Top:TranslateBy(Vector3.new(0,Change,0))
wait()
end
Moving = false
end
StartMoving()
DoorOpen.Changed:connect(StartMoving)
local Debounce = false
Button.Touched:connect(function()
if Debounce then return end
Debounce = true
DoorOpen.Value = not DoorOpen.Value
wait(4)
Debounce = false
end)
You might want to adjust the speed tho.
This can be used to move models, try adding something like this into your code. It's more dynamic.
a = Workspace.Model
for i=0.1,40 do
for i,v in pairs(a:getChildren()) do
if v:IsA("Part") then
v.CFrame = CFrame.new(v.CFrame + Vector3.new(0,0.1,0))
else print("Not a part")
end
end
end

Resources