[Guide] Undocumented VC:MP Findings

Started by GangstaRas, Aug 17, 2021, 04:33 PM

Previous topic - Next topic


I've realized that my VC:MP Scripting Book is proving difficult to get done in a timely fashion, so I will share as much of the information that I have in this post for others to utilize.

I would also encourage that those of you willing to share your experiences, reply below to make this post a general thread of findings and limitations that are either in the scripting or the game engine itself as it pertains to handling custom 3D models, 2DFX and so on.

All experimental data presented was observed on the 0.4.6 update for VC:MP. Future updates may change some or all of these limitations and/or nuances.

With that said, let's begin:

In my book on all of this, I would manually test every function/property stated to be available and even unavailable. This test was not exhaustive however, and so there is more data to be gathered. Most of these functions/properties have sufficient documentation and will not be mentioned here. Only undocumented behaviour about a function or property will stated here.

Player.AddSpeed( velocity ) <-- [Vector] Accelerates a player towards the velocity coordinate set. Player MUST have an initial Z axis acceleration, i.e. they must already be in the air for the command to work as intended. If they are on the ground, then two commands must be compounded onto each other. One to get the player off the ground (a Z axis propulsion), then the other to move the player to your desired coordinate. Failure to do this will result in no movement (if there isn't a Z axis coordinate change in your values) or the Z axis coordinate alone will play out from your values.

Player.SetDrunkLevel( visuals, handling ) <-- [Int] Sets player in a drunken state. The player does not exit the drunken state when they die. It will appear as if they did because you visually do not see the state after death, but upon subsequent use of the command a second time, you will notice no response. Therefore, currently, it is good practice to remove the drunken state on player death manually "Player.SetDrunkLevel(0,0)" to not have this bug present itself when you run the command again.

Player.AimDir <-- [Euler] Gets the angle of the player's aim from their last shot. AimDir does not use the X axis value (it is always between 0 and 180 degrees expressed in radians).

The Y axis value denotes the look up/down angle of the player in-game and has a range of -90 to 45 degrees expressed in radians.
The Z axis is used to denote the direction in the world the player is aiming with a range of -90 to 90 degrees. It has the following quadrant setup:

First Quadrant --> North to West = 0 to 90 degrees expressed in radians
Second Quadrant --> West to South = 90 to 0 degrees expressed in radians
Third Quadrant --> South to East = 0 to -90 degrees expressed in radians
Fourth Quadrant --> East to North = -90 to 0 degrees expressed in radians

Player.Angle <-- [Euler] Gets the current Z axis rotation of the player. It has a range of -180 degrees to 180 degrees expressed in radians. North is angle 0 degrees expressed in radians.

Up to 48 lights will work as intended within the 300 m radius of the player. Work as intended means corona, light effect and casted light shadow will be displayed.

You can however have up to 56 lights being displayed within the 300 m radius of the player, however, the light shadow will only work for the first 48 lights.

The maximum range of the light's effect is 25 m for the player/pedestrians and 50 m for vehicles regardless setting. A tapering point of 22 m and 45 m respectively was observed.

The shadow light texture has a maximum range of 45 m along the ground and will begin to taper at around 35 m. The shadow light texture seems to disappear if the light source is 15m+ above the ground surface.

The corona of the light can be seen for the full 300 m range with a taper at around 280 m, typical of any object.

Currently, VC:MP cannot fully distinguish a unique object by its ID or name once loaded in game memory. The only distinction its able to make is through the differing file sizes of the DFF file. Not even if you place the objects in separate 7z file packages will help. Let's say you have an object where only a minor texture change differentiates the two. If you load the object whether by XML or script, it will display whichever object comes first in the object.xml listing. The workaround for this I have found are one of two ways:
  • Export the DFF object using another plugin. This usually results in differing DFF sizes which will make things distinguishable by the game.
  • Modify the DFF in such a way that the file size will not be the same between objects, such as deleting/adding some vertices/polygons.

There is A LOT about this topic that's left to be figured out and so what I have to say is nowhere near exhaustive. These are just some of my current findings around the subject as of writing. This section is crucial for anyone wanting to create a simulated experience (e.g. simulator-type racer game-mode).

The gravity of GTA Vice City is about 20.044 m/s2. This was the deducted answer from experimenting with free-falling objects that are not subjected to air resistance (yes GTA Vice City has air resistance, so a normal free-fall won't get you this result, I'll tell you how when I get to that topic). This gravitational pull corresponds with the 0.008 default arbitrary value of the SetGravity function.

To achieve the real-world gravitational pull in-game, you must change this value to 0.003918. The SetGravity function for the most part is linear to achieve any gravitational pull you desire, but in truth, it is more accurate to say that it is probably pseudolinear. This may be due to floating-point limitations and/or frame rate limitations as the actual value needed to be 0.003914188785. The values highlighted in blue do not register; the red value appears to be the limit i.e. the least significant value that registers. While this is true, in practice I have found that 0.003915, 6 or 7 do not give any truly appreciable change in gravity.

And so, 0.003918 is used purely out of accuracy (it is +0.4% off the mark vs -0.11% off the mark with 0.003914).

Air Resistance:
I am not quite done with this topic. The physics here is a little beyond me to get things lined up to how it works in-game but I will explain what I did figure out nonetheless.

Air resistance is on every dynamic object of the game i.e. the player and vehicles. At first in doing the free-falling test, I was solely testing for the gravity. If there is no air resistance, no property of the object matters (weight, dimensions, shape etc.) when it comes to the time taken for an object to fall from a set distance. In testing out different objects, I noticed that they were falling at different rates, making my gravity testing useless at first. I then had my eureka moment and changed a vehicle's dimensions such that it was all 0 m or very close to it for each dimensional axes. In doing this I was able to capture the gravitational pull accurately from a 10,000 m free-fall (45.2 seconds at 1 g (0.003918) of gravity if you want to try it yourself). So the goal was now to figure out what are the settings of the air resistance to be able to accurately predict the falling rate of any object (and to even deduce what the player's weight is as well).

