Skip to content

Instantly share code, notes, and snippets.

@tomatenbaumful
Created October 8, 2023 13:30
Show Gist options
  • Save tomatenbaumful/40b09e605363dc6f1fcb54a2815b5736 to your computer and use it in GitHub Desktop.
Save tomatenbaumful/40b09e605363dc6f1fcb54a2815b5736 to your computer and use it in GitHub Desktop.
Code for getting street nodes on alt:V
setInterval(() => {
getVehicleNode(); //use a interval of your choice to send fresh nodes to the server
}, 250);
const MAX_SPEED = 40; // Max speed of the vehicle
const MIN_ANGLE_FULL_SPEED = 50; // Minimum angle at full speed
const MAX_ANGLE_STANDING_STILL = 180; // Maximum angle when standing still
export function getVehicleNode() {
const streamedVehicles = alt.Vehicle.streamedIn.filter(x => x.type === 1); //only synced vehicles, ignore parked ones!
const vehPos = streamedVehicles.map(x => x.pos);
const vehicleCount = streamedVehicles.length;
// ConVar.PEDSYNC.VEHICLE_STREAM_IN_TARGET: 40 / ConVar.PEDSYNC.VEHICLE_SPAWN_MINNODES_PER_TICK:10
const freeSlots = Math.min(ConVar.PEDSYNC.VEHICLE_STREAM_IN_TARGET - vehicleCount, ConVar.PEDSYNC.VEHICLE_SPAWN_MINNODES_PER_TICK);
const foundNodes: [alt.Vector3, number, boolean][] = [];
for (let i = 0; i < freeSlots; i++) {
// Get the player's position and rotation
const pos = localPlayer.vehicle?.pos ?? localPlayer.pos;
// Get the player's vehicle speed
const playerSpeed = localPlayer.vehicle?.speed ?? 0;
// Calculate the angle based on the vehicle's speed
// Spawn the the vehicles further away in an smaller cone so that they arent uselessly spawned and instantly unstreamed
const speedFactor = Math.min(playerSpeed / MAX_SPEED, 1);
const angle = (MAX_ANGLE_STANDING_STILL - MIN_ANGLE_FULL_SPEED) * (1 - speedFactor) + MIN_ANGLE_FULL_SPEED;
const radian = (angle / 360) * Math.PI;
const randomAngle = Math.random() * 2 * radian;
const res = randomAngle - radian;
const spawnRadius = ConVar.PEDSYNC.VEHICLE_SPAWN_RADIUS + playerSpeed - (localPlayer?.vehicle ? 0 : 30);
const randPos = {
x: pos.x - Math.sin(localPlayer.rot.z + res) * spawnRadius,
y: pos.y + Math.cos(localPlayer.rot.z + res) * spawnRadius,
z: pos.z,
};
//You can controll the type of nodes gotten with this native this only gets nodes on "normal" streets
const [, nodePos, heading] = native.getClosestVehicleNodeWithHeading(randPos.x, randPos.y, randPos.z, new alt.Vector3(0), 0, 0, 3, 0);
const [, density, flags] = native.getVehicleNodeProperties(nodePos.x, nodePos.y, nodePos.z);
//Try to flip the node 65% of the times, When getting thew heading of nodes in the middle of the street you will get an heading towards the direction you got the node from
//This results in you mostly getting nodes in you driving direction on a lot of roads flipping the node tries to get the node from the opposite direction so you get an heading in the opposite direction
//Since there is no flag to know if the road is split(has an node for each direction) or only has an single node, this will get the same direction if it is split
//if you would just randomly rotate the heading you would spawn vehicles on the wrong lanes if the road only has one direction
//The percentage of 65% is choosen so that you get more traffic against you since traffic on your side blocks the other traffic, this makes it feel more natural although still not great
const [finalPos, finalHeading] = flipNodeDirection(nodePos, heading, Math.random() > 0.35);
//Get the position of the edge of the road so we know the road width
const bounds = native.getRoadBoundaryUsingHeading(finalPos.x, finalPos.y, finalPos.z, finalHeading, new alt.Vector3(0));
//Lerp the position between the node and the edge i use a normal distributed randomness so it primarily spawns them towards the middle
const lerpedPos = finalPos.lerp(bounds[1], randomNormal());
//ConVar.PEDSYNC.VEHICLE_SPAWN_MINDIST_TO_PLAYER: 150 > Prevent vehicles crashing/spawning into the player when driving
if (pos.distanceTo(lerpedPos) < ConVar.PEDSYNC.VEHICLE_SPAWN_MINDIST_TO_PLAYER) {
continue;
}
//getLobbySetting('traffic_density'): 25
//Reduce the distance between vehicles depending on road density resulting in more packed traffic in populated areas
//This can be vastly improved upon but works for our case
const minDistance = getLobbySetting('traffic_density') / ((density + 5) / 4);
const invalid = vehPos.some(x => x.distanceTo(nodePos) < minDistance);
if (invalid) {
continue;
}
foundNodes.push([lerpedPos, (finalHeading / 180) * Math.PI, isNodeHighway(flags)]);
vehPos.push(lerpedPos);
}
if (!foundNodes || foundNodes.length === 0) return;
//send the nodes to the server to spawn vehicles, you need to perform checks on the server so that an malicous user cant spam vehicles
//In our case unstreamed vehicles are deleted so we have less of an concern since theres only so many vehicles that can be streamed
alt.emitServerRaw('addVehSyncSpawn', foundNodes);
}
function flipNodeDirection(nodePosition: alt.Vector3, heading: number, flipDirection: boolean, moveAmount = 2) {
//moveForward moves the position towards the heading setting the node either before or behind it
const checkPos = utils.vector.moveForward(nodePosition, new alt.Vector3(0, 0, heading).toRadians(), flipDirection ? moveAmount : -moveAmount);
//now getting the node again should result in the direction being flipped if possible
const [, pos, heading2] = native.getNthClosestVehicleNodeWithHeading(checkPos.x, checkPos.y, checkPos.z, 1, new alt.Vector3(0), 0, 0, 0, 0, 0);
return [pos, heading2] as const;
}
//The flags are bits set for each node containing mostly needless information the 7th bit is set when the node is on an highway
function isNodeHighway(flags: number) {
// Return true if 7th bit is set
return (flags & 0b1000000) !== 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment