Quantcast
Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
    1. Welcome to GTAForums!

    1. Red Dead Redemption 2

      1. PC
      2. Gameplay
      3. Missions
      4. Help & Support
    2. Red Dead Online

      1. Gameplay
      2. Find Lobbies & Outlaws
      3. Help & Support
      4. Frontier Pursuits
    1. Crews & Posses

      1. Recruitment
    2. Events

    1. GTA Online

      1. Diamond Casino & Resort
      2. DLC
      3. Find Lobbies & Players
      4. Guides & Strategies
      5. Vehicles
      6. Content Creator
      7. Help & Support
    2. Grand Theft Auto Series

    3. GTA 6

    4. GTA V

      1. PC
      2. Guides & Strategies
      3. Help & Support
    5. GTA IV

      1. Episodes from Liberty City
      2. Multiplayer
      3. Guides & Strategies
      4. Help & Support
      5. GTA IV Mods
    6. GTA Chinatown Wars

    7. GTA Vice City Stories

    8. GTA Liberty City Stories

    9. GTA San Andreas

      1. Guides & Strategies
      2. Help & Support
      3. GTA SA Mods
    10. GTA Vice City

      1. Guides & Strategies
      2. Help & Support
      3. GTA VC Mods
    11. GTA III

      1. Guides & Strategies
      2. Help & Support
      3. GTA III Mods
    12. Top Down Games

      1. GTA Advance
      2. GTA 2
      3. GTA
    13. Wiki

      1. Merchandising
    1. GTA Modding

      1. GTA V
      2. GTA IV
      3. GTA III, VC & SA
      4. Tutorials
    2. Mod Showroom

      1. Scripts & Plugins
      2. Maps
      3. Total Conversions
      4. Vehicles
      5. Textures
      6. Characters
      7. Tools
      8. Other
      9. Workshop
    3. Featured Mods

      1. DYOM
      2. OpenIV
      3. GTA: Underground
      4. GTA: Liberty City
      5. GTA: State of Liberty
    1. Red Dead Redemption

    2. Rockstar Games

    1. Off-Topic

      1. General Chat
      2. Gaming
      3. Technology
      4. Programming
      5. Movies & TV
      6. Music
      7. Sports
      8. Vehicles
    2. Expression

      1. Graphics / Visual Arts
      2. GFX Requests & Tutorials
      3. Writers' Discussion
      4. Debates & Discussion
    1. News

    2. Forum Support

    3. Site Suggestions

whorse

C++ ASI scripting in ScriptHookV

Recommended Posts

whorse

I'm gonna post some quick "tips and tricks" I use for GTA5 scripting in C++ that I've learned since I started making scripts this year. I realize that some of you may see this stuff as obvious or even inferior to your own methods, but at the same time, it seems like no one has ever actually gone out of their way to explain these kind of things; even stuff about functions and enums that are included as part of the ScriptHookV SDK are barely mentioned in the readmes and forum threads.

That said, I am by no means an expert programmer, but hopefully this will help some of you guys, because I do spend way too much time messing with this stuff.

 

First off, it can save you a lot of typing time to put these at the top of your script's .cpp file:

These allow you to call natives without using their namespace, like "DOES_ENTITY_EXIST(entity)" instead of "ENTITY::DOES_ENTITY_EXIST(entity)"

 

using namespace PLAYER; using namespace PED;using namespace ENTITY; using namespace VEHICLE;using namespace GAMEPLAY;using namespace WEAPON;using namespace AI; //add more namespaces as you see fit

 

 

 

Also, wrapping some of the most frequently used natives into short little functions can be helpful, like this:

 