This is where I got stuck but I do have some notable findings:
  • Air resistance in-game is only dependent on the X and Z dimensional values. The Y value will not change the result of the free-fall time
  • The heavier the object, the longer it takes to hit terminal velocity and the faster that terminal velocity will be. Inversely, the lighter the object, the quicker it hits terminal velocity and the slower that terminal velocity will be

It is unlikely that the game is actively calculating what the velocity of an object should be as it is falling, so therefore, it must be grabbing onto known constant values that affect air resistance, those probably being the weight of the object, the gravity and the dimensions of the object and come up with a value beforehand. This information may then be applied to a formula that represents the curvature of the deceleration of the object as it hits terminal velocity due to air resistance.

It is likely that that formula is VT = tanh(g * t /VT)
  • VT - terminal velocity
  • tanh - hyperbolic tangent function
  • g - gravity
  • t - elapsed time

This formula however has absolutely no variable that makes each object's free-fall speed different. And so while this will generate the quickest curve suitable for in-game real-time processing, another formula for VT is needed. I hypothesized that that formula would be this:

VT = sqrt(2 * m * g / [p * A * Cd])
  • VT - terminal velocity
  • m - mass of the object
  • g - gravity
  • p - density of the air
  • A - cross sectional area of the object (probably X axis * Z axis / 2)
  • Cd - drag coefficient of the object

We could hypothesize that for the density of the air, the constant the game could be using is 1.2-1.225 kg/m3, this however still makes the drag coefficient a mystery. All calculations for the drag coefficient that I'm able to find revolves around transposing this second formula for terminal velocity, which means this hypothesis is starting to look shakey.

From data gathered through testing, there are some other relationships that I was able to establish that might provide some clues:
  • For every 8x increase in weight, there's a 2x increase in terminal velocity
  • For every 4x decrease in dimensional values, there's a 2x increase in terminal velocity



