How to make an enemy follow a certain path - lua

How do I make my enemy follow a certain path? This is all I have right now, I'm using Tiled for the images and such. I'm trying to make a game where the player enters an area, and has to avoid the enemy to get the coin so the door will open to the next area. But because my enemy is patrolling the enemy sometimes enters the hallway to the next area and that's not supposed to happen obviously.
I also had a problem with the door, it doesn't disappear when I pick up the coin. I thought that the easiest way to do this is that when I reach a certain score, the door is removed, but it doesnt disappear until the player touches the door. Any suggestions? Maybe link the coin with the door so that when it's picked up, they both disappear somehow?
Level.Load ("Game.tmx")
player = {
object = nil,
score = 0,
health = 100,
speed = 3,
}
function Start()
--Identify the objects, naming them
player.object = Level.GetObject ("Player")
enemy = Level.GetObject ("Enemy")
-- music = Sound.Load ("music.mp3", true, true)
Hud.Message ( " Start ", 2)
end
function DoPlayer()
player.object = Level.GetObject( "Player")
end
function DoEnemey()
enemy = Level.GetObjects( "Enemey" )
end
function DoSign()
Sign = Level.GetObject( "Sign" )
end
function DoDoor()
door = Level.GetObject( "door" )
end
function Update()
--HUD
Hud.Score ("Score : " ..player.score )
Hud.Health ("Health : " ..player.health )
--Controllers
Controller.Wasd (player.object, player.speed)
Controller.Patrol (enemy, 2)
Camera.Follow (player.object)
end
function info()
Hud.Message ("Pick up coins to proceed to the next area!", 3)
end
function CoinPickup( target, source)
if target == player.object then
player.score = player.score + 1
Level.RemoveObject( source )
end
end
function DoorOpen( target, source)
if target == player.object
and player.score == 2
then Level.RemoveObject ( source )
end
end

Related

[FIVEM]/LUA. How would i compare two player id's here

Ive been racking brains all day tryna fix this and i am new with lua and cant come up with a solution to this problem even though im rather close.
SO
I have a drift leader board in my server. The problem is the script doesnt check for the players [id] in the table. So if player A has a score of 10 and Player B has a score of 5. Player A can take the 2nd position on the leaderboard (table) by getting a score of any int between. so lets say 7.
LEADERBOARD_CLEAR_TIME = 15 -- THIS IS IN MINUTES
local Players = {}
local Leaderboards = {}
RegisterCommand('cleardriftscores', function(source)
if IsPlayerAceAllowed(source, 'drift') then --Ace permissions
clearLeaderboard()
end
end)
RegisterNetEvent("SaveScore")
AddEventHandler("SaveScore", function(client, data)
local identifier = (GetPlayerIdentifier(source, 0))
local playerName = GetPlayerName(source)
if Players[identifier] ~= nil then
if Players[identifier].pb < data.curScore then
-- Personal Best Beat
local oldScore = Players[identifier].pb
Players[identifier] = { pb = data.curScore }
chatMessage(source, string.format("Congrats! You have just beaten your personal best drift chain score of ^2%s^0 with ^2%s^0!", oldScore, data.curScore))
end
else
Players[identifier] = { pb = data.curScore }
end
if #Leaderboards == 0 then
table.insert(Leaderboards, {score = data.curScore, name = playerName, id = identifier})
chatMessage(-1, string.format("^2%s^0 has started off the leaderboard with a score of ^2%s^0!", playerName, data.curScore))
end
for k, v in ipairs(Leaderboards) do
if v.score < data.curScore and checkLeaderboard(identifier) then
-- Outpassed Someone
table.insert(Leaderboards, { score = data.curScore, name = playerName, id = identifier } )
chatMessage(-1, string.format("^2%s^0 has beaten ^2%s's^0 score of ^2%s^0 with ^2%s^0! They are in ^2%s^0 place", playerName, v.name, v.score, data.curScore, GetPlacement(k)))
break
end
end
table.sort(Leaderboards, compare) --Currently bugged, when player 1 is in first and player 2 is in second. Player 1 can take second by getting a score inbetween table entry 1 & 2
end)
function chatMessage(target, msg)
TriggerClientEvent('chat:addMessage', target or -1, {
color = { 255, 0, 0},
multiline = true,
args = {"[Drift] ", msg}
})
end
function checkLeaderboard(identifier)
for k, v in ipairs(Leaderboards) do
if v.id == identifier then
Leaderboards[k] = nil
end
end
return true
end
function compare(a, b) --actual compare funtion. Need to check for player id's somehow...
if a ~= nil and b ~= nil then
return a.score > b.score --I tried adding another compare here but that didnt work.
end
end
function GetPlacement(number) --This doesnt work very well. (12nd place??)
lastDigit = number % 10
local placement = 'th'
if lastDigit == 1 then
placement = 'st'
elseif lastDigit == 2 then
placement = 'nd'
elseif lastDigit == 3 then
placement = 'rd'
end
return number .. placement
end
function clearLeaderboard()
Leaderboards = {}
chatMessage(-1, "The Drift leaderboard has been cleared!")
end
Citizen.CreateThread(function() --Code for timer to reset leaderboard
while true do
while #Leaderboards == 0 do
Citizen.Wait(0)
end
Citizen.Wait((LEADERBOARD_CLEAR_TIME * 1000 * 60) - (60 * 1000 * 5))
chatMessage(-1, "The Drift Leaderboard is clearing in 5 minutes!")
Citizen.Wait(1000 * 60 * 3)
chatMessage(-1, "The Drift Leaderboard is clearing in 2 minutes!")
Citizen.Wait(1000 * 60 * 1)
chatMessage(-1, "The Drift Leaderboard is clearing in 1 minute!")
Citizen.Wait(1000 * 60 * 1)
clearLeaderboard()
end
end)
The problem here is the code that adds to the leaderboards.
If a player is only allowed to be on a leaderboard once, you should check for that!
Here's how I'd do it:
If the player has beaten his/her personal best then attempt to add his new score to the leaderboards.
Start from the lowest score on the leaderboard. For each score:
If the score owner is equal to the player, delete this score (it's outdated).
If the personal best is smaller than the currently inspected score, insert the personal best one below the current inspected score.
Doing so there won't be any duplicates.
Btw, compare function is just fine ;)
But, just a friendly reminder:
a ~= nil and b ~= nil
--This is simpler and faster!
a and b
Remember that nil in Lua evaluates to false. Therefore, if you need to check if something holds value just:
if a then --This checks if a isn't false or nil
--your code goes here
end
And also that line is not really useful in your comparing function.
Hope that helps!