//little one-line function called '$' to get hash keys:Hash $(char* getHashKey) {      return GAMEPLAY::GET_HASH_KEY(getHashKey); } //Example: call it like below to quickly convert a $TRING into a hash-keyPED::SET_RELATIONSHIP_BETWEEN_GROUPS(1, $("CIVMALE"), $("COUGAR"));//Rockstar seems to do something similar to this in the scripts, as everywhere there is a native that needs a hash-key, the argument used is simply the string surrounded by curly-braces and with a '$' at the front (unless this was something the decompilers added themselves - not sure how the script translation works)//quick function to get-coords-of-entity:Vector3 coordsOf(Entity entity) {      return ENTITY::GET_ENTITY_COORDS(entity, 1);}//quick function to get distance between 2 points:float distanceBetween(Vector3 A, Vector3 B) {     return GAMEPLAY::GET_DISTANCE_BETWEEN_COORDS(A.x, A.y, A.z, B.x, B.y, B.z, 1);} //example usage:if (distanceBetween(coordsOf(player), targetCoords) < 50)     //do something//quick "get random int in range 0-x" function simply called '_':int _(int randomIntInRange){      return GAMEPLAY::GET_RANDOM_INT_IN_RANGE(0, randomIntInRange); } //I call it _ because --to me, at least-- underscore implies "fill-in-the-blank" or "uncertain",// and a random value up to the number in parenthesis is definitely uncertain//draw random-colored line from coords A to coords B, using above _() function for getting the random red/green/blue valuesvoid drawLine(Vector3 A, Vector3 B) //must be looped because DRAW_LINE only works for one tick{     int R = _(255);     int G = _(255);     int B = _(255);     GRAPHICS::DRAW_LINE(A.x, A.y, A.z, B.x, B.y, B.z, R, G, B, 255); }//quick 1-out-of-x odds of something happening:bool oneIn(int Odds){      return (GAMEPLAY::GET_RANDOM_INT_IN_RANGE(1, Odds) == 1); } //example:if (oneIn(5))     WORLDPROBE::CALL_MOM(player, mom);

 

 

 

Another nifty thing is to use std::vector to hold/manage your entities, coords, groups, whatever:

 

std::vector<Ped> peds; //declare as a global variableif (canAddPed(somePed)) //however you find them..     peds.push_back(somePed); //adds somePed as the last ped inside "peds" vector//later, go through the peds vector in a for loop and use it to control all the peds inside:for (int i = 0; i < peds.size(); i++) {         //filter out the dead/bad ones based on whatever criteria you choose     if (!ENTITY::DOES_ENTITY_EXIST(peds.at(i)) || ENTITY::IS_ENTITY_DEAD(peds.at(i))){          peds.erase(peds.begin() + i); //erases ped at i from vector          continue; //skip to the next iteration of the for-loop     }     Ped ped1 = peds.at(i); //give the vector-ped a proper identifier     dehumanizePed(ped1); //do stuff like make ped1 fearless and crazy     makeFight(ped1, findRandomTargetFor(ped1)); //make ped1 fight a random opponent from the vector}//In this case, bool-canAddPed(ped), void-dehumanizePed(ped), void-makeFight(ped), and Ped-findRandomTargetFor(ped) are all hypothetical functions I just made up for this example.//But here is how a working findRandomTargetForPed would look anyway:Ped findRandomTargetFor(Ped ped){     int pool = int(peds.size());     Ped pedx = peds.at(GAMEPLAY::GET_RANDOM_INT_IN_RANGE(0, pool)); //pick a random ped from your collection in the peds vector     for (int i = 0; i < 4; i++) //check 4 times before giving up          if ((ped != pedx) && isGoodTarget(pedx)) //so ped doesnt fight itself, and so target is valid (hypothetical function)               return pedx; //found one! Returns a "good target" to fight          else //try again by getting a new random ped               pedx = peds.at(GAMEPLAY::GET_RANDOM_INT_IN_RANGE(0, pool));     return pedx; //returns ped from final attempt if a good one couldnt be found}//Last but not least, you can use a function like below to remove duplicates that have been accidentally added to your vector in multiple places (things of any type, not just peds)//First off, you must put this at the very top with your other #includes, or this function wont work:#include <algorithm> template <typename Type> //to let the function work with vectors holding all types of entities/coords/whatevervoid remove_duplicates(std::vector<Type> vec) {  std::sort(vec.begin(), vec.end());  vec.erase(unique(vec.begin(), vec.end()), vec.end());}

 

 

 