Player.Frozen <-- [Bool] Sets the player in a frozen state where they cannot move.

This property does not change upon death, and so while the player will have control to respawn, they will spawn frozen. Simply add "player.Frozen = false" into the onPlayerSpawn() event to prevent this issue in your scripts.

[$FF] and [$80] are text codes you can use to overwrite colours of text in the chat.
::Message("[#ff0000]test[$80]"+ msg);The [$80] portion will negate any colour changes in the msg variable.

Credits to vito for pointing these out to me.

Note: This needs more validation.

As some of you know, the minimap is based on a 8 x 8 grid array of images that are 128 x 128 pixels. It is unknown to me if this was inherently a GTA Vice City problem or if its VC:MP related, but when you try to get to the map's North and Western edge, the first column and row glitches out. It doesn't show with the normal game as easily because the edges are sea images.

What this causes is a push down of the image grid array of one row and one column of tile width (128 pixels) of the North and West coordinates. And so, in drawing a custom map or in utilizing other maps (GTA:SA for example), you may need to accommodate for this by offsetting the drawn map 128 pixels left and up from its proper alignment.

More investigation needed.


Quote from: GangstaRas on Aug 17, 2021, 04:33 PMI've realized that my VC:MP Scripting Book is proving difficult to get done in a timely fashion
You stole my idea REEEEE (/s)

Anyways how much pages does the book have?


This is something everybody must see and find easily.

Thank you for sharing!


Quote from: Athanatos on Sep 14, 2021, 12:39 PM
Quote from: GangstaRas on Aug 17, 2021, 04:33 PMI've realized that my VC:MP Scripting Book is proving difficult to get done in a timely fashion
You stole my idea REEEEE (/s)

Anyways how much pages does the book have?

Lol quite a bit actually, 56 pages to be exact.

I've covered:
  • Basic server files and executable setup
  • All events and server settings
  • Most of the functions, methods and properties (even the ones that were on wiki with no explanation or link) and my personal notes on how they work and what to do and not to do with them
  • Was starting up explaining the game files that define objects and the game world (IDE, IPL, TXD, COL, DAT etc.)
  • My object and physics findings thus far
  • Appendix with Car colour IDs, animation and animation group IDs. To include all reference IDs (so vehicle IDs, player skin IDs, announce text codes etc.)
Even the post Xmair made the other day on how to create plugins, that was originally done for my sake and the Scripting book but I took too long to  publish and he gave up on me and posted here lol


Creating plugins is a whole other topic. If I was you, I'd make an other book about creating plugins. (Scripting != programming)

How much is it going to cost ;D ? I want a free copy



Player.SecWorld <-- [Int] Contrary to what the VC:MP Wiki would've alluded to, SecWorld is not unique to a player, all players still have access to objects/pickups that exist in any world. For other players to see what you can see in SecWorld, they merely need to be set to the same SecWorld ID as the object/pickup.

The desync that is possible with SecWorld however can create a unique scenario. If let's say player A loaded a wall in SecWorld for himself, other players are not able to see nor interact with this wall can walk through it no problem but their shots will not register as the shots on player A's screen are interacting with the object that is loaded.

On the other side, player A cannot walk through the object if it has collision but they can actually shoot the other players dead regardless. It will look like he's just shooting the wall but the shots do register out to the other end as no object is on screen for the other player.

Player.FPS <-- [Float] The frame-rate issued by this property has a delayed change of 4 to 12 seconds. Therefore when sampling someone's frame-rate, it is recommended to do an averaging of the results, of which is very accurate to the averaging of real-time FPS sampling.

Player.Speed <-- [Vector] This property definitely doesn't work when the player is in a vehicle, and should not be mistaken as a general property to report the player's displacement

If you were to use Player.Speed to report velocity of a player on foot, then there is an approximate 22 - 24 ms (millisecond) of sampling time. This was done on Squirrel platform, Java scripts may report a different finding.