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:
[spoiler=SCRIPTS]
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.
Methods:
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.
Properties:
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:
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.
[/spoiler]
[spoiler=2DFX]
Lights:
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.
[/spoiler]
[spoiler=OBJECTS]
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:
[spoiler=PHYSICS]
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).
Gravity:
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:
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)
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])
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:
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:
[spoiler=SCRIPTS]
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.
Methods:
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.
Properties:
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.
[/spoiler]
[spoiler=2DFX]
Lights:
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.
[/spoiler]
[spoiler=OBJECTS]
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.
[spoiler=PHYSICS]
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).
Gravity:
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