Jump to content

Dzsozi (h03)

Members
  • Posts

    682
  • Joined

  • Last visited

  • Days Won

    2

Posts posted by Dzsozi (h03)

  1. On 28/11/2022 at 07:43, Zango said:

    3. getElementsByType("removeWorldObject") will return custom elements that represent each removal. Use getElementData on each element and get posX, posY, posZ and radius data fields, on the ones with the right model.

    Thank you, I didn't know I could do this with getElementsByType ?

  2. This is a great idea and would be much simpler and easier indeed, now that you said I realised it. But I think the reason I wanted to do a scanning type solution is because of the custom placed objects (i know I can check those and I already did in the original script), but also, more likely because of the removed world models. What would you suggest to check that, if I were doing the solution you mentioned? I have some custom maps which for sure remove some lamps, I made some that removes even a whole street.

  3. Sorry for bumping this topic that late, I got some free time now,  I still wasn’t able to finish this project based on the examples, also I just realised that the scan will be going diagonally if I add to x and y at the same time, therefore the scan wouldn’t apply to the whole world.

    I would like to achieve an effect of scanning such as from the top left corner of the map to the bottom right, going sideways, and when the camera reaches the right side, it snaps back to the left and gets offset on the y axis.

    I am not sure about how to achieve this between the range of -3000 to 3000 on the x and y axis inside a coroutine function with loops, am I even doing it the right way by using for loop? I would have to prevent the loop from happening on the y axis before the scanX reaches the right side.

    Right now I can’t send my current code and I can’t remember what errors I got, I’m at work but I wanted to reply and let you know that I’m still interested and trying to fix & get it done.

    I will post my code asap, could you please maybe help me finishing and correcting it?

  4. On 16/11/2022 at 19:36, IIYAMA said:

     

    A coroutine is a kind of thread type, which you can use to put a function to a temporary stop.

     

    Thank you for teaching me, really, I tried to make the scanning with these functions, I get an error spam "cannot resume dead coroutine" because of the timer (this line)

    STREET_LIGHTS.SCAN_TIMER = setTimer(resumeScan, 1000, 0)

    Here's my current code, could you help me with what am I doing wrong?

    local STREET_LIGHTS = {
    	VALID_MODELS = {
    		[1294] = {name = "mlamppost", offsets = {Vector3(0,0.3,1)}, radius = {start = 0.075, stop = 50}},
    		[1297] = {name = "lamppost1", offsets = {Vector3(0,0.3,1)}, radius = {start = 0.075, stop = 50}},
    		[1290] = {name = "lamppost2", offsets = {Vector3(3,0.03,5.4), Vector3(-3,0.03,5.4)}, radius = {start = 0.075, stop = 75}},
    		[1226] = {name = "lamppost3", offsets = {Vector3(-1.3,0.165,3.675)}, radius = {start = 0.075, stop = 50}},
    	},
    	
    	OBJECT_LIGHTS = {},
    }
    
    local SCAN = {
    	current = Vector2(-3000, -3000),
    }
    
    local SCAN_WORLD = coroutine.create(function()
    	for x = SCAN.current.x, 3000, 10 do
    		if math.abs(x) % 100 == 100 then SCAN.current.x = x print"yield" coroutine.yield() end
    		
    		for y = SCAN.current.y, 3000, 10 do
    			if SCAN.current.x  >= 3000 and SCAN.current.y >= 3000 then
    				if isTimer(STREET_LIGHTS.SCAN_TIMER) then killTimer(STREET_LIGHTS.SCAN_TIMER) end
    				STREET_LIGHTS.SCAN_TIMER = nil
    				setCameraTarget(localPlayer)
    				coroutine.yield()
    				break
    			end
    			
    			if math.abs(y) % 100 == 100 then SCAN.current.y = y coroutine.yield() end
    			setCameraMatrix(x, y, 500, x, y, 0)
    			
    			local hit, _,_,_,_,_,_,_,_,_,_,worldModelID,worldX,worldY,worldZ,worldRotX,worldRotY,worldRotZ,_ = processLineOfSight(
    				x, y, 500,
    				x + 5, y + 5, 0,
    				true, false, false, true, false,
    				false, false, false, nil, true, false
    			)
    			
    			if hit then
    				--if STREET_LIGHTS.VALID_MODELS[worldModelID] then
    					print(worldModelID) -- do i actually get the models on screen? i get some numbers for sure but camera is stuck and position is not updating
    				--end
    			end
    		end
    	end
    	return true
    end)
    
    local function resumeScan()
    	return coroutine.resume(SCAN_WORLD)
    end
    
    addEventHandler("onClientResourceStart", resourceRoot, function()
    	if not isTimer(STREET_LIGHTS.SCAN_TIMER) then
    		STREET_LIGHTS.SCAN_TIMER = setTimer(resumeScan, 1000, 0)
    	end
    	
    	return true
    end)

    And actually I don't even get the "yield" output as expected from this line:

    if math.abs(x) % 100 == 100 then SCAN.current.x = x print"yield" coroutine.yield() end

    Camera is stuck at one place on the top right corner of the map and first I get a bunch of spam of 3 or 4 numbers, then the error about cannot resume dead coroutine.

  5. On 14/11/2022 at 18:19, IIYAMA said:

    Is everything clear or do you have any questions? @Dzsozi (h03)

    Sorry I didn’t make time to reply, my fault. Actually it is not so clear, I don’t really know how to use coroutine functions, I never did before. Not so sure how to implement it for the purpose I need.

    On 15/11/2022 at 12:11, thisdp said:

    by the way, you need to move your camera at the same coordinate of processLineOfSight, because of the streaming system.

    Thank you for the advice I didn’t even think about that and actually makes sense to do it.

  6. Hello!

    I would like to get the positions of all the lampposts. I was trying to use processLineOfSight to achieve this, since getElementsByType("object") doesn't return default world objects.

    I tried using a loop from 0 to 3000 on the x and y axis, but I get an infinite running script error. Why do I get this error and how could I achieve a scan on the world? Here's my current code:

    (I commented out most parts while trying to find the cause of the problem, so I would only see the positions of all the lamps if the scan was successful)

    local STREET_LIGHTS = {
    
    	VALID_MODELS = {
    		[1294] = {name = "mlamppost", offsets = {Vector3(0,0.3,1)}, radius = {start = 0.075, stop = 50}},
    		[1297] = {name = "lamppost1", offsets = {Vector3(0,0.3,1)}, radius = {start = 0.075, stop = 50}},
    		[1290] = {name = "lamppost2", offsets = {Vector3(3,0.03,5.4), Vector3(-3,0.03,5.4)}, radius = {start = 0.075, stop = 75}},
    		[1226] = {name = "lamppost3", offsets = {Vector3(-1.3,0.165,3.675)}, radius = {start = 0.075, stop = 50}},
    	},
    	
    	OBJECT_LIGHTS = {},
    }
    
    function processStreetLights()
    	for x = 0, 3000, 5 do
    		for y = 0, 3000, 5 do
    			local hit, _,_,_,_,_,_,_,_,_,_,worldModelID,worldX,worldY,worldZ,worldRotX,worldRotY,worldRotZ,_ = processLineOfSight(
    				x, y, 0,
    				x + 5, y + 5, 500,
    				true, false, false, true, false,
    				false, false, false, nil, true, false
    			)
    			
    			if hit then
    				--[[if STREET_LIGHTS.VALID_MODELS[worldModelID] then
    					local data = STREET_LIGHTS.VALID_MODELS[worldModelID]
    					local searchlights = {}
    					
    					for i, offset in pairs(data.offsets) do
    						local startPos = Vector3(worldX, worldY, worldZ + offset.z) --Vector3(exports["sa_utility"]:getPositionFromElementOffset(obj, offset.x, offset.y, offset.z))
    						
    						startPos.x = worldX + math.sin( math.rad( worldRotZ ) ) * offset.x
    						startPos.y = worldY + math.cos( math.rad( worldRotZ ) ) * offset.y
    						
    						local endPos = startPos --Vector3(exports["sa_utility"]:getPositionFromElementOffset(obj, offset.x, offset.y, 0.25))
    						endPos.z = worldZ + 0.25
    						
    						searchlights[i] = SearchLight(startPos, endPos, data.radius.start, data.radius.stop, true)
    					end
    					
    					--STREET_LIGHTS.OBJECT_LIGHTS[obj] = searchlights
    					table.insert(STREET_LIGHTS.OBJECT_LIGHTS, searchlights)
    				end]]
    				print(worldX, worldY, worldZ)
    			end
    		end
    	end
    	
    
    	--[[for k, obj in pairs(getElementsByType("object")) do
    		if STREET_LIGHTS.VALID_MODELS[obj.model] then
    			local data = STREET_LIGHTS.VALID_MODELS[obj.model]
    			local searchlights = {}
    			
    			for i, offset in pairs(data.offsets) do
    				local startPos = Vector3(exports["sa_utility"]:getPositionFromElementOffset(obj, offset.x, offset.y, offset.z))
    				local endPos = Vector3(exports["sa_utility"]:getPositionFromElementOffset(obj, offset.x, offset.y, 0.25))
    				
    				searchlights[i] = SearchLight(startPos, endPos, data.radius.start, data.radius.stop, true)
    			end
    			
    			STREET_LIGHTS.OBJECT_LIGHTS[obj] = searchlights
    		end
    	end]]
    	
    	return true
    end
    
    addEventHandler("onClientResourceStart", resourceRoot, function()
    	processStreetLights()
    	return true
    end)

    Thank you for help in advance!

  7. On 26/10/2022 at 00:42, Snow-Man said:

    I have already did one like this

    dxSetRenderTarget(texture, true)

    dxDrawText(500, 500, 240, 250, .....)

    dxSetRenderTarget ()

    dxDrawImage(500, 500, 120, 125, image...)

     

    But the same problems doesn't display, when i apply the texture the text just disappear

    It is working and the text is drawn, the problem is that you are setting the text position outside of the render target canvas size.

    When you are drawing inside a render target, the positions start from 0 again.

    textureWidth, textureHeight = 100, 50
    texture = dxCreateRenderTarget(textureWidth, textureHeight, true)
    
    dxSetRenderTarget(texture, true)
       dxDrawText("text", 5, 5, textureWidth, textureHeight)
    dxSetRenderTarget()
    
    dxDrawImage(posx, posy, textureWidth, textureHeight, texture)

    This will draw the render target as an image wherever you want, and inside of it will be the text on the top left corner, offset by 5 pixels.

  8. 27 minutes ago, IIYAMA said:

    Step 1: Find ALL existing slots

    This method does not do that unfortunately. So you will have to create a new one.

    local carrySlots = self:findExistingItemSlots(itemID)

    Step 2: Fill those up.

    Step 3: Create new slots for the remaining items.

    Repeat until is fine. ?

    repeat 
    	local slotX, slotY = self:findFreeSlotForItem(itemID)
    	if slotX then
    		local countInsert = math.min(count, itemDetails.stacklimit)
    
    		-- ... 
     
    		local newItem = {
    			["itemID"] = itemID,
    			["count"] = countInsert,
    			["slot"] = {x = slotX, y = slotY},
    		}
    		count = count - countInsert
        
     		-- ...
    	end
    until count == 0 or not slotX

     

    Step 4: Return the items that do not fit, so that you can decide what to do with those.

    local remainingItems = object:giveItem(1, 8) 
    if #remainingItems > 0 then
    	iprint("items do not fit", #remainingItems)
    end

     

     

     

     

    Thank you so much, it works and I also managed to do the step 4 to manage remaining items, so I can add to main inventory or drop them. I appreciate it and your time ❤️

    • Like 1
  9. Hello!

    I started re-doing my inventory system, I made some functions based on this topic 

    I did everything without a problem, everything works fine, except a calculation I am trying to do when giving an item to a container. I have stack limits defined for items inside an ITEM_TABLE.

    The stack limit for, let's say Apple item is 5. When calling the giveItem function, I would like to check if the count exceeds the stack limit, then loop the item giving process until the newly calculated count value is less than the stack limit.

    So let's say I call

    -- itemID, count
    giveItem(1, 8) -- give 8 apples, but the apple has a stack limit of 5, so I would like the player to end up with 2 apple items, one with a count of 5 and one with a count of 3

    So then I would like the player to have 2 different apple items, one with a count of 5 and one with a count of 3

    This is how I was trying to do it, but it seems like I can't use the repeat until/while loop properly, or I am missing calculations, I can't get the result I want.

    function ContainerMethods:giveItem(itemID, count)
    	local itemDetails = getItemDetails(itemID)
    	if not itemDetails then return false end
    	count = tonumber(count) or 1
    	
    	local carrySlot = self:findExistingItemSlot(itemID)
    	if carrySlot and carrySlot.count + count <= itemDetails.stacklimit then -- if the container has an item like this and it's less than the stack limit, add the count
    		carrySlot.count = carrySlot.count + count
    	else
    		local tempCount = count
    		
    		repeat -- i don't know if i should use repeat until, or while loop, repeat until seems like the correct way of doing it
    		--while tempCount > itemDetails.stacklimit do
    			local slotX, slotY = self:findFreeSlotForItem(itemID)
    			if slotX and slotY then -- i am working with a grid inventory with different item sizes
    				for x = slotX, slotX + itemDetails.size.x - 1, 1 do
    					for y = slotY, slotY + itemDetails.size.y - 1, 1 do
    						self.grid[x][y] = true
    					end
    				end
    				
    				local newItem = {
    					["itemID"] = itemID,
    					["count"] = tempCount, -- should i make calculations here as well?? i don't think so but i could be wrong
    					["slot"] = {x = slotX, y = slotY},
    				}
    				
    				table.insert(self.items, newItem)
    				
    				tempCount = tempCount - itemDetails.stacklimit -- tempCount is the difference
    				print(tempCount)
    			else
    				print("no empty space for item '" .. itemID .. "' in container '" .. self.name .. "'")
    				return false
    			end
    		--end
    		until tempCount < itemDetails.stacklimit -- repeat adding a new item until the count is less than the stack limit
    	end
    	
    	return true
    end

    But the result I get is:

    spacer.png

    Notice that the debugscript prints 3, but my inventory has only one item with the count of 8, instead of 5 and 3. How can I make this function properly, so I don't have to worry about it in the future when I give an item to the player?

    Thank you for your help in advance!

  10. Sorry for not responding, meanwhile I solved the problem and this topic went unnoticed by me. I solved it by using Kam's updated scripts made by gold_fish ([REL] KAM's GTA Scripts (2018) / UPD: 31.05.2020 / - Tools - GTAForums). I read that this updated script supports exporting and importing normals and also reflections, therefore the problems were solved. Also I think I had some issues with the model itself, I was playing around with the smoothing groups and etc.

    Using the updated scripts solves the problem, however I will have to remake my model tho.

    This topic can be locked. 

  11. 9 hours ago, Cronoss said:

    First function:

    function toggleEngine(playerSource, cmd)
        local vehicle = getPedOccupiedVehicle(playerSource)
        if (vehicle) then

    Second function:

    function toggleLights(playerSource, cmd)
        local vehicle = getPedOccupiedVehicle(thePlayer)
        if (vehicle) then

      The second function has a problem returning the occupied vehicle, I don't know if that could be the cause of the problem you are having, or is that just a typo while you were writing the examples on forum.

    function toggleLights(playerSource, cmd)
        local vehicle = getPedOccupiedVehicle(playerSource) -- use playerSource instead of thePlayer, thePlayer is not defined so this will return an error and toggling lights won't happen
        if (vehicle) then

     

    9 hours ago, Cronoss said:

    Also, calling the database in different functions would cause bad optimization / lag ? 

    Example:

    function toggleLock(playerSource, cmd) ---------Toggle lock function
    	local owner = tostring(getPlayerName(playerSource))
        local vehicle = getNearestVehicle( playerSource, 5 ) or getPedOccupiedVehicle(playerSource)
        if (vehicle) then 
            local nameplate = getVehiclePlateText( vehicle )
            local ownerReq = exports.mysql:_Query("SELECT * FROM vehicles WHERE plate=?", nameplate) ------"First" time using the database to compare the plate
        	if (ownerReq) then
          
    ----------Toggle engine function-----------
    local nameplate = getVehiclePlateText( vehicle )
    local ownerReq = exports.mysql:_Query("SELECT * FROM vehicles WHERE plate=?", nameplate) --second time using the database in ANOTHER FUNCTION
        	if (ownerReq) then
            	if(#ownerReq > 0) then

    Yes, this would cause lag and lots of unnecessary data flow. You don't have to get a mysql result everytime you lock/start a vehicle. If I were doing toggleLock and toggleEngine functions for vehicles, I would just simply use them when a player types a command / presses a key, as you are doing, inside those toggleLock and toggleEngine functions simply use setVehicleLocked and setVehicleEngineState like

    function toggleLock(vehicle, state)
    	if not vehicle then return false end
    
    	setVehicleLocked(vehicle, state)
    	return true
    end

    For the command you would do

    function lockCommand(player, command)
    	local vehicle = getPedOccupiedVehicle(player)
    	if vehicle then
    		toggleLock(vehicle, not isVehicleLocked(vehicle))
    	end
    end
    addCommandHandler("lockveh", lockCommand)

    Keep the actual toggle functions seperate from the command functions, that way it will be easier for you to see, utilize and use those functions later as well.

    Then have a different save resource or function to save the data to mysql. You can make a timer every 5 seconds to call the saving, and inside the save function you would get every desired vehicle and their state of engine and locks to update mysql data with the new ones. This would make data usage and network flow much more less in my opinion, other than calling for mysql everytime a player types a command; imagine if they start spamming.

    Of course you can make anti-spam checks, but overall that way would be much more inefficient.

    Also there is a resource that you can use to check the usage of resources: performancebrowser - Multi Theft Auto: Wiki
    I believe it is already included in default MTA resources.

    • Thanks 1
  12. Possibility to create functioning ladders, create a ladder without the custom object, for default world ladders, or create a brand new one. Only vertical ladders are supported with Z rotation. Features one custom object.

    Easy to use functions, ~240 lines of code.

    Sprint button for fast climbing
    Jump button for jumping off a ladder
    Forward button for climbing up
    Backward button for climbing down

    You can basically create any length ladder with correct positions, since you can adjust offsets for start and end positions, so shifting leadders in the ground is a possibility to match the height of a building. Ladder is using collision tuboids for player detection.

    Contains a "/getladderpos" utility command to get the corrected position and rotation in front of player, copies Vector3 and int rotation to clipboard.

    Power of creating more interesting maps, possibility and an idea for a new "ladder parkour" gamemode, or just create more interesting roleplay / deathmatch / other scenarios by adding climbable ladders to the environment.

    I am planning to add custom animations, these default ones are good for now, and default world ladder positions in the future.

     

    I'm waiting for your feedback, let me know if you like it and/or have any suggestions! :D

    • Like 2
  13. 25 minutes ago, IIYAMA said:

    Yup, you can.

     

    local Highlight 
    local HighlightConstructor
    do
      local register = {}
      -- depending were you want it.
      Highlight = {
          register = register
      } 
    
      HighlightConstructor = {
          register = register
      } 
    end
    
    function Highlight:new( element, style, color, flashSpeed, intensityOrThickness )
      
    	local register = self.register
    	if register[element] then return end
    
    	local data = { element = element, style = style, color = color, flashSpeed = flashSpeed, intensityOrThickness = intensityOrThickness }
    	
    	register[element] = data
    	-- ...
      
    --
      
    newHightlight = Highlight:new()
    iprint(Highlight.register)
    iprint(newHightlight.register)

     

     

    Thank you again! I will try to make and implement it! Really appreciate your time, have a nice day!

    • Like 1
  14. 2 hours ago, IIYAMA said:

    You were almost there, you are doing great :)

    The trick here is to split of the constructor. So that your new table will only inherit methods from the constructor.

    local Highlight = {} -- base, just for the 'new' method
    local HighlightConstructor = {} -- constructor

      Finally, thank you so much!! This is what I was looking for, I just couldn't figure out how to implement it, I already knew in my mind that I will have to make 2 tables for this. For some reason I thought I can make the 'update' and 'destroy' functions in the same table as the 'new' function, turns out I can't if I want to avoid bad things to happen. :D

    Now I have this setup at the beginning and it works fine as intended

    local Highlight = {}
    --Highlight.__index = Highlight    -- I DONT THINK I WILL NEED THIS ANYMORE
    setmetatable(Highlight, {__call = function(_,...) return Highlight:new(...) end}) -- calling Highlight will call the Highlight:new method
    
    local HighlightConstructor = {}
    HighlightConstructor.__index = HighlightConstructor -- I NEED THIS INSTEAD

    playerHighlight.style returns given style, playerHighlight.new or playerHighlight:new doesn't work like I wanted to.

    2 hours ago, IIYAMA said:

    Note keep in mind there is a difference in . and : calls, depending on the set-up. Just in case you didn't know, which you probably did know.

    Yes, I knew that, thank you for giving a heads up! I changed it from . to : because you did it that way as well in your response, I am not sure tho what difference it makes in this case. I mean it should make no difference right here, if I am correct, since I am not using self inside the Highlight:new method.

    Anyways thank you once again! I learned new stuff and I am happy for that, also I can finally finish this resource xd

     

    EDIT: By the way, can I get rid of CREATED_HIGHLIGHTS and make it part of Highlight table? Will it work with a Highlight = {created = {}} setup or something like this? Or should I just keep them seperate? Since I was thinking about making methods like Highlight.getAllByElementType and such so I can get every created highlight of vehicles for example.

    • Thanks 1
  15. On 18/02/2022 at 17:01, IIYAMA said:

     

    Meta table = gets inherited

    local Highlight = {test1 = true}

    local newTable = setmetatable({test2 = true}, {__index = Highlight})

     

    The normal table = is inheriting from meta table

    local Highlight = {test1 = true}

    local newTable = setmetatable({test2 = true}, {__index = Highlight})

     

    print("newTable:", newTable.test1, newTable.test2 ) -- newTable:	true	true
    print("Highlight:", Highlight.test1, Highlight.test2 ) -- Highlight:	true	nil

     

    This means that the 'new' method should only be applied on newTable.

    local Highlight = {test1 = true}
    
    function newChild (self)
    	print("self", self)
    	return setmetatable({}, {__index = Highlight})
    end
    
    local newTable = setmetatable({test2 = true, new = newChild}, {__index = Highlight})
    
    print(newTable:new())
    print(newTable:new().test1) -- true
    print(newTable:new():new()) -- attempt to call a nil value (method 'new')

    For more explanation, please read the comments I made in the scripts as well;

    I am sorry, but I don't really understand how should I change my code. Could you please provide me a solution for this?

    So I have a Highlight table, each time this table gets called, a new Highlight is being created for the given element variable (I would like to make it function like the default MTA OOP stuff, like Vehicle(model, position) instead of Vehicle.new(model, position), so I have this setup here:

    local CREATED_HIGHLIGHTS = {}
    
    local Highlight = {}
    Highlight.__index = Highlight
    setmetatable(Highlight, {__call = function(_,...) return Highlight.new(...) end})
    
    function Highlight.new(element, style, color, flashSpeed, intensityOrThickness)
    	if not isElement(element) then return false end
    	if CREATED_HIGHLIGHTS[element] then return false end
    	
    	local data = setmetatable({}, Highlight) -- this has something else to do with it I guess
      
      	-- set variables
      
    	-- data.element = element
      
      	-- and so on
    
    	return data
    end

    And I have methods for it like Highlight:update() and Highlight:destroy(), the way I did it for example:

    function Highlight:destroy()
    	if not CREATED_HIGHLIGHTS[self.element] then return false end
    	if self.shader then self.shader:destroy() end	
    	
    	CREATED_HIGHLIGHTS[self.element] = nil
    	
    	return true
    end

    When I create a new highlight for the localPlayer, like:

    local playerHighlight = Highlight(localPlayer, "outline", {255,0,0,255}, 0, 1)

    Now everything works fine above, highlight gets created, I can access the data with playerHighlight.thickness and such, BUT

    I can then use the playerHighlight table (which should contain only data, like style, thickness etc) to create a new highlight on a totally different element like:

    local dumpster = Object(1337, localPlayer.position + Vector3(0,3,1))
    print(playerHighlight.new(dumpster, "fresnel", {100,200,255,255}, 2, 0.5)) -- returns table, I don't want to use .new here
    -- how can I make it return nil, or avoid calling of .new, but keep the :update() and :destroy() methods
    -- I would like playerHighlight to contain only the data given before

    Now I understand that the data table inherits from Highlight table at the Highlight.new function, so to the data table the methods get passed as well, but how should I change it so it won't inherit all the functions, I would like to access actual data and only methods with colon (:update, :destroy), not with dot (.new) . So my guess is that this line should be changed to something else instead of Highlight, I imagine

    local data = setmetatable({}, Highlight) -- change Highlight to what? Am I missing the creation of another table?

     

    Sorry if it looks like I am asking the same question twice, I am not ignoring your reply but I don't understand your explanation @IIYAMA, I mean I don't understand how should I implement it in this case.

    • Like 1
  16.  UPDATE: I got rid of the loadstring()() injection concept for this Class.lua script because it looks unnecessary, I will just make classes independently in resources because it is almost the same effort to do right now for what I would like to achieve, so I just changed

    local Highlight = {} --instead of Class()
    -- Highlight:new to Highlight.new, and inside the .new function
    local data = setmetatable({}, {__index = Highlight})

    However, I can still call .new function in the testing section with

    -- testing
    local playerHighlight = Highlight(localPlayer, "fresnel", {0,255,0,255}, 0, 1) -- creating new Highlight class for player
    local dumpster = Object(1337, localPlayer.position)
    playerHighlight.new(dumpster) -- [[ 	I DON'T WANT TO DO THIS :/    ]]
    • Like 1
  17. 4 hours ago, IIYAMA said:

    Do you remember that after loading a string, you have to call it again?

    It basically means that there is a function around it. And every time you call that function, you run the code inside it.

     

    That's how I did it in the first place, but I came up with an easier, and imo clenaer looking injector for lua files, this is how I did it:

    ooplib/Class.lua (however, if I make Class = {} a local variable, I get Attempt to call global 'Class' nil value error, I am curious why?)

    Class = {}
    Class.__index = Class
    setmetatable(Class, {__call = function( _,... ) return Class.new(...) end })
    
    function Class.new(properties)
    	local self = setmetatable(properties or {}, Class)
    	return self
    end

    ooplib/infuse.lua

    function getFileContent(filePath)
    	if not filePath then return false end
    	if not fileExists(filePath) then error("Can't open '"..filePath.."' (file doesn't exist)", 2) return false end
    	
    	local f = fileOpen(filePath, true)
    	local content = f:read(f.size)
    	f:close()
    	
    	return content
    end
    
    function classTemplate()
    	return getFileContent("Class.lua")
    end

    otherresource/testscript.lua

    loadstring(exports["sa_ooplib"]:classTemplate())() -- inject the Class.lua file's content
    
    local Highlight = Class() -- call the Class function from the injected script
    Highlight.__index = Highlight
    setmetatable(Highlight, {__call = function(_,...) return Highlight:new(...) end}) -- making this so I can call Highlight()
    
    function Highlight:new(element, style, color, flashSpeed, intensityOrThickness) -- create a Highlight class
    	local data = setmetatable({}, {__index = self}) -- COULD BE A PROBLEM HERE? Change self to what? Or is it okay like this?
    	-- set variables
    	data.element = element
    	-- and so on
    	return data
    end
    
    -- testing
    local playerHighlight = Highlight(localPlayer, "fresnel", {0,255,0,255}, 0, 1)
    setTimer(function()
    	local dumpster = Object(1337, localPlayer.position)
    	playerHighlight:new(dumpster) -- [[ 	I DON'T WANT TO DO THIS :/    ]]
    end, 2000, 1)

    But as you look at the testscript.lua script, I commented a line which I have new problems with. I can call the :new method from the returned playerHighlight data. How can I avoid doing that?

    Now I imagine that I would have to change the __index from self to something else?

    local data = setmetatable({}, {__index = self})

    Am I missing steps on setting up the Class.lua script?

  18. engineSetModelLODDistance
    setFogDistance
    setFarClipDistance

    Combining these functions should do the trick if you are trying to make the view distance higher. In some cases using setFarClipDistance should be enough.

    Also there are
    setVehiclesLODDistance and setPedsLODDistance

    Take a good look on these functions' wiki pages, high values for some of these could cause fps lag. Some of these functions' maximum values are also locked to a certain value.

    • Like 1
  19. UPDATE: I think I found the cause of the problem; if I make the Class table a global variable instead of a local variable, the script works, debugscript outputs 'xd' from testscript.lua as intended. But wouldn't the loadstring function make the script look basically like this?

    --[[
    	after using loadstring(createClass())() I imagine the script would have
    
    
    	local Class = {}
    	Class.__index = Class
    	setmetatable(Class, {__call = function( _,... ) return Class.new(...) end })
    
    	function Class.new()
    		local self = setmetatable({}, Class)
    		return self
    	end
    
    
    	at the beginning, after that comes the part down below
    	
    	|
    	|
    	V
    ]]
    
    
    local testClass = Class()
    testClass.name = "xd"
    
    function testClass:getName()
    	return self.name
    end
    
    print(testClass:getName())

     

    So I don't understand why making the 'Class' table as a local variable would be a problem.

    Is it because the type is defined as 'shared' in the meta, that's why I should make it a global variable??

    UPDATE: I changed Class.lua type from 'shared' to 'server' to match testscript.lua type, made Class a local table again, and I got the error again, so I don't understand really why it should be a global variable.

     

    Anyways, the fix was changing

    function createClass()
    	return [[
    		local Class = {}
    		Class.__index = Class
    		setmetatable(Class, {__call = function( _,... ) return Class.new(...) end })
    
    		function Class.new()
    			local self = setmetatable({}, Class)
    			return self
    		end
    	]]
    end

    to

    function createClass()
    	return [[
    		Class = {}
    		Class.__index = Class
    		setmetatable(Class, {__call = function( _,... ) return Class.new(...) end })
    
    		function Class.new()
    			local self = setmetatable({}, Class)
    			return self
    		end
    	]]
    end

    in case if you need this for your scripts.

    I would still appreciate if somebody could answer my questions regarding that problem!

  20. Hello!

    Lately I've been trying to make every script in OOP for simplicity. Since you have to define metatables and setup classes and methods for OOP to work properly, I tried to make an export function for it so I can create classes more easily. But when I was trying to do that and test it, I get an error message, which says:

    spacer.png

    And I don't really understand the reason why. What am I doing wrong? Right now I have 2 lua files, one of which contains the class creation, which is the following:

    Class.lua (set to 'shared' type in meta)

    function createClass()
    	return [[
    		local Class = {}
    		Class.__index = Class
    		setmetatable(Class, {__call = function( _,... ) return Class.new(...) end })
    
    		function Class.new()
    			local self = setmetatable({}, Class)
    			return self
    		end
    	]]
    end

    and I have a test script, which is in the same resource, just a different lua file:

    testscript.lua (set to 'server' type in meta)

    loadstring(createClass())()
    
    local testClass = Class()
    testClass.name = "xd"
    
    function testClass:getName()
    	return self.name
    end
    
    print(testClass:getName())

    As you can see I am using loadstring() function to infuse the class creation to the testscript.lua, but despite the fact I still get an error about Class() function being nil, why?

    Also, I have the Class.lua (which is set as 'shared') before anything else in the meta, so I really don't understand what causes this error. Just in case if you were wondering, here is the meta.xml:

    <meta>
    	<oop>true</oop>
    	
    	<script src="Class.lua" type="shared"/>
    	<script src="testscript.lua" type="server"/>
    </meta>

    Right now this is the full resource basically.

     

    Thank you for your answer in advance!

  21. 32 minutes ago, Cronoss said:

    I just found that I can "restrict" the spam of a command with "onPlayerCommand", I tested it and it works, it doesn't allow the command to execute if the player doesn't wait 2 seconds, there is any difference between this and that table example? 

    That could be a solution as well.

    I don't know the way you made your script since you didn't post it. But I would say that with my example you wouldn't need to check spamming with the onPlayerCommand event, I think it would make things easier. Also, the timer in my example is not necessary, I thought you would like to add 2.5 seconds delay to destroy the object, for some reason.

    But yes, in my opinion, if you are using this table method it would make things much easier for you to handle.

    Here's a little bit simpler version without the 'delay' timer, you could test it to see the difference for yourself.

    local SMOKERS = {}
    
    function isPlayerSmoking(player)
    	if SMOKERS[player] then return true end
    	return false
    end
    
    function startSmoking(player)
    	if isPlayerSmoking(player) then outputChatBox("You already have a cigarette.") return false end
      
    	SMOKERS[player] = {}
    	SMOKERS[player].cigarette = createObject(...) -- fill this with the correct data
      
    	setPedAnimation(player, "-", "-",false,false,false,false) -- set anim to smoking, you have to correct this
    	exports["pAttach"]:attach(SMOKERS[player].cigarette, player) -- some more stuff you have to do, fill the bone positions and more
    	
    	return true
    end
    
    function stopSmoking(player)
    	if not isPlayerSmoking(player) then outputChatBox("You don't have a lit cigarette.") return false end
    	
    	setPedAnimation(player)
    	exports["pAttach"]:detach(SMOKERS[player].cigarette, player)
    	destroyElement(SMOKERS[player].cigarette)
    	
    	SMOKERS[player] = nil  -- i don't think you need spam protection if you are using the table method, because the player can only start to smoke again if we set the table value of him to nil
    	
    	return true
    end

    Also, this way each player has their "own" cigarette. If you were using local variables for the object, and have 2 players, you couldn't destroy player 1's cigarette because the variable gets a new userdata value when you are creating the cigarette object for player 2.

    I hope I was understandable with my statements.?

    Note that I didn't test the script in game!

    • Thanks 1
  22. My guess is that when executing the command while doing the smoking, the local variables ('smoking', which is the cigarette object I suppose) gets overwritten by a new userdata, the same goes for the timer. Try using a table structure for things like this, that way you can make a difference between players, something like:

    local SMOKE = {
    	players = {},
    }
    
    function startSmoking(player)
    	if SMOKE.players[player] then return false end -- this is needed so you can't start smoking if you are already doing it
      
    	SMOKE.players[player] = {}
    	SMOKE.players[player].cigarette = createObject(...) -- fill this with the correct data
    	SMOKE.players[player].stopTimer = nil
      
    	setPedAnimation(player, "-", "-",false,false,false,false) -- set anim to smoking, you have to correct this
    	exports["pAttach"]:attach(SMOKE.players[player].cigarette, player) -- some more stuff you have to do, fill the bone positions and more
    	
    	return true
    end
    
    function stopSmoking(player)
    	if not SMOKE.players[player] then return false end -- check whether we should continue running the script, if the player is not smoking then don't execute furthermore
    	
    	if isTimer(SMOKE.players[player].stopTimer) then -- make sure to always kill the timer to avoid the glitch you were experiencing
    		killTimer(SMOKE.players[player].stopTimer)
    		SMOKE.players[player].stopTimer = nil
    	end
    	
    	setPedAnimation(player) -- remove the animation
      	
      	if not SMOKE.players[player].stopTimer then -- check if the timer exists, just for safety
    		SMOKE.players[player].stopTimer = setTimer(function() -- you want to always create the timer, so that's why it's outside the if isTimer statement
    			exports["pAttach"]:detach(SMOKE.players[player].cigarette, player) -- detaching before destroying the object, idk if it's necessary to include detach if you destroy the object, maybe you can delete this line
    			destroyElement(SMOKE.players[player].cigarette)
    			SMOKE.players[player] = nil
    		end, 2500, 1)
    	end
    	
    	return true
    end

    Also with this method you can check for the currently smoking players and make a function for it, like:

    function getSmokingPlayers()
    	return SMOKE.players
    end

    But this getSmokingPlayers() function is only necessarry if you would like to get the smoking players in other resources, so you could make an export for it.

    If you were going to loop it then the index will be the player and the value will be a table, you could make something like:

    function stopSmokingAllPlayers()
    	for player, data in pairs(getSmokingPlayers()) do -- NOTE THAT: getSmokingPlayers() is the same as SMOKE.players, you could also do that, only in the same script/resource tho
    		print(type(data.cigarette)) -- should output 'object' in the debugscript
    		stopSmoking(player)
    	end
    
    	return true
    end

    I have not tested this script, but I think it should work fine.

×
×
  • Create New...