SCRIPT ERROR: #lscustoms/lscustoms.lua:355: bad argument #1 to 'pairs' (table expected, got nil)

i was trying to implement something into my script and i don't know why i get that error when i try to enter into lscustoms (this script is made for fivem, it lets you tune your car) and then i got blackscreen -
SCRIPT ERROR:
#lscustoms/lscustoms.lua:355: bad argument #1 to 'pairs' (table expected, got nil)
DriveInGarage (#lscustoms/lscustoms.lua:355
fn (#lscustoms/lscustoms.lua:719)
-- Code :
From line 339 to line 360
local m = LSCMenu.categories:addSubMenu("LIGHTS", "Lights", "Improved night time visibility.",true)
AddMod(22,LSCMenu.categories.Lights,"HEADLIGHTS", "Headlights", nil, false)
if not IsThisModelABike(GetEntityModel(veh)) then
m = m:addSubMenu("NEON KITS", "Neon kits", nil, true)
m:addSubMenu("NEON LAYOUT", "Neon layout", nil, true)
local btn = m["Neon layout"]:addPurchase("None")
for n, mod in pairs(LSC_Config.prices.neonlayout) do
local btn = m["Neon layout"]:addPurchase(mod.name,mod.price)
end
m = m:addSubMenu("NEON COLOR", "Neon color", nil, true)
for n, mod in pairs(LSC_Config.prices.neoncolor) do
local btn = m:addPurchase(mod.name,mod.price)btn.neon = mod.neon
end
m = m:addSubMenu("XENON COLOR", "Xenon color", nil, true)
for n, mod in pairs(LSC_Config.prices.xenoncolor) do
local btn = m:addPurchase(mod.name,mod.price)btn.xenon = mod.xenon
local btn = m:addPurchase("Stock")
myveh.xenonColor = -1
end
end
from 682 to 757
--Magical loop that allows you to drive in garage if you successfully go through checks
Citizen.CreateThread(function()
while true do
Citizen.Wait(0)
--If you are not already in garage
if inside == false then
local ped = LocalPed()
--Well... yes... we actually need a car to do something
if IsPedSittingInAnyVehicle(ped) then
local veh = GetVehiclePedIsUsing(ped)
--If the vehicle exist, player is in driver seat and if this vehicle is a car or bike then we are good to go
if DoesEntityExist(veh) and GetPedInVehicleSeat(veh, -1) == ped and (IsThisModelACar(GetEntityModel(veh)) or IsThisModelABike(GetEntityModel(veh))) then
--So lets go through every garage
for i,pos in ipairs(garages) do
--Lets take the outside coords of garage
outside = pos.drivein
--Old enter:If vehicle is close enough, then text will be displayed - Press ENTER to enter garage
if LSC_Config.oldenter then
--So, if vehicle is close enough then we can continue
if GetDistanceBetweenCoords(outside.x,outside.y,outside.z,GetEntityCoords(ped)) <= f(5) then
--Lets check if our vehicle is not in the model black list, and if it is not then we can go further
if not tableContains(LSC_Config.ModelBlacklist,GetDisplayNameFromVehicleModel(GetEntityModel(veh)):lower()) then
--If the garage is locked
if pos.locked then
--If the config lock system is not enabled then we can go traight in garage, but if it is enabled then not
if not LSC_Config.lock then
inside = true
currentpos = pos
currentgarage = i
DriveInGarage()
else
drawTxt("~r~Asteapta, este deja cineva",4,1,0.5,0.8,1.0,255,255,255,255)
end
else
inside = true
currentpos = pos
currentgarage = i
DriveInGarage()
end
else
drawTxt("~r~Masina nu a fost modificata",4,1,0.5,0.8,1.0,255,255,255,255)
end
end
else
--So, if vehicle is close enough and it's facing the garage then we can continue
if math.abs(GetEntityHeading(veh)-outside.heading) <= 90 and IsVehicleStopped(veh) and GetDistanceBetweenCoords(outside.x,outside.y,outside.z,GetEntityCoords(ped)) <= f(5) then
--Lets check if our vehicle is not in the model black list, and if it is not then we can go further
if not tableContains(LSC_Config.ModelBlacklist,GetDisplayNameFromVehicleModel(GetEntityModel(veh)):lower()) then
--If the garage is locked
if pos.locked then
--If the config lock system is not enabled then we can go traight in garage, but if it is enabled then not
if not LSC_Config.lock then
inside = true
currentpos = pos
currentgarage = i
DriveInGarage()
else
drawTxt("~r~Asteapta, este deja cineva",4,1,0.5,0.8,1.0,255,255,255,255)
end
else
inside = true
currentpos = pos
currentgarage = i
DriveInGarage()
end
else
drawTxt("~r~Masina nu a fost modificata",4,1,0.5,0.8,1.0,255,255,255,255)
end
end
end
end
end
end
end
end
end)
If someone know what i was do wrong , please tell me :}

attempt to call method 'insert' (a nil value) when i restart game

I've added objects to the group so they get deleted and respawn when the game restarts but when the game restarts it gives the following error "attempt to call method 'insert' (a nil value)"
This is my object code:
local function spawnObject()
local objIdx = mRandom(#objects)
objName = objects[objIdx]
object = display.newImage( "images/fruit_" .. objName .. "_100.png" )
object.x = mRandom(screenLeft+30, screenRight-30)
object.y = screenTop
object.rotation = mRandom(-15, 15)
object.id = mRandom(-15,15)
group:insert( object )
if objIdx < 4 then
object.type = "food"
else
object.type = "other"
end
physics.addBody(object, "dynamic",{radius=45 , bounce = bt})
grassfront320w:toFront()
object.collision = objectCollision
object:addEventListener( "collision", object )
end
And for restarting I go to restart scene which I've created and from there I go back to my play scene.
Please help me solve the problem.
Don't add directly to the group view. Make a separate display group for the objects
local objectGroup = display.newGroup()
group:insert(objectGroup)
by doing it this way, you are not obligated to kill out the group when you change scene (if you are using composer or storyboard). It will clear it self in the memory when you change scene.

Modify the time value of a transition

How can i change the time value of a transition while is executing? For example I have the object "cesta" moving from the left to the right with a time of 4000ms then for some reason I want to change the time value to move it faster.
function createCesta()
...
transition.to(cesta, {time = 4000, x = screenW + 110})
...
end
function touchScreen(event)
if event.phase == "began" then
end
if event.phase == "ended" then
--change the time value from here "from 4000 to 2000"
end
end
The docs at http://docs.coronalabs.com/api/library/transition/index.html indicate that there is no function call to do this. You would therefore have to cancel the current incomplete transition and create a new one. For example,
local trans
local transTime = 4000 -- ms
local transStart
local object
function someEventHandler(event)
transition.cancel(trans)
local remaining = system.getTimer() - transStart - transTime
if remaining > 0 then
trans = transition.to(object, { time = remaining/2, x = ... }
end
end
function spawn()
object = display.newText(...)
trans = transition.to(object, {time = transTime}
transStart = system.getTimer()
end
This shows a spawn function where you create a display object and make it move via transition to some x, and an event handler that will get called at some point. It computes how much time is left in the transition and if > 0, creates a new transition with half that remaining time so double the "motion" speed of x.

Corona SDK 'nil value' issue

I am making a shooter game using the corona sdk...
My problem is that i am trying to delete an object(that is part of an array)when it leaves the screen.. when i do, i get an error that says 'trying to compare nil value to variable' which refers to a simple move function for every object in the array. Here is relevant parts of code:
function addAlien()
listeners('add')
end
function listeners(action)
if(action == 'add') then
Runtime:addEventListener('enterFrame',update)
enemyTimer = timer.performWithDelay(800,addEnemy,0)
else
Runtime:removeEventListener('enterFrame',update)
timer.cancel(enemyTimer)
end
end
function addEnemy(e)
enemy = display.newImage('drone.png')
enemy.x = 500
enemy.y = math.floor(math.random()*300)
enemy:scale(-0.1,0.1)
enemy.speed = math.random(2,6)
enemies.insert(enemies,enemy)
enemy.enterFrame = moveEnemy
Runtime:addEventListener('enterFrame',enemy)
end
function moveEnemy(self,event)
--if self.x < 100 then
---self:removeSelf()
--self = nil
--removeSelf()
--else
self.x = self.x-self.speed
--end
end
function update(e)
if(enemies.numChildren ~= 0)then
for i = 1,enemies.numChildren do
if(enemies[i] ~= nil)then
--enemies[i].x = enemies[i].x-3
if(enemies[i].x<100)then
--enemies:remove(enemies[i])
--display.remove(enemies[i])
--enemies[i] = nil
end
end
end
end
end
I have commented out the parts that give me errors.
Any help would be appreciated,
thanks
You might want to try and put the delete code in it's own function and then use a timer to remove it so that the function where you are currently deleting the object can return and you're not deleting yourself.
Another option would be to make it temporarily invisible and then loop through the table periodically and remove anything outside of the move handler.
You have a problem with the following code:
function listeners(action)
if(action == 'add') then
Runtime:addEventListener('enterFrame',update)
enemyTimer = timer.performWithDelay(800,addEnemy,0)
else
Runtime:removeEventListener('enterFrame',update)
timer.cancel(enemyTimer)
end
end
You only want to Runtime:addEventListener('enterFrame', update) once. If you do it everytime you add an enemy things will go wrong. The problem is your code runs again and again after the objects have been removed already.
hmmm. did you use the scene template? if so, you should only put the :removeself () and = nil values in the scene destroy section at the bottom. you also dont need the second removeself that is not attached to an object as that is probably the nil issue.
to summarize.
1-put all remove self () and = nil at the destroy section of scene template and use object.isVisible = false instead.
2-the nil error is most likely coming from the removeself() statement with no object. so change it to moveenemy:removeself ()
if my understanding is right this is how i would do it without using runtime
and using timer instead. ask away if you have question
local scrWidth = display.actualContentWidth
local scrHeight = display.actualContentHeight
local enemy = {} --this will hold your aliens
function addEnemy()
enemy[#enemy + 1] = display.newImage("drone.png")
enemy[#enemy].x = 500
enemy[#enemy].y = math.floor(math.random()*300)
enemy[#enemy]:scale(-0.1,0.1)
enemy[#enemy].speed = math.random(2,6)
end
local function update()
addEnemy()
--this will move the enemy to the left from right
for i=1,#enemy,1 do
enemy[i].x = enemy[i].x - enemy[i].speed
end
--the below codes will destroy each enemy that is out side the screen on the left
local function destroyWhenOutside()
for i=1,#enemy,1 do
if enemy[i].x < 0 - enemy[i].width then
enemy[i]:removeSelf()
enemy[i] = nil
elseif enemy[i].y < 0 - enemy[i].height then
enemy[i]:removeSelf()
enemy[i] = nil
end
end
end
destroyWhenOutside()
end
--this will loop the update every 1/1000 seconds
local timerUpdate = timer.performWithDelay(1,update,-1)
.

Resources