|
| 1 | +#include <a_samp> |
| 2 | +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 3 | + You can use mobile.inc to distinguish mobile |
| 4 | + players from PC and not to use this script |
| 5 | + with PC players. This include by default |
| 6 | + is avaliable only for servers that bought |
| 7 | + "Hosted" tab, but maybe there is free ones |
| 8 | + on the github. |
| 9 | + |
| 10 | +#include "mobile.inc" // Needs Pawn.RakNet plugin |
| 11 | +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 12 | +*/ |
| 13 | + |
| 14 | +/* |
| 15 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 16 | + Have 13 warnings from rotation include. |
| 17 | + If it is a big problem, then you can |
| 18 | + use your own functions for calculating |
| 19 | + plane's offset coordinates instead. |
| 20 | +*/ |
| 21 | +#include <rotation> |
| 22 | +#include <rotation_misc> |
| 23 | +#include <rotation_extra> |
| 24 | +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 25 | +#include <colandreas> // For collisions |
| 26 | + |
| 27 | +#include <Pawn.RakNet> // For bulletSync packets |
| 28 | +#define FILTERSCRIPT // For Pawn.RakNet :D |
| 29 | + |
| 30 | +#define DEBUG_MODE false // To have extra prints in log |
| 31 | +#define COLLISION_SEARCH_MIN_RADIUS 1 // Radius near plane |
| 32 | +#define COLLISION_SEARCH_RADIUS_STEP 1 // Step increasing radius |
| 33 | +#define COLLISION_SEARCH_MAX_RADIUS 5 // Radius far away from plane |
| 34 | +#define COLLISION_MINIMAL_DISTANCE 10 // Distance from plane to start search |
| 35 | +#define COLLISION_MAXIMAL_DISTANCE 200 // Maximal search distance |
| 36 | +#define COLLISION_DISTANCE_STEP 10 // Search for every 10 metres |
| 37 | +#define RUSTLER_DAMAGE_PLAYERS 10 // Plane's damage to players |
| 38 | +#define RUSTLER_DAMAGE_VEHICLES 10 // Damage to vehicles |
| 39 | +#define RUSTLER_DAMAGE_BODYPART 3 // Bodypart to damage to (not used) |
| 40 | +#define RUSTLER_MODEL_ID 476 // ModelID of Rustler plane |
| 41 | +#define RUSTLER_WEAPON_ID 31 // WeaponID for killing messages / damage sync |
| 42 | +#define NOTSET -1 |
| 43 | + |
| 44 | +#define BULLET_SYNC_ENABLE true // Set true if you want to send BulletSync packet when damaging player |
| 45 | +#define BULLET_SYNC_STREAM_ENABLE true // Set true if you want to send BulletSync to all players in victim's stream area |
| 46 | +#if BULLET_SYNC_ENABLE == true |
| 47 | +#define BULLET_SYNC 206 |
| 48 | +#define BULLET_HIT_TYPE_NONE 0 |
| 49 | +#define BULLET_HIT_TYPE_PLAYER 1 |
| 50 | +#define BULLET_HIT_TYPE_VEHICLE 2 |
| 51 | +#define BULLET_HIT_TYPE_OBJECT 3 |
| 52 | +#define BULLET_HIT_TYPE_PLAYER_OBJECT 4 |
| 53 | +#endif |
| 54 | + |
| 55 | +forward OnCheckFireUpdate(); |
| 56 | +forward OnRustlerFiring(playerid, vehicleid); |
| 57 | +forward GetNearestPlayer(Float:x, Float:y, Float:z, Float:radius, playerid); |
| 58 | + |
| 59 | +new firingTimer[MAX_PLAYERS]; |
| 60 | +new playerDeath[MAX_PLAYERS]; |
| 61 | + |
| 62 | + |
| 63 | +public OnFilterScriptInit() |
| 64 | +{ |
| 65 | + print("----------------------------\n|-[rustlerFireFix script by d7.KrEoL loaded]"); |
| 66 | + print("| 15.12.24"); |
| 67 | + print("|->Discord:https://discord.gg/QSKkNhZrTh"); |
| 68 | + print("|->VK:https://vk.com/d7kreol\n----------------------------"); |
| 69 | + |
| 70 | + for (new i = 0; i < MAX_PLAYERS; i++) |
| 71 | + { |
| 72 | + firingTimer[i] = NOTSET; |
| 73 | + playerDeath[i] = NOTSET; |
| 74 | + } |
| 75 | + if (!CA_Init()) |
| 76 | + printf("[rustlerFireFix]: cannot create raycast world. Script may not work well."); |
| 77 | + return 1; |
| 78 | +} |
| 79 | + |
| 80 | +public OnFilterScriptExit() |
| 81 | +{ |
| 82 | + print("\n--dlink Test Unloaded.\n------------------"); |
| 83 | + return 1; |
| 84 | +} |
| 85 | + |
| 86 | +public OnPlayerKeyStateChange(playerid, newkeys, oldkeys) |
| 87 | +{ |
| 88 | + if ((newkeys & KEY_ACTION) && !(oldkeys & KEY_ACTION) && IsPlayerInAnyVehicle(playerid)) |
| 89 | + { |
| 90 | + new vehicleid = GetPlayerVehicleID(playerid); |
| 91 | + if (GetVehicleModel(vehicleid) != RUSTLER_MODEL_ID) |
| 92 | + return 0; |
| 93 | + AddPlayerFiringTimer(playerid, vehicleid); |
| 94 | + } |
| 95 | + if ((oldkeys & KEY_ACTION) && !(newkeys & KEY_ACTION)) |
| 96 | + { |
| 97 | + if (firingTimer[playerid] != NOTSET) |
| 98 | + KillFiringTimer(playerid); |
| 99 | + } |
| 100 | + return 0; |
| 101 | +} |
| 102 | + |
| 103 | +public GetNearestPlayer(Float:x, Float:y, Float:z, Float:radius, playerid) |
| 104 | +{ |
| 105 | + new result = NOTSET; |
| 106 | + new Float:distance = radius; |
| 107 | + new Float:playerDistance = radius * 2; |
| 108 | + for(new i = 0; i < MAX_PLAYERS; i++) |
| 109 | + { |
| 110 | + if(!IsPlayerConnected(i) || !IsPlayerSpawned(i) || i == playerid) |
| 111 | + continue; |
| 112 | + playerDistance = GetPlayerDistanceFromPoint(i, x, y, z); |
| 113 | + if (playerDistance < distance) |
| 114 | + { |
| 115 | + result = i; |
| 116 | + distance = playerDistance; |
| 117 | + } |
| 118 | + } |
| 119 | + return int:result; |
| 120 | +} |
| 121 | + |
| 122 | +public OnRustlerFiring(playerid, vehicleid) |
| 123 | +{ |
| 124 | + if (!IsPlayerConnected(playerid) || !IsPlayerSpawned(playerid) || !IsPlayerInAnyVehicle(playerid)) |
| 125 | + { |
| 126 | + KillFiringTimer(playerid); |
| 127 | + return; |
| 128 | + } |
| 129 | + #if DEBUG_MODE == true |
| 130 | + printf("\n------------\n\n\nRustler fire from id: %d", playerid); |
| 131 | + #endif |
| 132 | + if (!IsPlayerInAnyVehicle(playerid)) |
| 133 | + KillFiringTimer(playerid); |
| 134 | + new Float:positionX, Float:positionY, Float:positionZ; |
| 135 | + GetVehiclePos(vehicleid, positionX, positionY, positionZ); |
| 136 | + new Float:offsetX, Float:offsetY, Float:offsetZ; |
| 137 | + new Float:castX, Float:castY, Float:castZ; |
| 138 | + new targetid = NOTSET; |
| 139 | + new searchRadius = COLLISION_SEARCH_MIN_RADIUS; |
| 140 | + |
| 141 | + for (new i = COLLISION_MINIMAL_DISTANCE; i < COLLISION_MAXIMAL_DISTANCE; i += COLLISION_DISTANCE_STEP) |
| 142 | + { |
| 143 | + GetVehicleRelativePos(vehicleid, 0, i, 0, offsetX, offsetY, offsetZ); |
| 144 | + targetid = GetNearestPlayer(offsetX, offsetY, offsetZ, searchRadius, playerid); |
| 145 | + |
| 146 | + if (searchRadius < COLLISION_SEARCH_MAX_RADIUS) |
| 147 | + searchRadius += COLLISION_SEARCH_RADIUS_STEP; |
| 148 | + if (targetid == NOTSET) |
| 149 | + continue; |
| 150 | + |
| 151 | + new Float:targetX, Float:targetY, Float:targetZ; |
| 152 | + if (!GetPlayerPos(targetid, targetX, targetY, targetZ)) |
| 153 | + { |
| 154 | + #if DEBUG_MODE == true |
| 155 | + printf("bad target %d", targetid); |
| 156 | + #endif |
| 157 | + |
| 158 | + break; |
| 159 | + } |
| 160 | + |
| 161 | + if (CA_RayCastLine(positionX, positionY, positionZ, targetX, targetY, targetZ, castX, castY, castZ) != 0) |
| 162 | + { |
| 163 | + #if DEBUG_MODE == true |
| 164 | + printf("!!![COLLISION FOUND: %.2f %.2f %.2f] (Player %d is behind the object)", castX, castY, castZ, targetid); |
| 165 | + #endif |
| 166 | + |
| 167 | + break; |
| 168 | + } |
| 169 | + // Uncomment if you're using mobile.inc to recognize mobile clients |
| 170 | + /*if (!IsPlayerMobile(targetid)) //#include "mobile.inc" above |
| 171 | + { |
| 172 | + printf("player %d is not mobile player", targetid); |
| 173 | + break; |
| 174 | + }*/ |
| 175 | + #if BULLET_SYNC_ENABLE == true |
| 176 | + new bulletData[PR_BulletSync]; //To send bulletData |
| 177 | + bulletData[PR_hitId] = targetid; |
| 178 | + bulletData[PR_origin][0] = positionX; |
| 179 | + bulletData[PR_origin][1] = positionY; |
| 180 | + bulletData[PR_origin][2] = positionZ; |
| 181 | + bulletData[PR_hitPos][0] = targetX; |
| 182 | + bulletData[PR_hitPos][1] = targetY; |
| 183 | + bulletData[PR_hitPos][2] = targetZ; |
| 184 | + bulletData[PR_offsets][0] = 0; |
| 185 | + bulletData[PR_offsets][1] = 0; |
| 186 | + bulletData[PR_offsets][2] = 0; |
| 187 | + bulletData[PR_weaponId] = RUSTLER_WEAPON_ID; |
| 188 | + #endif |
| 189 | + |
| 190 | + if (IsPlayerInAnyVehicle(targetid)) |
| 191 | + { |
| 192 | + new targetvehicleid = GetPlayerVehicleID(targetid); |
| 193 | + #if DEBUG_MODE == true |
| 194 | + printf("%d damaged vehicle: %d(player: %d)", playerid, targetvehicleid, targetid); |
| 195 | + #endif |
| 196 | + new Float:vehiclehealth = 1000; |
| 197 | + if (!GetVehicleHealth(targetvehicleid, vehiclehealth)) |
| 198 | + continue; |
| 199 | + SetVehicleHealth(targetvehicleid, vehiclehealth - RUSTLER_DAMAGE_VEHICLES); // Can rewrite with you GiveVehicleDamage logic here |
| 200 | + #if BULLET_SYNC_ENABLE == true |
| 201 | + bulletData[PR_hitType] = BULLET_HIT_TYPE_VEHICLE; |
| 202 | + SendBulletSync(playerid, targetvehicleid, bulletData); |
| 203 | + #endif |
| 204 | + |
| 205 | + break; |
| 206 | + } |
| 207 | + else |
| 208 | + { |
| 209 | + #if DEBUG_MODE == true |
| 210 | + printf("%d damaged player: %d", playerid, targetid); |
| 211 | + #endif |
| 212 | + new Float:playerhealth = 100; |
| 213 | + if (!GetPlayerHealth(targetid, playerhealth)) |
| 214 | + continue; |
| 215 | + GivePlayerDamage(targetid, playerid, RUSTLER_DAMAGE_PLAYERS); |
| 216 | + #if BULLET_SYNC_ENABLE == true |
| 217 | + bulletData[PR_hitType] = BULLET_HIT_TYPE_PLAYER; |
| 218 | + SendBulletSync(playerid, targetid, bulletData); |
| 219 | + #endif |
| 220 | + |
| 221 | + break; |
| 222 | + } |
| 223 | + } |
| 224 | + #if DEBUG_MODE == true |
| 225 | + printf("---------"); |
| 226 | + #endif |
| 227 | +} |
| 228 | + |
| 229 | +public OnPlayerDeath(playerid, killerid, reason) |
| 230 | +{ |
| 231 | + if (playerDeath[playerid] > NOTSET) |
| 232 | + { |
| 233 | + SendDeathMessage(playerDeath[playerid], playerid, RUSTLER_WEAPON_ID); |
| 234 | + playerDeath[playerid] = NOTSET; |
| 235 | + return 0; |
| 236 | + } |
| 237 | + return 1; |
| 238 | +} |
| 239 | + |
| 240 | +#if BULLET_SYNC_ENABLE == true |
| 241 | +stock SendBulletSync(playerid, victimid, data[PR_BulletSync]) |
| 242 | +{ |
| 243 | + new BitStream:bs = BS_New(); |
| 244 | + BS_WriteValue(bs, |
| 245 | + PR_UINT8, BULLET_SYNC, |
| 246 | + PR_UINT16, playerid |
| 247 | + ); |
| 248 | + BS_WriteBulletSync(bs, data); |
| 249 | + #if BULLET_SYNC_STREAM_ENABLE == true |
| 250 | + for (new i = 0; i < MAX_PLAYERS; i++) |
| 251 | + { |
| 252 | + if (IsPlayerStreamedIn(i, victimid)) |
| 253 | + PR_SendPacket(bs, i); |
| 254 | + } |
| 255 | + #else |
| 256 | + PR_SendPacket(bs, victimid); |
| 257 | + #endif |
| 258 | + BS_Delete(bs); |
| 259 | +} |
| 260 | +#endif |
| 261 | + |
| 262 | +stock AddPlayerFiringTimer(playerid, vehicleid) |
| 263 | +{ |
| 264 | + if (firingTimer[playerid] != NOTSET) |
| 265 | + KillFiringTimer(playerid); |
| 266 | + new FuncName[16] = "OnRustlerFiring"; |
| 267 | + firingTimer[playerid] = SetTimerEx(FuncName, 100, true, "ii", playerid, vehicleid); |
| 268 | +} |
| 269 | + |
| 270 | +stock GivePlayerDamage(playerid, damagerid, Float:damage) // Can rewrite with your GivePlayerDamage (OnFoot) logic here |
| 271 | +{ |
| 272 | + new Float:health, Float:armour; |
| 273 | + if (!GetPlayerHealth(playerid, health)) |
| 274 | + return; |
| 275 | + if (!GetPlayerArmour(playerid, armour)) |
| 276 | + return; |
| 277 | + if (armour > 0) |
| 278 | + { |
| 279 | + SetPlayerArmour(playerid, armour - damage); |
| 280 | + damage = damage - armour; |
| 281 | + if (damage <= 0) |
| 282 | + return; |
| 283 | + } |
| 284 | + if (health - damage <= 0) // Do "player kill" logic |
| 285 | + { |
| 286 | + //SendDeathMessage(damagerid, playerid, RUSTLER_WEAPON_ID); |
| 287 | + playerDeath[playerid] = damagerid; |
| 288 | + } |
| 289 | + SetPlayerHealth(playerid, health - damage); |
| 290 | + #if DEBUG_MODE == true |
| 291 | + printf("Damage has given to %d", playerid); |
| 292 | + #endif |
| 293 | +} |
| 294 | + |
| 295 | +stock KillFiringTimer(playerid) |
| 296 | +{ |
| 297 | + KillTimer(firingTimer[playerid]); |
| 298 | + firingTimer[playerid] = NOTSET; |
| 299 | +} |
| 300 | + |
| 301 | +stock IsPlayerSpawned(playerid) |
| 302 | +{ |
| 303 | + new pState = GetPlayerState(playerid); |
| 304 | + return 0 <= playerid <= MAX_PLAYERS && pState != PLAYER_STATE_NONE && pState != PLAYER_STATE_WASTED && pState != PLAYER_STATE_SPECTATING; |
| 305 | +} |
0 commit comments