-
Posts
1,461 -
Joined
-
Last visited
-
Days Won
34
Everything posted by DiSaMe
-
You can also implement your own fires using createEffect. This is going to require more work because you'll have to remake the fire mechanics in Lua (spreading, dying out, setting peds/vehicles on fire, extinguishing using fire extinguisher/firetruck) but the upside is that you'll be in full control - such fires will not spread or die out outside control of your script, and you can make them fully synced between all players.
-
I'm not sure I can think of a scenario where avoiding such problem would be difficult. If you call triggerClientEvent, then there will be a (usually short) window of time when the state of the checkbox has changed on the client side but the signal hasn't arrived at the server yet. But as long as you pass the desired engine state along with the event data, the checkbox state and the actual engine state should be consistent in the end.
-
This can't be done as far as I know - each resource has its own global environment, which can only be modified from that resource. So you need to override the function in each resource.
-
The trolley poles look like custom objects attached to the vehicle using attachElements and their rotation is controlled with setElementAttachedOffsets. Alternatively, they can be parts of custom vehicle model, rotated using setVehicleComponentRotation. On every frame (in onClientPreRender event), you would find a particular point of the overhead wire and rotate the trolley poles towards that point, so it would look like they are connected all the time. Finding the point of the overhead wire depends on the format of the wire data you're using, but generally it involves finding a sphere-line intersection point. That is, if a sphere is positioned where the trolley pole is mounted and its radius equals the length of the trolley pole, and a line matches the overhead wire, then the sphere-line intersection point is the point that the trolley pole should be rotated towards. That requires some math. Calculating rotation from position is another part that requires math - if you have a position in world coordinate system, you can use getElementMatrix to get a matrix that you can then use to get the same position with respect to the vehicle. Then you can use math.atan2 to convert position to rotation angles (in radians), math.deg to convert angles from radians to degrees and pass the resulting values to setElementAttachedOffsets. And when the power is disconnected, setVehicleEngineState can be used to disable the engine.
-
The world objects aren't MTA elements so you don't have the same level of control over them that you have over objects that you add. What you can do is removing the original objects with removeWorldModel and adding your own identical ones.
-
But does the player respawn immediately, with your code, or does it not work either? I don't see grove and police variables being defined anywhere, so if this is the full script, it's understandable why it doesn't work. You either need to store the teams in these variables (for example, you can use getTeamFromName) so one of them would compare equal to team variable, or check the team in some other way, like instead of comparing the team element, you can retrieve its name and compare that, like this: local team = getPlayerTeam(source) local teamName = getTeamName(team) if teamName == "Grove" then -- ... end if teamName == "Police" then -- ... end But then it depends on what exactly the names of the teams are, because they have to be exactly the same to compare equal.
-
My suggestion: Use getCameraMatrix to get the x, y, z position of camera. Use getWorldFromScreenPosition on position of the cursor, with depth being the maximum distance you want to reach, to get the cursor's 3D position at that distance. Use processLineOfSight between camera and cursor coordinates to find the 3D position of the vehicle's surface point where the cursor is pointing. Transform that surface point from world coordinate space to vehicle's local coordinate space with the help of getElementMatrix. This one is a bit tricky since the element matrix allows transforming from local to world space easily, using matrix x vector multiplication, but doing the opposite (world to local) requires multiplying the inverse of that matrix by vector, and there doesn't seem to be a built-in function for calculating the inverse of a matrix in MTA. I could do that in Lua, since it's not very complicated, but still easy enough to make a mistake. Luckily, the vehicles have a scale of 1 and no shearing transformations (unless some glitch with Monster truck wheels occurs which does exactly that kind of crap to other vehicles), which makes the rotation matrix (the 3x3 part of element matrix) orthogonal, which in turn makes its inverse equal to transpose. This is my attempt to transform the coordinates: -- wx, wy, wz are the coordinates in world space local m = getElementMatrix(vehicle) local rx, ry, rz = wx-m[4][1], wy-m[4][2], wz = m[4][3] local lx = m[1][1]*rx + m[1][2]*ry + m[1][3]*rz local ly = m[2][1]*rx + m[2][2]*ry + m[2][3]*rz local lz = m[3][1]*rx + m[3][2]*ry + m[3][3]*rz Unless I screwed something up, lx, ly, lz should be wx, wy, wz relative to the vehicle. By transforming the cursor's 3D position like that, you can get the cursor's position with respect to the vehicle, which sounds like what you need.
-
If you need to execute the same code you have, but with a delay, then you can wrap it in a function that's inside a setTimer call, like this: function respawn() --[[ source variable won't exist after the event handler finishes so it won't be available inside the timer function, therefore we retain its value by creating a local variable "player" and assigning the value to it ]] local player = source setTimer(function() -- whatever code you need to execute end, 10000, 1) end addEventHandler("onPlayerWasted", getRootElement(), respawn) And because the source variable won't exist when the timer executes, you need to replace all occurrences with player.
-
My point was that you shouldn't do that much work on the client. The client-side code should only be used for playing the sound. Which in this case is the part with syncedranfominform function and onPlaySyncedRandInform event. Everything else should be done on the server. You should call setTimer from onPlayerVehicleEnter event to start the timer and call killTimer from onPlayerVehicleExit event to stop it - so every time you enter a vehicle, a timer will be created to execute every 2 minutes, with first execution occurring 2 minutes after entering. And when you exit the vehicle, that timer will be destroyed so it will no longer execute. An example: local timers = {} function startTimerOnEnter(vehicle, seat, jacked) if seat == 0 then -- only call it if entered the driver seat local timer = setTimer(whateverFunctionYouNeedToCall, 120000, 0) -- create a timer timers[source] = timer -- store the timer in the table under source, which is the player end end function stopTimerOnExit(vehicle, seat, jacker, forcedByScript) local timer = timers[source] -- retrieve the timer from the table if timer then -- if, for whatever reason, the timer wasn't previously created, we need to check it killTimer(timer) -- destroy the timer timers[source] = nil -- clear the field from the table since the timer no longer exists end end addEventHandler("onPlayerVehicleEnter", root, startTimerOnEnter) addEventHandler("onPlayerVehicleExit", root, stopTimerOnExit) There are going to be some additional cases where you need to clean up, like destroying the timer if the player quits while in the vehicle, as onPlayerVehicleExit won't be triggered. This is just the main part to show what it should look like.
-
MTA allows setting ped control states, camera angle and some other things, and that's all you need most of the time. Determining what control states and camera angle to set is up to you. If you want the ped to follow the player, the main part of that is setting the forwards control state and changing the camera angle to control the direction, regardless of how you determine that direction - so yes, you can do it using A* algorithm. And if a ped caused MTA to crash, that must have been a bug, because generally a script is not supposed to crash the game. But I don't remember that consistently happening to me, even though I used to spend a lot of time scripting peds.
-
This may be an out-of-bounds limitation. Some things don't work correctly outside (-3000; 3000) coordinate range. It's also possible that it's due to the number of pickups, although the streamer is supposed to keep the element count within game limits so you would always see the closest ones. Either way, it just seems that the pickups are buggy. When I had problems with them, I had to resort to using rotating objects instead.
-
I haven't found the cause by reading the script, but I'd recommend adding debug messages using outputChatBox or outputDebugString in various places to check things like variable values and whether the script execution enters if/then/end blocks.
-
There are functions like addVehicleSirens, setVehicleSirens and a few others that you can find by searching for "siren" in the function lists in the wiki. But I'm not familiar with them and I don't know how flexible they are. But you can also create your own emergency lights using createMarker and attach them to a vehicle using attachElements, then control it with setMarkerColor and setMarkerSize, using either setTimer or onClientRender/onClientPreRender with getTickCount for timing, which gives you more control than the built-in siren functionality does.
-
You're getting the text of the selected item of giveWindowGridlist and using that text as row index of gridlistPlayers1 to get the text from. Which won't work unless the grid list actually contains values like 0, 1, 2, ... I think you meant to get the player name the same way you get item? Like using guiGridListGetSelectedItem on gridlistPlayers1 and then passing the result to guiGridListGetItemText?
-
1) To make the sound play for other players, you should make all the controlling code server-side, and use triggerClientEvent on nearby players to trigger a client-side event which would call playSound3D. To reset the timer when the player exits the bus: instead of having a timer run all the time, call setTimer in onPlayerVehicleEnter to start the timer and killTimer in onPlayerVehicleExit to stop the timer. 2) You have a variable player for the first parameter in sendTexturesToPlayer, but onPlayerJoin has no parameters and uses source for the player who joined - so you need to pass source instead of player to triggerClientEvent. But even then, there's another problem: when the player joins, the resource hasn't loaded on the client side yet so it hasn't had time to call addEvent yet, which will cause triggerClientEvent to fail. Instead, you need to use onPlayerResourceStart for this. Because this event will be triggered on each resource start, you also need to check if its first parameter (loadedResource in the wiki) equals the return value of getThisResource.
-
From your description it's not really clear if you use all needed functions. You mentioned engineLoadTXD/DFF, but do you also call destroyElement? Those are elements so they remain in the memory until you destroy them. I tried making loading on demand once (I ran into some bugs so it didn't work at all, but that was in 2010), and realizing that destroyElement needs to be called is one of the things I've had in mind since then. I had no trouble figuring it out, but it felt unintuitive. On one hand, it's the same as destroying objects, vehicles, markers and other elements, you destroy them when they're no longer needed. On the other hand, unlike those elements, TXD and DFF are not physical entities on their own that exist inside GTA world, so the need to destroy them is easy to miss. It's like destroying objects/vehicles/markers is a part of gameplay (the element is visible and interacts with other elements and the game world, then it disappears), while destroying TXD/DFF is a part of memory management (nothing changes gameplay-wise, it's just needed for technical reasons).
-
I disagree. I needed to understand matrices, figuring out how they work (examples in getElementMatrix wiki page were an important part of that) was one of my major achievements because it enabled me to do calculations in 3D that I hadn't been able to before then. It's up to each person to decide if they need it or not. It was definitely not a waste of time for me so I wouldn't make such strong assumptions. As you have noticed, the 4th table represents the position. As for the others - they represent rotation and scale. More specifically, the 3x3 part of the matrix (that is, from matrix[1][1] to matrix[3][3]) is what you would call a "rotation matrix", which tells the directions of the object's coordinate system with respect to the world's coordinate system. matrix[1] tells where the object's X direction is pointing to, matrix[2] tells Y and matrix[3] tells Z. This allows you to do transformation of points from one coordinate system to another, using addition/subtraction and multiplication operations. For example, if you want to calculate the absolute coordinates of a point that's 0.5 units to the right (X) and 2 units to the front (Y) from the car, the calculations would go like this: absolute_position = 0.5*car_x_direction + 2*car_y_direction + car_position Or, by including the Z direction for completeness, we can also express it like this: absolute_position = 0.5*car_x_direction + 2*car_y_direction + 0*car_z_direction + car_position Basically the relative coordinates are (0.5, 2, 0), and by multiplying each component by vector of respective direction of the car and putting them together, you get the offset from the car in world's coordinate system, then by adding the car's position you get the absolute position. Written in plain Lua, with calculations done separately on each component, it would look like this: local absolute_position = { 0.5*matrix[1][1] + 2*matrix[2][1] + 0*matrix[3][1] + matrix[4][1], 0.5*matrix[1][2] + 2*matrix[2][2] + 0*matrix[3][2] + matrix[4][2], 0.5*matrix[1][3] + 2*matrix[2][3] + 0*matrix[3][3] + matrix[4][3] } That's the kind of calculation that happens inside matrix:transformPosition when using MTA's Matrix and Vector3 classes. It's up to you which one you use. Using MTA's classes helps you keep your code concise, while doing the calculations yourself allows you to reuse the math code anywhere else you use Lua, not just in MTA. As for your specific case - getElementBoundingBox returns the bounds relative to the element. So you can use them to choose a point in car's coordinate system and then transform it using the matrix. To elaborate a bit more regarding the matrix values in your example - notice how, if we round the small values to zero (they are non-zero because of some numerical inaccuracies to begin with), the 3x3 part looks like this: matrix[1] = {1, 0, 0} -- X axis (the right side), matches the world's X direction (east) matrix[2] = {0, 1, 0} -- Y axis (the front), matches the world's Y direction (north) matrix[3] = {0, 0, 1} -- Z axis (the top), matches the world's Z direction (top) Which demonstrates that since your car is not rotated, each of your car's X, Y and Z direction points to the respective direction in the world's coordinates. If you rotated the car clockwise by 90 degrees, the values would be different: matrix[1] = {0, -1, 0} -- X axis (the right side), matches the world's -Y direction (south) matrix[2] = {1, 0, 0} -- Y axis (the front), matches the world's X direction (east) matrix[3] = {0, 0, 1} -- Z axis (the top), matches the world's Z direction (top) I hope this explanation helps you in case you want to understand better how matrices work. And if you don't care what happens under the hood, then it's okay too, you can just use Matrix and Vector3 classes with transformPosition method.
-
If you want something to happen at a specific time, you can call getTime (for in-game time) or getRealTime (for real time) to retrieve the current time and use it to calculate the interval from the current moment to that specific time. Then call setTimer with that interval. To make a ped reappear after death, you would do all of that in setPedWasted event, destroying the old ped and creating a new one from within the function passed to setTimer.
-
There's no functionality for creating water emitters, but I just had an idea for a workaround: you can create a fire truck, make it invisible (using setElementAlpha) and collisionless (setElementCollisionsEnabled), create a ped (createPed), put it into the fire truck (warpPedIntoVehicle) and have it use the water cannon (setPedControlState with vehicle_fire control). If you want to attach it to a vehicle, you can attach the fire truck itself with attachElements. To move/rotate the water cannon around, you can change the position/rotation of the fire truck and/or use setVehicleTurretPosition. You may have to take some further measures to hide the fact that it's a separate vehicle, like disabling the damage (setVehicleDamageProof), preventing players from entering it (cancelEvent in onVehicleEnter event) and whatever else you can think of.
-
From what I understand, the time setting is a part of the map, used by mapmanager resource to change the time. Nothing to do with your script, it's just that they both call setTime, and each call overrides whatever the time was set to previously. If you want to override it from your script, you can try using mapmanager's event onGamemodeMapStart for that - so whenever a map starts and the mapmanager changes the time, your script would change it back. According to the code of mapmanager, the map settings are applied after onGamemodeMapStart is triggered so your overriding code should have a delay in order to set time after the map's time setting is applied, not before. setTimer with 0 milliseconds will probably do the thing. I'm not familiar with manager resources so I don't know if there's a cleaner way to do it.
-
If you take a closer look at the first line, the part that retrieves the name looks like this: getAccountName(getPlayerAccount(player)) While in the new line, it looks like this: getPlayerAccount(player) getPlayerAccount function returns an account, which is a separate MTA data type, so comparing it to a string will always evaluate to false. You need to use getAccountName like the original line does, but to avoid retrieving the name multiple times, it's better to store it in a variable: local name = getAccountName(getPlayerAccount(player)) if name == "SomeStaffName" or name == "SomeOtherStaffName" or name == "YetAnotherStaffName" then -- code to execute if the name matches end Even better, you can put the names as keys into the table and do the lookup: -- outside the function local StaffNames = { SomeStaffName = true, ["Some Other Staff Name"] = true, -- both forms are valid but this one allows spaces and other symbols that cannot be used with the other form YetAnotherStaffName = true } -- inside the function local name = getAccountName(getPlayerAccount(player)) if StaffNames[name] then -- code to execute if the name matches end But a script that bans someone for attempting to ban someone else looks like a script that may have unintended consequences.
-
Let me guess, you're calling triggerClientEvent from onPlayerJoin. But onPlayerJoin is triggered before the client scripts have been downloaded and started so it won't work. You have to call triggerClientEvent when you know the client-side script has started. In the older days, we would use triggerServerEvent from the client-side script to let the server know. Now there's onPlayerResourceStart event for that.
-
If you know for sure the problem is not in your code, then yes, resources that come with MTA could be another place to look. I would try replacing kickPlayer with a wrapper that calls debug.traceback and outputs it to the log in all running resources, so whenever a player gets kicked by a script, the server log would tell where the call came from. Then, if the traceback doesn't show up in the log, most likely it was done directly using /kick command. Which either means they somehow managed to gain access or there's actually a vulnerability in MTA server. I'm not ruling out any possibilities, but I assume user mistakes first.