And here are some useful functions for registering virtual-keyboard key-presses:

 

bool switch_pressed(int key) {	return (GetAsyncKeyState(key) & 0x8000) != 0; //returns true while key is pressed down}//this one takes a bool pointer (needs one for each key) meant to keep track of whether or not the button was depressed on the last tickbool switch_just_released(int key, bool *keyPressed){	if ((GetAsyncKeyState(key) & 0x8000) != 0)		*keyPressed = true;	else if (*keyPressed == true)	{		*keyPressed = false;		return true;	}	return false;} // it changes the bool across the whole script.//Use it like this:bool minusKeyPressed; //<--declare at the top of your main methodif (switch_just_released(0x6D, &minusKeyPressed))     modActive = !modActive; //toggles mod on/off

 

 

 

And finally, how to get all peds or vehicles or objects currently in the game world into one array for each:

 

std::vector<Ped> peds;const int ARR_SIZE = 1024; //max size of array to hold all the pedsPed worldPeds[ARR_SIZE]; //array to hold all the pedsint numPedsInWorld = worldGetAllPeds(worldPeds, ARR_SIZE); //fills up worldPeds with peds, and returns the number of peds found as an int in numPedsInWorldfor (int i = 0; i < numPedsInWorld; i++){     if (canAddPed(worldPeds[i])) //just like in the vector example above...          peds.push_back(worldPeds[i]); //move the peds you want from the array to the vector, because the vector is far more versatile}//You can also use this in exactly the same way with objects and vehicles, using:int worldGetAllVehicles(int *arr, int arrSize);//andint worldGetAllObjects(int *arr, int arrSize);//these three functions are newly added to the ScriptHookV SDK by Alexander Blade, and their range from the player is only limited by the max range that the player can affect such entities naturally, unlike the very-similar-but-much-shorter-range PED::GET_PED_NEARBY_PEDS() and GET_PED_NEARBY_VEHICLES() natives. Open "pools.sln" (or its script.cpp file) from the "samples" folder in the scriptHookV SDK download if you want to see more.

 

 

 

That is all I got for the moment, but I'll probably post more stuff soon about ray-tracing, getting coordinates x-units away from the camera (centered on the crosshair), and using apply-force-to-entity.

Please feel free to post your own shortcuts, programming habits and tools that you find useful for GTA scripting! Or to show how something I'm doing could be done better! I just want to learn, anyway

Edited by whorse

Share this post


Link to post
Share on other sites
whorse

Here is an example of ray casting and some things you can do with it:

 

This function calculates the direction that the gameplay camera is looking based on the camera's rotation and coordinates. It takes an argument called "distance", which is used to calculate the coordinates of the point that is that x-distance away from the camera. So if you say getCoordFromCam(50), it will return coordinates of the point 50 meters in front of the center of the camera. And if you say getCoordsFromCam(1), it will return the same thing as a normal rotToDir() function that doesn't account for distance.

Vector3 getCoordsFromCam(float distance){	Vector3 camRot = CAM::GET_GAMEPLAY_CAM_ROT(2);	Vector3 camCoords = CAM::GET_GAMEPLAY_CAM_COORD();	float tZ = camRot.z * 0.0174532924F;	float tX = camRot.x * 0.0174532924F;	float absX = abs(cos(tX));	return {camCoords.x + (-sin(tZ) * absX) * distance, 0,                 camCoords.y + (cos(tZ) * absX) * distance, 0,                 camCoords.z + (sin(tX)) * distance, 0};        /*A Vector3 actually has six parts to it, not three: x, _paddingx, y, _paddingy, z, and _paddingz.         So you can declare a Vector3 inside curly braces like this if you know to separate the real XYZ values from the padding values (I just use 0 for them)*/}

How to use raycasting (use below one time for each ray-cast):

bool is_ped, is_vehicle, is_object; //declare these at the top, they will be used later to mark what kind of entity you hitVector3 camCoords = CAM::GET_GAMEPLAY_CAM_COORD();Vector3 farCoords = getCoordsFromCam(5000.0F); //get coords of point 5000m from your crosshairVector3 endCoords, surfaceNormal; BOOL hit; //placeholder variables that will be filled by _GET_RAYCAST_RESULTEntity entityHit = 0; //also will be filled by RAYCAST_RESULT, but you should clear it to 0/NULL/-1 each time so that you dont pick up the last entity you hit if the ray misses// THESE TWO NATIVES MUST BE CALLED RIGHT ONE AFTER ANOTHER IN ORDER TO WORK:// Cast ray from camera to farCoords:int ray = WORLDPROBE::_CAST_RAY_POINT_TO_POINT(camCoords.x, camCoords.y, camCoords.z, farCoords.x, farCoords.y, farCoords.z, -1, player, 7);// flag "-1" means it will intersect with anything. // "player" means it will ignore the player (player = PLAYER_PED_ID()). Not sure what 7 means.// You can change "player" to "0" if you want the ray to be able to hit yourself; or you can change it to another entity you want the ray to ignore.WORLDPROBE::_GET_RAYCAST_RESULT(ray, &hit, &endCoords, &surfaceNormal, &entityHit); // fills the bool "hit" with whether or not the ray was stopped before it reached farCoords// fills "endCoords" with the coords of where the ray was stopped on its way to farCoords// fills "surfaceNormal" with some kind of offset-coords of where the entity was hit relative to said entity// fills "entityHit" with the handle of the entity that was hit (if it was an entity)//If you want to use the entityHit for something, use code like below:if (hit && ENTITY::DOES_ENTITY_EXIST(entityHit)) {     active = true; //use an "active" bool if you need it for what you're doing     is_ped, is_vehicle, is_object = false; //reset these from what they were with the last entityHit     switch (ENTITY::GET_ENTITY_TYPE(entityHit)) { //GET_ENTITY_TYPE returns 1 if ped, 2 if veh and 3 if object (and 0 if none of those).          case 1:               is_ped = true;               break;          case 2:               is_vehicle = true;               break;          case 3:               is_object = true;               break;          default:               active = false; //something is wrong, the entity hit is not a ped/vehicle/object, so set inactive               break;     }     //the below native will retrieve the offset from the entity's main coords to the coords of where the ray hit the entity     Vector3 offset = ENTITY::GET_OFFSET_FROM_ENTITY_GIVEN_WORLD_COORDS(entityHit, endCoords.x, endCoords.y, endCoords.z);}

Now you can use the offset to calculate (at any time) the coordinates of the location on the entity where the ray originally hit it, no matter where the entity has moved since you first cast the ray. Then you can use this offset to attach a rope to that part of the entity, pick up the entity by that part, put particle effects on that part of the entity or whatever else you can come up with. The surfaceNormal coords give you something similar to this offset, but not quite

if (active) {     if (is_ped)          targCoords = PED::GET_PED_BONE_COORDS(entityHit, -1, offset.x, offset.y, offset.z); //gives the coords of what limb you hit     else          targCoords = ENTITY::GET_OFFSET_FROM_ENTITY_IN_WORLD_COORDS(entityHit, offset.x, offset.y, offset.z);}

Another thing you can do is warp yourself inside a vehicle hit, or teleport yourself to the endCoords (kinda like the portal gun mod):

if (warp_key_pressed()) { //some key was pressed and released     if (is_vehicle && VEHICLE::_IS_ANY_VEHICLE_SEAT_EMPTY(entityHit))          AI::TASK_WARP_PED_INTO_VEHICLE(player, entityHit, -2); //-2 means warp to "any available seat" in the vehicle hit     else          ENTITY::SET_ENTITY_COORDS(player, endCoords.x, endCoords.y, endCoords.z, 1, 1, 1, 1); //warp to endCoords}

Really though, You do not have to do anything with the entity at all. You can just use ray-casting to get the end-coordinates of where the ray hit, and that alone can be very useful. You can use the below code to launch an object from the player to the coords that the ray hit.

if (launch_key_pressed) {     Vector3 playerCoords = ENTITY::GET_ENTITY_COORDS(player, true);     //this applies just enough force to make the object reach endCoords from playerCoords     Vector3 forceToApply = {endCoords.x - playerCoords.x, 0,                             endCoords.y - playerCoords.y, 0,                             endCoords.z - playerCoords.z, 0};     Object some_projectile = OBJECT::CREATE_OBJECT($("some_object"), playerCoords.x, playerCoords.y, playerCoords.z, 1, 1, 1);     //remember, "$" just means "get hash key" here. Read the section about wrappers in the OP for an explanation     ENTITY::_APPLY_FORCE_TO_ENTITY2(some_projectile, 1, forceToApply.x, forceToApply.y, forceToApply.z, 1, 0, 1, 1);      if (ENTITY::HAS_ENTITY_COLLIDED_WITH_ANYTHING(some_projectile))          ENTITY::SET_ENTITY_AS_NO_LONGER_NEEDED(some_projectile);}

How to speed up mod development:

 

For quick testing and development, create a .SYMLINK (soft-link) file in your GTA V folder that links to your mod project's ASI file in the /release/ folder, where it gets compiled to every time. Having a symbolic link is the same thing as having the actual file there, as far as the computer is concerned. This way, every time you rebuild your mod, it will be automatically updated in your GTA game folder as well, without you having to actually move any files. This works so well that you actually cannot even rebuild the mod if your game is currently running with the symlink ASI file hooked in, as it will say that the ASI file you are trying to rebuild is currently in use.

 

CpMjFDV.png

screenshot from my GTAV game folder, showing .symlink files and ScriptHookV.dev

 

In order to un-hook the symlink so that you can rebuild, make sure you have a blank text file re-named to "ScriptHookV.dev" in your game directory. Putting it there allows you to just press CTRL+R at any time ingame to disable/enable the scripthook. You can use this to update your mod in-game; simply press CTRL+R, minimize the game, update and rebuild your mod, move+overwrite the new ASI file to the game directory (if you didn't create the symlink like mentioned above), and press CTRL+R again to enable the updated version of your mod.

Be sure to run the game in borderless-windowed mode if you're going to be minimizing frequently. To make a symlink, open the command prompt in Administrator mode and use the "mklink" command (with no special parameters). The first address is the location that you want the .symlink file to be created (so your GTA V game directory). The second address is the actual location of where your .ASI mod gets compiled to every time you rebuild. You must put the addresses in quotations if there are spaces in the folder names.

I08G356.jpg

Here is a screenshot I took of my command prompt after I successfully made a symlink, so I would remember how I did it. I run modded GTA from inside Sandboxie, so my modded game folder is inside D:\Sandbox\...\Games\GTAV rather than the real install location of D:\Games\GTAV

Edited by whorse

Share this post


Link to post
Share on other sites
gtaVmod

Here is a screenshot I took of my command prompt after I successfully made a symlink, so I would remember how I did it.

 

Don't you hear about FAR Manager? Hit Alt-F6 and you are ready.

 

And thanks for ray casting tut, that should be useful.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • 1 User Currently Viewing
    0 members, 0 Anonymous, 1 Guest

×
×
  • Create New...

Important Information

By using GTAForums.com, you agree to our Terms of Use and Privacy Policy.