Jump to content

DiSaMe

Helpers
  • Posts

    1,461
  • Joined

  • Last visited

  • Days Won

    34

Everything posted by DiSaMe

  1. The purpose of this forum section is to allow people to lead other people on the right track, which is exactly what I'm trying to do. The problem isn't that the number of arguments doesn't match, the problem is that in the end the operations get performed on values that are invalid for those operations. triggerServerEvent("serverAddVehicleUpgrade", getRootElement(), veh, tonumber(whl[1][1]) ) ? ? | | | | +-------------------------------+ | | | | | | | | +--------------------------------------+ | | | | | | | | +----------------------------------------------------+ | | | | | | | | +------------------------------------------------------+ | | | | V V V V function serverAddVehicleUpgrade(vehicle, upgrade, player, price) | | +----------------------------+ | | | | | +--------------------------+ | | | | V V | | if getPlayerMoney(player) > price then | | | | +--------------------------+ | | | | +--------------------------+ | | V V takePlayerMoney(player, price)
  2. I'm not familiar with HLSL syntax (never did any programming in HLSL), but based on https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-per-component-math#the-vector-type, it should look something like this: float x = VS.Position.x; float y = VS.Position.y; float z = VS.Position.z; If it's unpacking syntax that you want (assigning to multiple variables from a single vector at once), I don't think HLSL has it, but you could write a function with one in vector argument and three out scalar arguments for each component.
  3. You trigger the event like this: triggerServerEvent("serverAddVehicleUpgrade", getRootElement(), veh, tonumber(whl[1][1])) Arguments for handler function start at argument 3. Therefore, two arguments are passed, veh and tonumber(whl[1][1]). However, the handler function looks like this: function serverAddVehicleUpgrade(vehicle, upgrade, player, price) There are four arguments - more than were passed from the client. Only vehicle and upgrade get assigned. player and price default to nil.
  4. You could store the weapon elements in a table and then check in onClientPlayerDamage if attacker is in that table.
  5. You need to detect when the weapon hits a player using onClientPlayerDamage, check if the attacker (first parameter) is a weapon element that's not supposed to do damage and call cancelEvent.
  6. I was supposed to notice this thread a little sooner, but anyway. Started playing regularly and scripting in 2008, drifted away during 2013-2016, came back to this forum this year, not playing anymore, but visiting here regularly because of the great moments I had years ago.
  7. Did you try outputting the variable values and tracking down where the booleans get introduced? I assume you're using getScreenFromWorldPosition, assigning the return values to sx and sy and not checking those values. The thing is, if the point is off screen, that means there isn't a valid position on the screen for that point, and the function returns false (edgeTolerance argument can prevent this for some positions, but the function will still return false if the point is behind the camera). So false and nil get assigned to sx and sy respectively, and then you attempt to subtract from those values. That's where the error comes from. If you only draw one nametag in that event handler, it makes sense that it appears to work fine, because the error only occurs in cases where the nametag wouldn't be visible anyway.
  8. You need to get the screen resolution with guiGetScreenSize and use it to calculate the coordinates for that resolution. That page has examples for this particular purpose.
  9. @Dutchman101 already mentioned putting elements into a table, and I want to elaborate on that. These functions that return the lists of elements, they create a new table and fill it with elements every time they're called, which is inefficient to do repeatedly if we can reuse previously attained information. An example: local players = {} local function resourceStarted() -- when the script starts, we populate the table with players that are already playing for key, player in ipairs(getElementsByType("player")) do players[player] = true end end addEventHandler("onClientResourceStart", resourceRoot, resourceStarted) local function playerJoined() -- when a player joins, we add that player to the table players[source] = true end addEventHandler("onClientPlayerJoin", root, playerJoined) local function playerQuit() -- when a player quits, we remove that player from the table players[source] = nil end addEventHandler("onClientPlayerQuit", root, playerQuit) -- looping through all players: for player in pairs(players) do end That is, we only retrieve the elements with getElementsByType at the beginning, and keep track of subsequent changes by ourselves. If you only want to track the streamed in elements, you can add the needed arguments to getElementsByType like @IIYAMA showed and replace "onClientPlayerJoin" and "onClientPlayerQuit" with "onClientElementStreamIn" and "onClientElementStreamOut" respectively. But then again, you will also need to use getElementType to filter out elements of other types. Note that whereas getElementsByType returns a sequence (the key is a number, the value is an element), the table in my example is a set (the key is an element, the value is true). Looping is slightly different for that reason.
  10. DiSaMe

    Sporadic

    You need string.sub function, it returns a substring of the string. Using the same position for start and end, you get single characters. local value = "Text" local P1, P2, P3, P4 = string.sub(value, 1, 1), string.sub(value, 2, 2), string.sub(value, 3, 3), string.sub(value, 4, 4) Because the value is a string, you can call string functions on it as methods, using OOP syntax. This one is a little shorter and does the same thing as above: local value = "Text" local P1, P2, P3, P4 = value:sub(1, 1) value:sub(2, 2), value:sub(3, 3), value:sub(4, 4)
  11. I would never tell you that - to the contrary, bots were too underused in MTA last time I checked, and I don't know how much the situation has changed since then (it was long ago though). If you want to make bots that perform tasks, that's great. However, if you're not a scripter, it won't be that easy. MTA provides functions for low level control of bots, such as setPedControlState. The good thing about it is that you can make the bot do anything. The bad thing is that this "anything" has to be made from primitive actions. Making a bot drive from point A to B by setting acceleration, braking and steering controls takes a considerable amount of effort. The scripts that @IIYAMA posted links to, may help you. npc_hlc provides scripting functions for higher level controls and takes care of control states for you - so instead of calculating the angles and telling the bot "steer left/right and accelerate", you can tell it "drive to that point". npc_tseq is basically a GUI for npc_hlc. It allows you to create peds and assign tasks to them via GUI. However, even those scripts only have fairly primitive tasks. You can tell the bot to "drive straight to that point" or "drive along that line", but you can't just provide the destination position and have the bot drive there while following the roads. That is, no pathfinding. Meaning you have to put a bunch of "drive along line" tasks into one path. If you have a fixed set of source-destination pairs, you can retrieve the coordinates manually and build sequences out of them. But if you need the bot to travel between arbitrary points, then you have to do pathfinding, which is a problem if you have no scripting experience.
  12. That's a good point. It may look like a matter of taste, but there is a difference which may have big implications. If you call dbExec from within the command handler of "ck", right after kickPlayer, then the character only gets deleted when the player uses that command. If you call dbExec whenever the player gets kicked, the character gets deleted even if the player gets kicked by other means. The consequences may be very undesirable if there's some progress associated with a character. Suppose you have an AFK kicker script. The player stays idle for a few minutes, gets kicked - all progress they've made on that character is gone.
  13. Actually, you shouldn't have to manage the life of bots yourself. At least they shouldn't be invincible by default. I suspect where the problem might be. You create the bots at z position 0 or 2. z = 0 means sea level. You're creating them below the ground. And there is no water in that place, they just fall down. Which leads to this: When peds fall below z coordinate -100 or something similar, the game teleports them at some point on ped path. However, that's from the perspective of GTA SA. If you weren't the syncer when that happened, MTA server still sees the ped at the position where it was created. So you see the peds near the gym but they're invincible because the syncer controls the damage and the peds have no syncer. You may think you're the syncer because you see them close to you, but you're not. To become the syncer, you have to come closer to their actual position, where MTA server sees them. It may be confusing because the positions of unsynced peds aren't updated on clients to match the one that the server sees - unless the server calls setElementPosition, in which case the clients will see the ped teleporting to the specified position, but afterwards there's nothing that stops the ped from straying again. If anything, it's position of unsynced bots that you have to manage yourself, because the server doesn't have physics. For simplicity, you could make a timer on the server that, if the ped is unsynced (using getElementSyncer), gets the position of the ped and assigns that same position. That would keep teleporting the unsynced peds back to their actual positions.
  14. Thanks everyone, I'm happy to know my works are appreciated here. Yes, but this is one of the reasons why I regret leaving for so long. If I had left without having done anything important for people to remember me, it would have been easier. No one looking for me and wondering where the hell I disappeared. Now it's more like I created expectations for people and then vanished. I heard about it when my website went down, and I thought I should bring it back, maybe run a server myself, but I never did because "I'm not in the mood at this moment". Glad I didn't even have to in the end. Anyway, it seems that bone_attach is the most popular of my resources. Its main purpose wasn't even to manage the state (attaching/detaching), its main purpose was to provide rotation calculations because that's what I considered the hard part. I remember there was a time when I could calculate 2 angles from 2 positions but not 3 angles from 3 positions, meaning I could align the object to the bone but not rotate it along with the bone around the bone's axis. But... Yes, until recently. With getElementBoneRotation being introduced, that part is now obsolete. Regarding my resources in general, I made them because I wanted to benefit MTA, but I remember being somewhat annoyed by relative lack of action from the rest of the community. There were servers that I enjoyed playing in, some of them running simple but original gamemodes. But like anywhere else, for every good server there were lots of not-so-good ones. And then there were many people looking for some traffic script but hardly anyone trying to make one themselves. It felt so disappointing. Ped scripting capabilities were very underused, especially considering with them you can do far more than just peds randomly walking and driving around. I mean yes, it's expected that in any gaming community most people would be just players and not scripters. But for everything that's possible to do with MTA, that was a huge waste. Perhaps I was just overreacting because I was a little anxious, being unsure where my life was going while also worrying about MTA at the same time. But it all seems good enough now.
  15. I don't know why I even have to point this out since this is something I figured out automatically in my early days of MTA scripting. But it seems necessary because I see people using Lua tables but not taking advantage of their flexibility. Very brief overview of tables as arrays Anyway, Lua tables are often used as arrays - that is, data structures that store values under consecutive integer keys starting at 1 (0 in most other languages, but that's another story), making a sequence. Various functions that operate on tables, including those in table namespace, treat them as arrays. Take this example: local fruits = {} table.insert(fruits, "apple") table.insert(fruits, "banana") table.insert(fruits, "lemon") table.insert(fruits, "orange") It creates an empty table, then uses table.insert to insert values. Because table.insert treats the table as an array and because it inserts at the end of the sequence if position argument is omitted, we end up with values "apple", "banana", "lemon" and "orange" values under keys 1, 2, 3 and 4 respectively. Using index operator [], you can retrieve the associated value from the key: -- assuming standalone Lua, using print -- replace with outputChatBox or another output function if running in MTA print(fruits[1]) -- outputs: apple print(fruits[2]) -- outputs: banana print(fruits[3]) -- outputs: lemon print(fruits[4]) -- outputs: orange Tables as sets However, table keys don't have to be integers. You can use any value except nil and nan. Strings, other tables, MTA elements. Therefore, if the order of values is irrelevant and the values are unique, instead of inserting the value under some key, you can use that value itself as the key. And set the associated value to true: local fruits = {} fruits["apple"] = true fruits["banana"] = true fruits["lemon"] = true fruits["orange"] = true Now we have a table with keys "apple", "banana", "lemon" and "orange", while the associated value true is only used to indicate the presence of key and nothing else. That effectively makes the table a set, a collection of unique values with no particular order. Usage and comparison Insertion So we have two different ways to insert values: -- array table.insert(fruits, "apple") -- set fruits["apple"] = true This alone doesn't say much. Both operations are simple and take a roughly constant amount of time to execute. However, it makes other operations very different. Removal Because arrays require you to know the key to remove the value, you have to loop through the array to find it. In contrast, with sets you can just assign nil (because nil is the value for unassigned fields): -- array function removeFromArray(array, value) for i, v in ipairs(array) do if v == value then table.remove(array, i) end end end removeFromArray(fruits, "banana") -- set fruits["banana"] = nil Arrays are very inefficient for this if there is a large number of values, because the more values there are in total, the longer the removal of a single value will take - whereas removing from the set will take more or less the same. Checking if exists Checking for presence of a value is a lot like removal, you need to know where the value is, so arrays require looping. With sets, you just retrieve the associated value. If the checked value exists, the associated value will be true, otherwise it will be nil. -- array function existsInArray(array, value) for i, v in ipairs(array) do if v == value then return true end end return false end if existsInArray(fruits, "lemon") then print("found") else print("not found") end -- set if fruits["lemon"] then print("found") else print("not found") end Arrays are again inefficient in the same way. Looping Looping is roughly the same, but as far as I know, Lua tables are optimized to be used as arrays, so I guess looping may be a little faster for arrays than sets. I have never checked this myself. -- array for key, value in ipairs(fruits) do print("found value: "..value) end -- set for value in pairs(fruits) do print("found value: "..value) end Notice that ipairs is used for the array and pairs is used for the set. pairs will work for the array as well, but the order will be unspecified. ipairs is meant to be used on arrays, it starts at index 1 and increments until no value is found. Size retrieval Lua has length operator #, which returns the array length when used on tables. But there is no built-in operator or function to retrieve the total number of fields in a table, so it takes more to get the size of a set. -- array local count = #fruits print("fruit count: "..count) -- set function getSetSize(set) local count = 0 for value in pairs(set) do count = count+1 end return count end local count = getSetSize(fruits) print("fruit count: "..count) This makes size retrieval much more efficient for arrays than sets because the more values the set has, the longer it takes to loop through them. That's speaking of sets in their simplest form though. You can have a structure like this: local fruits = { count = 0, values = {} } And modify the count every time a value is inserted or removed. Then you can retrieve the size simply by reading fruits.count, which is efficient. But then other operations have to be altered too so we're not getting into this here. Checking if empty Checking if a collection is empty means checking if its size is 0. For arrays, nothing changes, but for sets it can be done efficiently. -- array if #fruits == 0 then print("array is empty") else print("array is not empty") end -- set if next(fruits) == nil then print("set is empty") else print("set is not empty") end next is a Lua function for traversing the table. It's used by pairs too, called once for each pair. But here we call next directly. Its first returned value is one of the keys in the table, or nil if no key is found. Conclusion For some of the most common operations, sets are both more efficient and simpler to operate on than arrays. In its simplest form, it loses against array in size retrieval, but you can keep track of size separately. When you want to store unique values and their order is unimportant, using tables as sets is probably the right choice. Syntax summary: tbl = {} -- insertion tbl[value] = true -- removal tbl[value] = nil -- checking if exists if tbl[value] then end -- looping for value in pairs(tbl) do end -- checking if empty if next(tbl) == nil then end
  16. It means you pass a boolean to ipairs, which means "result" is a boolean. According to dbPoll documentation, it returns false if there's something wrong, in which case it returns two more values indicating what went wrong. So if you change this: local result = dbPoll(qh, 0) to this: local result, error_code, msg = dbPoll(qh, 0) if result == false then outputDebugString("Error code: "..error_code) outputDebugString("Error message: "..msg) end it will display a more detailed message.
  17. The variable name in that function's parameter list is "targett", but you're checking "target", so it's a different variable.
  18. My mistake, I meant if isElementSyncer(source) then But now I looked at your code more closely and realized that this checking will prevent the code inside "if boss == "Mutant" then" block from working when the attacker is not the syncer, because that code requires the attacker to be the local player. So that would require more changes. Or you can just keep using "attacker == localPlayer" instead of isElementSyncer after all.
  19. Player damages won't duplicate, right, but you probably didn't notice my first reply. If you use isElementSyncer instead of checking "attacker == localPlayer", the damage will be synced by the player who syncs the ped, and there will be no duplications whether the attacker is a player or a ped.
  20. The damage will still be triggered multiple times if multiple players see the ped damaging another ped.
  21. This has the advantage that the damage event will necessarily be triggered if the attacker sees himself hitting the ped. However, the attacker is not necessarily a player, and the server side code indicates that peds are supposed to damage each other too. Therefore, this code prevents the event from triggering in cases where a ped damages another ped. It's better to check if you are the syncer of the ped: if isElementSyncer(ped) then end This way, the ped will be damaged if the player who syncs the ped sees him getting damaged. It's also more consistent this way because that's exactly what the syncer is for: controlling what happens to the synced element.
  22. There's a simpler solution: use function getAccountsByData. If no account has the value, the returned table will be empty. So this expression will evaluate to true if the number is already taken: #getAccountsByData("bank", bank_number) != 0 Apart from being easier, using getAccountsByData has the advantage of working correctly even if new bank numbers get added from outside your script - which may or may not be relevant in your case. And if you still decide to keep track of them yourself, the problem with the script is that in_table function loops through the whole list of bank numbers whenever you check if a number is registered. That's inefficient because the more registered numbers you have, the slower it gets. Instead of using bankNumbers table as an array (where numbers are stored as values in a sequence), you should use it as a set (where numbers are stored as keys). -- adding a bank number bankNumbers[bankNum] = true -- instead of: table.insert(bankNumbers, bankNum) -- checking for a bank number: while bankNumbers[bankNum] do -- instead of: while in_table(bankNumbers, bankNum) do
  23. By "remove only the 3 big cities", do you mean remove the cities but keep the rest of the map? Then I suggest you make a list of object model IDs and positions to be removed, then loop through that list and call removeWorldModel for each entry. But building the list itself is the hard part. I don't know if MTA map editor has any functionality that helps this. If it doesn't, then there are several things you can do. You can use some GTA SA map editor to pick out the objects. You can take the IDs and positions directly from .ipl files in GTA SA data. processLineOfSight returns model and position of the world object if it hits one. You can either use it to pick out the objects manually or make a loop that goes through a large area and collects the object data. processLineOfSight won't work on objects that are not loaded so you have to make sure you are close enough to objects you're trying to pick. Another potential problem with looping over an area is that you may miss small objects, and if you shoot lines of sight more densely, it will take longer to process the whole area. But since that only needs to be done once, it's okay as long as all objects are detected. Or you can loop over the area, then go through it manually to pick the remaining objects.
  24. No, the wiki says setPedControlState was made to work for the local player as well (in addition to peds, because previously it only worked for peds). It was setControlState that only worked for players, and because setPedControlState now works for both player and peds, setControlState has been deprecated on the client side. Anyway, if that still doesn't work, I guess the ped has not been streamed in yet when the function gets called. Another thing I just realized that needs to be taken into account is that when you hold the fire control, you only shoot missiles once. Not repeatedly. In order to keep shooting repeatedly, you need to press it repeatedly. The same holds true for peds, you need to keep switching the control state between true and false. A timer can do this: local hunter = createVehicle(425,x,y,z) local ped = createPed(0,x,y,z) warpPedIntoVehicle(ped, hunter) local function toggleFireControl() local old_state = getPedControlState(ped, "vehicle_fire") setPedControlState(ped, "vehicle_fire", not old_state) end setTimer(toggleFireControl, 100, 0)
  25. setPedAnalogControlState takes a third argument, which is a number between 0 (no effect) to 1 (full effect). For the control to take full effect, it should be like this: setPedAnalogControlState(ped, "vehicle_fire", 1) But I don't know if it's even valid to use analog control state for vehicle_fire, because you can either be shooting or not, there's no "in-between". For this reason, maybe it makes more sense to use setPedControlState: setPedControlState(ped, "vehicle_fire", true)
×
×
  • Create New...