driverfs.pwn 72 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122
  1. #if 0
  2. #pragma option -r
  3. #pragma option -d3
  4. #endif
  5. /* -----------------------------------------------------------------------------
  6. Smooth NPC Drivers - Have some Singleplayer-like NPCs on your server! - by NaS & AIped (c) 2013-2016
  7. Version: 1.2.5
  8. This is the FS-version of our NPC Drivers Script.
  9. To find bugs and improve its features we decided to release this compact version for testing and experimenting purposes.
  10. Please post any suggestions or buggy encounters to our thread in the official SA-MP Forums (http://forum.sa-mp.com/showthread.php?t=587634). Thanks!
  11. Note: The steep-hills-glitch (vehicles snapping around while driving up/down) is not possible to fix script-wise. This is probably caused by the "smooth" but unprecise movement of NPCs client-sided and the node distances (Z not always precise).
  12. ---------------
  13. > Credits
  14. AIped - Initiator of the 2013 version, help, ideas, scripting, ...
  15. Gamer_Z - RouteConnector Plugin, QuaternionStuff Plugin and great help with some math-problems.
  16. OrMisicL & ZiGGi - FCNPC Plugin
  17. Pottus and all other developers of ColAndreas
  18. kvann - Modern GPS Plugin
  19. Feel free to use and modify as you wish, but don't re-release this without our permission!
  20. // -----------------------------------------------------------------------------
  21. Latest Changenotes:
  22. [v1.2.5]
  23. - Added support for the Modern GPS Plugin by kvann/kristoisberg
  24. [v1.2.4]
  25. - Removed dependancy on QuaternionStuff
  26. - Now uses Nero_3D's rotations.inc to calculate Vehicle Rotations
  27. [v1.2.3]
  28. - Support for newest FCNPC Version (1.7.6)
  29. - NPCs can drive bikes (and also lean left/right in turns)
  30. - NPCs rotate on all axes now, depending on terrain
  31. - Taxi call will now be correctly aborted if the called Taxi gets killed by someone
  32. [v1.2.2]
  33. - Support for newest FCNPC Version (1.7.5)
  34. - Small optimizations
  35. [v1.2.1]
  36. - Support for newest FCNPC version (1.0.5)
  37. - Added Cops (count as civilains)
  38. Cops turn on the sirens sometimes to annoy their surroundings
  39. - Added Skin Arrays for different types (Civ, Cops, Taxis)
  40. - There is a very rough time measurement now when you choose your destination
  41. - Fixed stop'n'go (Using math instead of crappy distance guesses)
  42. - Better and more efficient movement streaming method (based on NPC Streaming..) - streamer not required anymore
  43. - Automatic start & end node calculation based for parking lots (1-connection nodes) + new GPS.dat with many
  44. fixes and a lot of new parking lots and improvements -> NPCs actually drive somewhere and wait a bit (will be extended)
  45. [v1.2]
  46. - General performance improvements
  47. - Using ColAndreas for more precise rotations
  48. . Using Streamer Plugin for Areas
  49. - Manually fixed hundreds of nodes
  50. - Reduction of path length (-> Less memory usage)
  51. - Streaming NPC-Movements (npcs will move very roughly (skip every 3 nodes) if no players are around -> saving quite a lot run-time calculations)
  52. - NPCs brake when someone (another npc) is in front of them
  53. [v1.1]
  54. - Improved random start- and destination node calculations (added PathNodes array), also improves NPC spreading around the world
  55. - Shortened paths on recalculations (performance improvement)
  56. - Improved speed calculations, speed changes over time by steepness and turning radiuses
  57. - Brakes when near to destination
  58. TO DO:
  59. - Add detection of players, make the NPCs Stop etc
  60. - Add more efficient code that detects other NPCs and Players in vicinity (current method is bad, but works for now)
  61. - Optimizations!
  62. *///----------------------------------------------------------------------------
  63. // -----------------------------------------------------------------------------
  64. #define SCRIPT_NAME "driverfs" // The Scriptname (by default driverfs.pwn)
  65. #define FILTERSCRIPT
  66. #include <a_samp>
  67. #undef MAX_PLAYERS
  68. #define MAX_PLAYERS (1000) // Redefine to your MAX_PLAYERS value to save some memory.
  69. #include <FCNPC>
  70. #include <GPS>
  71. #include <ColAndreas>
  72. #include <rotations>
  73. #include <zcmd>
  74. // ----------------------------------------------------------------------------- CONFIG
  75. #define NPC_NAMES "NPCDriver_%d" // %d will be replaced by the Drivers's ID (not NPC ID!)
  76. #define DEBUG_BUBBLE (false) // Lets NPCs show info via chat bubbles
  77. #define DEBUG_PRINTS (false) // Prints calculation times and warnings
  78. #define INFO_PRINTS (true) // Prints Driver Info every X seconds
  79. #define INFO_DELAY (300) // seconds
  80. #define MAP_ZONES (false) // Creates gang zones for every driver as replacement for a map marker (all npcs are always visible in ESC->Map)
  81. #define SEND_DEATH_MESSAGE (false) // Sends death message for killed NPC in chat (change in OnPlayerDeath to fit your Server)
  82. #define DRIVER_AMOUNT (450) // TOTAL NPC COUNT - Different driver types are part of the overall driver amount (300/20/20 = 300 NPCS of which are 20 Taxis, 20 Cops and 260 Normies)
  83. #define DRIVER_TAXIS (50)
  84. #define DRIVER_COPS (35)
  85. #define MAX_NODE_DIST (18.0)
  86. #define MIN_NODE_DIST (2.5) // Small changes here usually make a big difference. Do not go below 1.5.
  87. #define SIDE_DIST (2.02) // Distance to the center of the road, 2m is usually the best (unfortunately the actual data of road sizes hasn't been included with the GPS Plugin)
  88. #define SMOOTH_W_DATA (0.6) // Smoothing values, DATA - data weight, SMOOTH - smooth weight, should be bewtween 0.1 and 1.0
  89. #define SMOOTH_W_SMOOTH (0.2)
  90. #define SMOOTH_AMOUNT (20) // Amount of smoothing passes - was dynamic before but that made it hard to limit - medium smoothing is 15 (data 0.6, weight 0.2)
  91. #define MIN_SPEED (0.7)
  92. #define MAX_SPEED (2.2)
  93. #define DUTY_SPEED_BOOST (1.125) // Speed boost when on duty (Cops, Taxi)
  94. #define STEER_ANGLE (5.0)
  95. #define JAM_DIST (15.0) // Distance between 2 Drivers to make them slow down
  96. #define JAM_ANGLE (25) // INT! Max angle distance between 2 Drivers to make them slow down
  97. #define MAX_PATH_LEN (1350)
  98. #define MAX_TAXI_DIST (3000.0) // Max Distance to a taxi to respond
  99. #define TAXI_RANGE (35.0) // Max Range to closest nodes (Upon calling)
  100. #define TAXI_COOLDOWN (60) // seconds
  101. #define TAXI_TIMEOUT (40) // seconds
  102. #define ROUTE_MIN_DIST (900.0)
  103. #define ROUTE_MAX_DIST (2400.0)
  104. #define DRIVERS_ROUTE_ID (10000) // Starting routeid for path calculations - change if conflicts arise
  105. #define DIALOG_ID (10000) // Starting dialogid for dialogs - change if conflicts arise
  106. // ----------------------------------------------------------------------------- INTERNAL CONFIG/DEFINES
  107. #define DRIVER_TYPE_RANDOM (0)
  108. #define DRIVER_TYPE_TAXI (1)
  109. #define DRIVER_TYPE_COP (2)
  110. #define DRIVER_STATE_NONE (0)
  111. #define DRIVER_STATE_DRIVE (1)
  112. #define DRIVER_STATE_PAUSE (2)
  113. #define MAX_PATH_NODES (800) // Max Start & End Nodes
  114. #define TAXI_STATE_NONE (0)
  115. #define TAXI_STATE_DRIVE1 (1)
  116. #define TAXI_STATE_WAIT1 (2)
  117. #define TAXI_STATE_DRIVE2 (3)
  118. #define ZONES_NUM (90) // This is just for determining npc distances to each other via integers, lower value means bigger zones -> more npcs to check
  119. #define DID_TAXI (DIALOG_ID + 0)
  120. #pragma dynamic (50000) // Needs to be higher for longer paths/more npcs (for 900 - you can lower this or remove it if you use less than 400)!
  121. // ----------------------------------------------------------------------------- DEFINE WHERE NPCS SPAWN/GOTO - If you narrow it down to a small area lower the NPC Amount proportionally!
  122. #define MAP_ENABLE_LS (true) // Note that if you enable (for example) only LV and SF, the drivers will most likely drive from LV to SF and vice-versa as well.
  123. #define MAP_ENABLE_COUNTY (true)
  124. #define MAP_ENABLE_SF (true)
  125. #define MAP_ENABLE_LV (true)
  126. #define MAP_ENABLE_LV_DESERT (true)
  127. #if MAP_ENABLE_LS != true && MAP_ENABLE_SF != true && MAP_ENABLE_LV != true && MAP_ENABLE_LV_DESERT != true && MAP_ENABLE_COUNTY != true
  128. #error You must at least enable one area (MAP_ENABLE_* defines)
  129. #endif
  130. #if MAP_ENABLE_LS != true || MAP_ENABLE_SF != true || MAP_ENABLE_LV != true || MAP_ENABLE_LV_DESERT != true || MAP_ENABLE_COUNTY != true
  131. #if MAP_ENABLE_LS != true
  132. new Float:LSCoords[4][4] = // maxx, maxy, minx, miny - Created with GTA Zone Editor by zeppelin - Quite rough but works perfectly for nodes
  133. {
  134. {2992.19, -1093.75, 70.94, -2851.56},
  135. {2984.38, -875.00, 257.81, -1093.75},
  136. {1601.56, -687.50, 750.00, -875.00},
  137. {1601.56, -585.94, 882.81, -695.31}
  138. };
  139. #endif
  140. #if MAP_ENABLE_SF != true
  141. new Float:SFCoords[4][4] = // maxx, maxy, minx, miny
  142. {
  143. {-1421.88, 1562.50, -2898.44, -710.94},
  144. {-1171.88, 617.19, -1453.13, -695.31},
  145. {-1023.44, 54.69, -1195.31, -375.00},
  146. {-1867.19, -703.13, -2265.63, -1062.50}
  147. };
  148. #endif
  149. #if MAP_ENABLE_LV != true
  150. new Float:LVCoords[4] = // maxx, maxy, minx, miny
  151. {3015.63, 3031.25, 859.38, 625.00};
  152. #endif
  153. #if MAP_ENABLE_LV_DESERT != true
  154. new Float:LVDesertCoords[4][4] = // maxx, maxy, minx, miny
  155. {
  156. {859.38, 3000.00, -875.00, 523.44},
  157. {-875.00, 3007.81, -1320.31, 875.00},
  158. {-1304.69, 3015.63, -2117.19, 1671.88},
  159. {-2117.19, 3007.81, -2976.56, 2117.19} // Bayside!
  160. };
  161. #endif
  162. #if MAP_ENABLE_COUNTY != true
  163. new Float:CountyCoords[8][4] = // maxx, maxy, minx, miny
  164. {
  165. {46.88, -1085.94, -2945.31, -2968.75},
  166. {257.81, -695.31, -1898.44, -1085.94},
  167. {250.00, -375.00, -1187.50, -695.31},
  168. {250.00, 335.94, -1015.63, -375.00},
  169. {765.63, 445.31, 234.38, -929.69},
  170. {882.81, 453.13, 765.63, -695.31},
  171. {2968.75, 593.75, 882.81, -585.94},
  172. {2976.56, -570.31, 1593.75, -875.00}
  173. };
  174. #endif
  175. #endif
  176. // ----------------------------------------------------------------------------- Arrays, Vars etc
  177. enum E_DRIVERS
  178. {
  179. bool:nUsed,
  180. bool:nOnDuty,
  181. bool:nActive, // Active means a player is close (-> does all calculations, otherwise skips some nodes and doesnt process collision/rotation)
  182. nNPCID,
  183. nType,
  184. nState,
  185. nCurNode,
  186. MapNode:nLastStart,
  187. MapNode:nLastDest,
  188. Float:nDistance,
  189. Float:nSpeed,
  190. nSkinID,
  191. nVehicle,
  192. nVehicleModel,
  193. bool:nVehicleIsBike,
  194. Float:nVehicleLastLean,
  195. nPlayer,
  196. nLT, // Last Tick
  197. nCopStuffTick,
  198. nCalcFails,
  199. nZoneX,
  200. nZoneY,
  201. nDeathTick,
  202. bool:nResetVeh
  203. #if MAP_ZONES == true
  204. , nGangZone
  205. #endif
  206. };
  207. new Drivers[DRIVER_AMOUNT][E_DRIVERS];
  208. new NPCDriverID[MAX_PLAYERS] = {-1, ...};
  209. new Float:DriverPath[DRIVER_AMOUNT][MAX_PATH_LEN][3];
  210. new DriverPathLen[DRIVER_AMOUNT];
  211. new PlayerEnterDriver[MAX_PLAYERS] = {-1, ...};
  212. new Float:VehicleZOffsets[] = // Contains normal 4wheel vehicles, including Quad, Police Cars and Police Rancher, since the angle calculations also some bikes
  213. {
  214. 1.0982/*(400)*/,0.7849/*(401)*/,0.8371/*(402)*/,-1000.0/*(403)*/,0.7416/*(404)*/,0.8802/*(405)*/,-1000.0/*(406)*/,-1000.0/*(407)*/,-1000.0/*(408)*/,0.7901/*(409)*/,
  215. 0.6667/*(410)*/,-1000.0/*(411)*/,0.8450/*(412)*/,-1000.0/*(413)*/,-1000.0/*(414)*/,0.7754/*(415)*/,-1000.0/*(416)*/,-1000.0/*(417)*/,-1000.0/*(418)*/,0.8033/*(419)*/,
  216. 0.7864/*(420)*/,0.8883/*(421)*/,0.9969/*(422)*/,-1000.0/*(423)*/,0.7843/*(424)*/,-1000.0/*(425)*/,0.7490/*(426)*/,-1000.0/*(427)*/,1.1306/*(428)*/,0.6862/*(429)*/,
  217. -1000.0/*(430)*/,-1000.0/*(431)*/,-1000.0/*(432)*/,-1000.0/*(433)*/,-1000.0/*(434)*/,-1000.0/*(435)*/,0.7756/*(436)*/,-1000.0/*(437)*/,1.0092/*(438)*/,0.9020/*(439)*/,
  218. 1.1232/*(440)*/,-1000.0/*(441)*/,0.8379/*(442)*/,-1000.0/*(443)*/,-1000.0/*(444)*/,0.8806/*(445)*/,-1000.0/*(446)*/,-1000.0/*(447)*/,0.5835/*(448)*/,-1000.0/*(449)*/,
  219. -1000.0/*(450)*/,-1000.0/*(451)*/,-1000.0/*(452)*/,-1000.0/*(453)*/,-1000.0/*(454)*/,-1000.0/*(455)*/,-1000.0/*(456)*/,-1000.0/*(457)*/,0.8842/*(458)*/,-1000.0/*(459)*/,
  220. -1000.0/*(460)*/,0.5674/*(461)*/,0.5917/*(462)*/,0.5328/*(463)*/,-1000.0/*(464)*/,-1000.0/*(465)*/,0.7490/*(466)*/,0.7465/*(467)*/,-1000.0/*(468)*/,-1000.0/*(469)*/,
  221. -1000.0/*(470)*/,0.3005/*(471)*/,-1000.0/*(472)*/,-1000.0/*(473)*/,0.7364/*(474)*/,0.8077/*(475)*/,-1000.0/*(476)*/,-1000.0/*(477)*/,1.0010/*(478)*/,0.7994/*(479)*/,
  222. 0.7799/*(480)*/,-1000.0/*(481)*/,1.1209/*(482)*/,-1000.0/*(483)*/,-1000.0/*(484)*/,-1000.0/*(485)*/,-1000.0/*(486)*/,-1000.0/*(487)*/,-1000.0/*(488)*/,1.1498/*(489)*/,
  223. -1000.0/*(490)*/,0.7619/*(491)*/,0.7875/*(492)*/,-1000.0/*(493)*/,-1000.0/*(494)*/,1.3588/*(495)*/,0.7226/*(496)*/,-1000.0/*(497)*/,1.0726/*(498)*/,0.9988/*(499)*/,
  224. 1.1052/*(500)*/,-1000.0/*(501)*/,-1000.0/*(502)*/,-1000.0/*(503)*/,-1000.0/*(504)*/,1.1498/*(505)*/,0.7100/*(506)*/,0.8319/*(507)*/,1.3809/*(508)*/,-1000.0/*(509)*/,
  225. -1000.0/*(510)*/,-1000.0/*(511)*/,-1000.0/*(512)*/,-1000.0/*(513)*/,1.5913/*(514)*/,-1000.0/*(515)*/,0.8388/*(516)*/,0.8608/*(517)*/,0.6761/*(518)*/,-1000.0/*(519)*/,
  226. -1000.0/*(520)*/,0.5569/*(521)*/,0.5529/*(522)*/,0.5569/*(523)*/,-1000.0/*(524)*/,-1000.0/*(525)*/,0.7724/*(526)*/,0.7214/*(527)*/,-1000.0/*(528)*/,0.6374/*(529)*/,
  227. -1000.0/*(530)*/,-1000.0/*(531)*/,-1000.0/*(532)*/,0.7152/*(533)*/,0.7315/*(534)*/,0.7702/*(535)*/,0.7437/*(536)*/,-1000.0/*(537)*/,-1000.0/*(538)*/,-1000.0/*(539)*/,
  228. 0.8672/*(540)*/,-1000.0/*(541)*/,0.7501/*(542)*/,0.8309/*(543)*/,-1000.0/*(544)*/,0.8169/*(545)*/,0.7293/*(546)*/,0.7404/*(547)*/,-1000.0/*(548)*/,0.7048/*(549)*/,
  229. 0.8274/*(550)*/,0.8066/*(551)*/,-1000.0/*(552)*/,-1000.0/*(553)*/,1.0894/*(554)*/,0.6901/*(555)*/,-1000.0/*(556)*/,-1000.0/*(557)*/,0.6349/*(558)*/,0.6622/*(559)*/,
  230. 0.7105/*(560)*/,0.8190/*(561)*/,0.6632/*(562)*/,-1000.0/*(563)*/,-1000.0/*(564)*/,0.6317/*(565)*/,0.7889/*(566)*/,0.8733/*(567)*/,0.8720/*(568)*/,-1000.0/*(569)*/,
  231. -1000.0/*(570)*/,-1000.0/*(571)*/,-1000.0/*(572)*/,-1000.0/*(573)*/,-1000.0/*(574)*/,0.6107/*(575)*/,0.6128/*(576)*/,-1000.0/*(577)*/,-1000.0/*(578)*/,0.9359/*(579)*/,
  232. 0.8016/*(580)*/,-1000.0/*(581)*/,-1000.0/*(582)*/,-1000.0/*(583)*/,-1000.0/*(584)*/,0.5899/*(585)*/,-1000.0/*(586)*/,0.7336/*(587)*/,-1000.0/*(588)*/,0.6643/*(589)*/,
  233. -1000.0/*(590)*/,-1000.0/*(591)*/,-1000.0/*(592)*/,-1000.0/*(593)*/,-1000.0/*(594)*/,-1000.0/*(595)*/,0.7278/*(596)*/,0.7756/*(597)*/,0.7178/*(598)*/,1.1971/*(599)*/,
  234. 0.7171/*(600)*/,-1000.0/*(601)*/,0.8129/*(602)*/,0.8440/*(603)*/,-1000.0/*(604)*/,-1000.0/*(605)*/,-1000.0/*(606)*/,-1000.0/*(607)*/,-1000.0/*(608)*/,1.0727/*(609)*/,
  235. -1000.0/*(610)*/,-1000.0/*(611)*/
  236. };
  237. new DriverSkins[] = // Skin IDs for citizens, not sorted - no specific/story skins
  238. {
  239. 10, 101, 12, 13, 136, 14, 142, 143, 15, 151, 156, 168, 169,
  240. 17, 170, 180, 182, 183, 184, 263, 186, 185, 19, 216, 91, 206,
  241. 21, 22, 210, 214, 215, 220, 221, 225, 226, 222, 223, 227, 231,
  242. 228, 234, 76, 235, 236, 89, 88, 24, 218, 240, 25, 250, 261, 40,
  243. 41, 35, 37, 38, 44, 69, 43, 46, 9, 93, 39, 48, 47, 229, 58, 59,
  244. 60, 233, 72, 55, 94, 95, 98, 241, 242, 73, 83
  245. };
  246. new CopSkins[] = // Skin IDs for cops
  247. {
  248. 280, 281, 282, 283, 288, 306, 307, 310, 311
  249. };
  250. new TaxiSkins[] = // Skin IDs for taxi drivers - kind of randomly picked
  251. {
  252. 188, 20, 36, 262, 7, 56
  253. };
  254. new MapNode:PathNodes[MAX_PATH_NODES], PathNodesNum = 0; // Start & End Nodes for paths - generated at init - Always use newest GPS.dat to have enough 1-connection nodes & well spread NPCs!
  255. // I'll gather such nodes until we have about 1200 or even more.
  256. new IgnoredPathNodes[] = // Nodes to ignore for start/end nodes - mostly too many at one spot except stated otherwise - will NOT be ignored for regular driving
  257. {
  258. // Parking LS (too many)
  259. 9522,9513,9503,9511,9502,
  260. // Underground Parking LS
  261. 3845,3844,3852,3857,
  262. // Underground Parking SF - Some left over ;)
  263. 21198,21143,21148,21205,21149,21204,21206,21150,21172,21187,21112,21129,21115,21109,
  264. // Underground Parking LV
  265. 27155,27158,27165,27164,27156,
  266. // South LV Houses
  267. 19386,19388,19392,19393,19398,19277,19920,19984,19991,19985,19937,19768,
  268. 19977,19971,23291,19264,19343,23624,19263,19359,19350,19344,19349,19271,
  269. // More stupid LV nodes
  270. 24559,24555,24551,
  271. // Chiliad - Completely ignored
  272. 1895,2214,2232,
  273. // Country jump bridge (connection removed - two bad endpoints)
  274. 19517, 19516
  275. };
  276. new RandomVehicleList[212], VehicleListNum = 0;
  277. new Taxi[MAX_PLAYERS] = {-1, ...}; // -1 => no taxi called, everything else => driverid
  278. new TaxiState[MAX_PLAYERS] = {TAXI_STATE_NONE, ...};
  279. new LastTaxiInteraction[MAX_PLAYERS];
  280. new bool:InTaxiView[MAX_PLAYERS];
  281. enum E_DESTINATIONS
  282. {
  283. destName[36],
  284. Float:destX,
  285. Float:destY,
  286. Float:destZ
  287. };
  288. new gDestinationList[][E_DESTINATIONS] =
  289. {
  290. {"Los Santos Airport", 1643.2167, -2241.9209, 13.4900},
  291. {"Grove Street (LS)", 2500.9397, -1669.3757, 13.3438},
  292. {"Skatepark (LS)", 1923.5677,-1403.0310,13.2974},
  293. {"Mount Chiliad (LS)", -2250.8413,-1719.0470,480.0685},
  294. {"--------"},
  295. {"San Fierro Airport", -1424.2325, -291.3162, 14.1484},
  296. {"Jizzy's Club (SF)", -2625.6680, 1382.9760, 7.1820},
  297. {"Wang Cars (SF)", -1976.1716, 287.7719, 35.1719},
  298. {"Avispa Country Club (SF)", -2723.8706, -312.4941, 7.1875},
  299. {"Otto's Autos (SF)", -1628.4856, 1198.1681, 7.0391},
  300. {"--------"},
  301. {"Las Venturas Airport", 1682.3629, 1447.5713, 10.7722,},
  302. {"Four Dragons Casino (LV)", 2033.4517, 1009.9388, 10.8203},
  303. {"Caligula's Casino (LV)", 2158.8887, 1679.9889, 10.6953},
  304. {"Yellow Bell Golf Club (LV)", 1464.2926, 2773.0825, 10.6719},
  305. {"--------"},
  306. {"Blueberry (LS)", 200.8919, -144.7279, 1.5859},
  307. {"Palomino Creek (LS)", 2266.0808, 27.1097, 26.1645},
  308. {"Dillimore (LS)", 660.9581, -535.4933, 16.3359},
  309. {"Bayside (SF/LV)", -2466.1084, 2234.2334, 4.5125},
  310. {"Angel Pine (SF/LS)", -2119.8252, -2492.1013, 30.6250},
  311. {"El Quebrados (LV)", -1516.0896, 2540.1277, 55.6875},
  312. {"Las Barrancas (LV)", -745.9706, 1565.6580, 26.9609},
  313. {"Las Payasadas (LV)", -170.1701, 2693.7996, 62.4128},
  314. {"Bone County (LV)", 712.7426, 1920.7234, 5.5391},
  315. {"Verdant Meadows (LV)", 399.0638, 2484.6252, 16.484375}
  316. };
  317. new gDestinationDialogSTR[678];
  318. new MaxPathLen = 0;
  319. new rescueid = 0; // Current ID to check in the RescueTimer (only checks few entries (20) each time it calls to prevent long loops)
  320. new avgcalctimes[50] = {100, ...}, avgcalcidx;
  321. new avgticks[50] = {200, ...}, avgtickidx;
  322. new rescuetimer = -1;
  323. #if INFO_PRINTS == true
  324. new updtimer = -1;
  325. #endif
  326. new bool:Initialized = false;
  327. new InitialCalculations = 0, InitialCalculationStart;
  328. new NumRouteCalcs = 0, ExitPlayerID = -1; // Important for smooth FS unloading
  329. // -----------------------------------------------------------------------------
  330. forward Float:SmoothPath(const Float:path[][2], len = sizeof path);
  331. forward Float:OffsetPath(const Float:path[MAX_PATH_LEN][2], len, Float:d);
  332. forward Float:Get2DAngleOf3Points(Float:x1, Float:y1, Float:x2, Float:y2, Float:x3, Float:y3);
  333. forward Float:RayCastLineZ(Float:X, Float:Y, Float:Z, Float:dist);
  334. forward Float:floatangledistdir(Float:firstAngle, Float:secondAngle);
  335. forward MapNode:GetRandomStartEndPathNode();
  336. // -----------------------------------------------------------------------------
  337. public OnFilterScriptInit()
  338. {
  339. Drivers_Init();
  340. return 1;
  341. }
  342. public OnFilterScriptExit()
  343. {
  344. Drivers_Exit(1, 0);
  345. return 1;
  346. }
  347. public OnGameModeInit()
  348. {
  349. Drivers_Init();
  350. return 1;
  351. }
  352. public OnGameModeExit()
  353. {
  354. Drivers_Exit(1, 1);
  355. return 1;
  356. }
  357. // -----------------------------------------------------------------------------
  358. Drivers_Init()
  359. {
  360. if(Initialized) return 1;
  361. new name[MAX_PLAYER_NAME], cmp[MAX_PLAYER_NAME], len;
  362. strcat(cmp, NPC_NAMES);
  363. for(new i = 0; i < strlen(cmp); i ++) if(cmp[i] == '%') { cmp[i] = 0; break; }
  364. len = strlen(cmp);
  365. for(new i = 0; i < DRIVER_AMOUNT; i ++) Drivers[i][nNPCID] = -1;
  366. if(len >= 3)
  367. {
  368. for(new i = 0; i < MAX_PLAYERS; i ++)
  369. {
  370. if(!FCNPC_IsValid(i)) continue;
  371. GetPlayerName(i, name, MAX_PLAYER_NAME);
  372. if(strcmp(name, cmp, false, strlen(cmp)) == 0 && strlen(name) == len) FCNPC_Destroy(i);
  373. }
  374. }
  375. FCNPC_SetUpdateRate(30);
  376. CA_Init(); // You should uncomment this if you don't initialize ColAndreas before this FS gets loaded!
  377. format(gDestinationDialogSTR, sizeof(gDestinationDialogSTR), "");
  378. new minsize = sizeof(gDestinationList) * 9 + 1; // plus ("\n" + color code(8)) * number of entries
  379. for(new i = 0; i < sizeof(gDestinationList); i ++) minsize += strlen(gDestinationList[i][destName]);
  380. if(sizeof(gDestinationDialogSTR) < minsize) printf("[DRIVERS] Warning: Higher the size of gDestinationDialogSTR from %d to at least %d. Not all Destinations can be displayed.", sizeof(gDestinationDialogSTR), minsize);
  381. for(new i = 0; i < sizeof(gDestinationList); i ++)
  382. {
  383. if(strlen(gDestinationDialogSTR) >= sizeof(gDestinationDialogSTR) - strlen(gDestinationList[i][destName]) - 10) break; // In case gDestinationDialogSTR is too small, stop at the last Teleport that fits in to prevent cut-off
  384. if(gDestinationList[i][destName][0] == '-') format(gDestinationDialogSTR, sizeof(gDestinationDialogSTR), "%s{666666}%s\n", gDestinationDialogSTR, gDestinationList[i][destName]);
  385. else format(gDestinationDialogSTR, sizeof(gDestinationDialogSTR), "%s{999999}%s\n", gDestinationDialogSTR, gDestinationList[i][destName]);
  386. }
  387. if(rescuetimer != -1) KillTimer(rescuetimer);
  388. rescuetimer = SetTimer("RescueTimer", 500, 1);
  389. #if INFO_PRINTS == true
  390. if(updtimer != -1) KillTimer(updtimer);
  391. updtimer = SetTimer("PrintDriverUpdate", INFO_DELAY*1000, 1);
  392. #endif
  393. // ---------------- GENERATE START & END NODES
  394. new Float:X, Float:Y, Float:Z;
  395. for(new MapNode:i, max_node = GetHighestMapNodeID(); _:i <= max_node && PathNodesNum < MAX_PATH_NODES; i ++)
  396. {
  397. if(!IsValidMapNode(i)) continue;
  398. new count;
  399. GetMapNodeConnectionCount(i, count);
  400. if(count != 1) continue;
  401. new bool:ignore = false;
  402. for(new j = 0; j < sizeof(IgnoredPathNodes); j ++) if(i == MapNode:IgnoredPathNodes[j])
  403. {
  404. ignore = true;
  405. break;
  406. }
  407. if(ignore) continue;
  408. #if MAP_ENABLE_LS != true || MAP_ENABLE_SF != true || MAP_ENABLE_LV != true || MAP_ENABLE_LV_DESERT != true || MAP_ENABLE_COUNTY != true // Check for disabled zones (if any)
  409. GetMapNodePos(i, X, Y, Z);
  410. #if MAP_ENABLE_LS != true
  411. for(new j = 0; j < sizeof(LSCoords); j ++) if(X < LSCoords[j][0] && Y < LSCoords[j][1] && X > LSCoords[j][2] && Y > LSCoords[j][3])
  412. {
  413. ignore = true;
  414. break;
  415. }
  416. if(ignore) continue;
  417. #endif
  418. #if MAP_ENABLE_SF != true
  419. for(new j = 0; j < sizeof(SFCoords); j ++) if(X < SFCoords[j][0] && Y < SFCoords[j][1] && X > SFCoords[j][2] && Y > SFCoords[j][3])
  420. {
  421. ignore = true;
  422. break;
  423. }
  424. if(ignore) continue;
  425. #endif
  426. #if MAP_ENABLE_LV != true
  427. if(X < LVCoords[0] && Y < LVCoords[1] && X > LVCoords[2] && Y > LVCoords[3]) continue;
  428. #endif
  429. #if MAP_ENABLE_LV_DESERT != true
  430. for(new j = 0; j < sizeof(LVDesertCoords); j ++) if(X < LVDesertCoords[j][0] && Y < LVDesertCoords[j][1] && X > LVDesertCoords[j][2] && Y > LVDesertCoords[j][3])
  431. {
  432. ignore = true;
  433. break;
  434. }
  435. if(ignore) continue;
  436. #endif
  437. #if MAP_ENABLE_COUNTY != true
  438. for(new j = 0; j < sizeof(CountyCoords); j ++) if(X < CountyCoords[j][0] && Y < CountyCoords[j][1] && X > CountyCoords[j][2] && Y > CountyCoords[j][3])
  439. {
  440. ignore = true;
  441. break;
  442. }
  443. if(ignore) continue;
  444. #endif
  445. #endif
  446. PathNodes[PathNodesNum] = i;
  447. PathNodesNum ++;
  448. }
  449. if(PathNodesNum < 30) print("DRIVER WARNING: Insufficient amount of parking lots - Use newest GPS.dat or enable more areas!");
  450. // ---------------- CONNECT NPCS & stuff
  451. for(new i = 0; i <= 211; i ++) // Generate a list of vehicles to use
  452. {
  453. if(VehicleZOffsets[i] < -950.0 || i == 20 || i == 38) continue;
  454. RandomVehicleList[VehicleListNum] = i+400;
  455. VehicleListNum ++;
  456. }
  457. new maxnpc = GetServerVarAsInt("maxnpc"), othernpcs = 0;
  458. for(new i = 0; i < MAX_PLAYERS; i ++)
  459. {
  460. if(IsPlayerNPC(i)) othernpcs ++;
  461. if(IsPlayerConnected(i) && InTaxiView[i]) SetCameraBehindPlayer(i);
  462. Taxi[i] = -1;
  463. LastTaxiInteraction[i] = GetTickCount() - TAXI_COOLDOWN*1000;
  464. NPCDriverID[i] = -1;
  465. }
  466. Initialized = true;
  467. InitialCalculationStart = GetTickCount();
  468. for(new i = 0; i < DRIVER_AMOUNT; i ++) Drivers[i][nNPCID] = -1;
  469. new npcname[MAX_PLAYER_NAME];
  470. for(new i = 0; i < DRIVER_AMOUNT; i ++)
  471. {
  472. if(i >= maxnpc - othernpcs)
  473. {
  474. printf("[DRIVERS] Error: maxnpc exceeded, current limit for this script: %d.", maxnpc-othernpcs);
  475. break;
  476. }
  477. new MapNode:startnode = GetRandomStartEndPathNode(), MapNode:endnode, Float:dist;
  478. do
  479. {
  480. endnode = GetRandomStartEndPathNode();
  481. GetDistanceBetweenMapNodes(startnode, endnode, dist);
  482. }
  483. while(dist < ROUTE_MIN_DIST || dist > ROUTE_MAX_DIST);
  484. GetMapNodePos(startnode, X, Y, Z);
  485. new vmodel, colors[2], skinid;
  486. if(i < DRIVER_TAXIS)
  487. {
  488. Drivers[i][nType] = DRIVER_TYPE_TAXI;
  489. vmodel = (random(2) ? 420 : 438);
  490. skinid = TaxiSkins[random(sizeof(TaxiSkins))];
  491. colors = {-1, -1};
  492. }
  493. else if(i < DRIVER_COPS + DRIVER_TAXIS)
  494. {
  495. Drivers[i][nType] = DRIVER_TYPE_COP;
  496. switch(random(5))
  497. {
  498. case 0: vmodel = 596;
  499. case 1: vmodel = 597;
  500. case 2: vmodel = 598;
  501. case 3: vmodel = 599;
  502. case 4: vmodel = 523; // HPV
  503. }
  504. skinid = CopSkins[random(sizeof(CopSkins))];
  505. colors = {-1, -1};
  506. }
  507. else
  508. {
  509. Drivers[i][nType] = DRIVER_TYPE_RANDOM;
  510. do
  511. {
  512. vmodel = RandomVehicleList[random(VehicleListNum)];
  513. } while(vmodel == 596 || vmodel == 597 || vmodel == 598 || vmodel == 599 || vmodel == 420 || vmodel == 438 || vmodel == 523);
  514. skinid = DriverSkins[random(sizeof(DriverSkins))];
  515. colors[0] = random(127), colors[1] = random(127);
  516. }
  517. format(npcname, MAX_PLAYER_NAME, NPC_NAMES, i);
  518. Drivers[i][nVehicle] = CreateVehicle(vmodel, X, Y, Z + 100000.0, 0.0, colors[0], colors[1], 120000); // Spawn somewhere noone ever will get! This prevents FCNPC's spawn flickering (vehicles showing up at spawn coords between movements for < 1ms (annoying when driving into them just then!))
  519. if(!FCNPC_IsValid(Drivers[i][nNPCID])) Drivers[i][nNPCID] = FCNPC_Create(npcname);
  520. if(!FCNPC_IsValid(Drivers[i][nNPCID]))
  521. {
  522. printf("[DRIVERS] Error: Failed creating NPC (Driver ID %d). Aborted!", i);
  523. DestroyVehicle(Drivers[i][nVehicle]);
  524. break;
  525. }
  526. FCNPC_Spawn(Drivers[i][nNPCID], skinid, X, Y, Z + 1.0);
  527. FCNPC_PutInVehicle(Drivers[i][nNPCID], Drivers[i][nVehicle], 0);
  528. FCNPC_SetPosition(Drivers[i][nNPCID], X, Y, Z + VehicleZOffsets[vmodel - 400]);
  529. NPCDriverID[Drivers[i][nNPCID]] = i;
  530. //FCNPC_SetInvulnerable(Drivers[i][nNPCID], true);
  531. //FCNPC_SetHealth(Drivers[i][nNPCID], 100.0);
  532. Drivers[i][nOnDuty] = false;
  533. Drivers[i][nPlayer] = -1;
  534. Drivers[i][nCurNode] = 0;
  535. Drivers[i][nState] = DRIVER_STATE_NONE;
  536. Drivers[i][nSkinID] = skinid;
  537. Drivers[i][nVehicleModel] = vmodel;
  538. Drivers[i][nVehicleLastLean] = 0.0;
  539. Drivers[i][nUsed] = true;
  540. Drivers[i][nLT] = GetTickCount();
  541. Drivers[i][nLastStart] = startnode;
  542. Drivers[i][nLastDest] = endnode;
  543. #if MAP_ZONES == true
  544. Drivers[i][nGangZone] = -1;
  545. #endif
  546. switch(vmodel)
  547. {
  548. case 448, 461, 462, 463, 521, 522, 523: Drivers[i][nVehicleIsBike] = true;
  549. default: Drivers[i][nVehicleIsBike] = false;
  550. }
  551. pubCalculatePath(i, startnode, endnode);
  552. }
  553. printf("\n\n Total Drivers: %d, Random Drivers: %d, Taxi Drivers: %d, Cops: %d\n maxnpc: %d, Other NPCs: %d\n Number of random nodes: %d/%d\n\n", DRIVER_AMOUNT, (DRIVER_AMOUNT - DRIVER_TAXIS - DRIVER_COPS), DRIVER_TAXIS, DRIVER_COPS, maxnpc, othernpcs, PathNodesNum, MAX_PATH_NODES);
  554. print(" Initial Calculations started, please wait a moment to finish ...");
  555. return 1;
  556. }
  557. forward Drivers_Exit(fastunload, gmx);
  558. public Drivers_Exit(fastunload, gmx)
  559. {
  560. if(!Initialized && fastunload == 1) return 1;
  561. for(new i = 0; i < MAX_PLAYERS; i ++)
  562. {
  563. if(IsPlayerConnected(i) && !IsPlayerNPC(i) && InTaxiView[i]) SetCameraBehindPlayer(i);
  564. Taxi[i] = -1;
  565. NPCDriverID[i] = -1;
  566. }
  567. if(rescuetimer != -1) KillTimer(rescuetimer);
  568. rescuetimer = -1;
  569. #if INFO_PRINTS == true
  570. if(updtimer != -1) KillTimer(updtimer);
  571. updtimer = -1;
  572. #endif
  573. if(fastunload == 0) // This prevents crashes when exiting the FS by destroying NPCs in seperate calls (might be fixed in new version).
  574. {
  575. print("[DRIVERS] Warning: Unloading Driver FS ...");
  576. SetTimerEx("Drivers_DestroyID", 1000, 0, "i", 0);
  577. }
  578. else
  579. {
  580. for(new i = 0; i < DRIVER_AMOUNT; i ++)
  581. {
  582. if(!Drivers[i][nUsed]) continue;
  583. Drivers[i][nUsed] = false;
  584. if(GetVehicleModel(Drivers[i][nVehicle]) >= 400 && !gmx) DestroyVehicle(Drivers[i][nVehicle]);
  585. //if(FCNPC_IsValid(Drivers[i][nNPCID])) FCNPC_Destroy(Drivers[i][nNPCID]);
  586. Drivers[i][nNPCID] = -1;
  587. Drivers[i][nVehicle] = -1;
  588. }
  589. }
  590. Initialized = false;
  591. return 1;
  592. }
  593. forward Drivers_DestroyID(count);
  594. public Drivers_DestroyID(count)
  595. {
  596. if(count < 0 || count >= DRIVER_AMOUNT)
  597. {
  598. if(count == DRIVER_AMOUNT)
  599. {
  600. Initialized = false;
  601. if(IsPlayerConnected(ExitPlayerID))
  602. {
  603. SendClientMessage(ExitPlayerID, -1, "Driver FS unloaded.");
  604. print("[DRIVERS] Warning: Driver FS unloaded.");
  605. }
  606. else print("[DRIVERS] Warning: Driver FS unloaded.");
  607. //SendRconCommand("unloadfs "SCRIPT_NAME);
  608. return 2;
  609. }
  610. return 0;
  611. }
  612. if(NumRouteCalcs > 0)
  613. {
  614. if(IsPlayerConnected(ExitPlayerID))
  615. {
  616. new str[50];
  617. format(str, sizeof(str), "Waiting for %d Path Calculations to proceed.", NumRouteCalcs);
  618. SendClientMessage(ExitPlayerID, -1, str);
  619. printf("[DRIVERS] Warning: Waiting for %d Path Calculations to proceed.", NumRouteCalcs);
  620. }
  621. else printf("[DRIVERS] Warning: Waiting for %d Path Calculations to proceed.", NumRouteCalcs);
  622. SetTimerEx("Drivers_DestroyID", 3000, 0, "i", count);
  623. return 1;
  624. }
  625. if(Drivers[count][nUsed])
  626. {
  627. Drivers[count][nUsed] = false;
  628. if(FCNPC_IsValid(Drivers[count][nNPCID]))
  629. {
  630. FCNPC_RemoveFromVehicle(Drivers[count][nNPCID]);
  631. FCNPC_Destroy(Drivers[count][nNPCID]);
  632. NPCDriverID[Drivers[count][nNPCID]] = -1;
  633. }
  634. if(GetVehicleModel(Drivers[count][nVehicle]) >= 400) DestroyVehicle(Drivers[count][nVehicle]);
  635. Drivers[count][nNPCID] = -1;
  636. Drivers[count][nVehicle] = -1;
  637. }
  638. SetTimerEx("Drivers_DestroyID", 7, 0, "i", ++count);
  639. return 1;
  640. }
  641. // -----------------------------------------------------------------------------
  642. public FCNPC_OnCreate(npcid)
  643. {
  644. return 1;
  645. }
  646. // -----------------------------------------------------------------------------
  647. public FCNPC_OnSpawn(npcid)
  648. {
  649. return 1;
  650. }
  651. // -----------------------------------------------------------------------------
  652. public FCNPC_OnStreamIn(npcid, forplayerid)
  653. {
  654. new driverid = GetDriverID(npcid);
  655. if(PlayerEnterDriver[forplayerid] != -1 && PlayerEnterDriver[forplayerid] == driverid)
  656. {
  657. PutPlayerInVehicle(forplayerid, Drivers[driverid][nVehicle], 1);
  658. PlayerEnterDriver[forplayerid] = -1;
  659. }
  660. return 1;
  661. }
  662. // -----------------------------------------------------------------------------
  663. public FCNPC_OnDeath(npcid, killerid, reason)
  664. {
  665. if(!Initialized) return 1;
  666. if(!FCNPC_IsSpawned(npcid)) return 1;
  667. new driverid = GetDriverID(npcid);
  668. if(driverid == -1) return 1;
  669. if(IsPlayerConnected(Drivers[driverid][nPlayer]))
  670. {
  671. SendClientMessage(Drivers[driverid][nPlayer], -1, "[Taxi Service]: {FF0000}Sorry, our driver could not make it to your location. Please call again if you still need a pick up.");
  672. Taxi[Drivers[driverid][nPlayer]] = -1;
  673. }
  674. Drivers[driverid][nOnDuty] = false;
  675. Drivers[driverid][nPlayer] = -1;
  676. Drivers[driverid][nState] = DRIVER_STATE_NONE;
  677. Drivers[driverid][nDeathTick] = GetTickCount();
  678. Drivers[driverid][nResetVeh] = false;
  679. #if SEND_DEATH_MESSAGE == true
  680. if(IsPlayerConnected(killerid))
  681. {
  682. new str[100], name[MAX_PLAYER_NAME+1], weap[25], killdesc[10];
  683. GetPlayerName(npcid, str, sizeof(str));
  684. GetPlayerName(killerid, name, sizeof(name));
  685. GetWeaponName(reason, weap, sizeof(weap));
  686. switch(random(16))
  687. {
  688. case 0: strcat(killdesc, "humiliated");
  689. case 1: strcat(killdesc, "killed");
  690. case 2: strcat(killdesc, "torn apart");
  691. case 3: strcat(killdesc, "erased");
  692. case 4: strcat(killdesc, "vaporized");
  693. case 5: strcat(killdesc, "filled with lead");
  694. case 6: strcat(killdesc, "wiped out");
  695. case 7: strcat(killdesc, "slaughtered");
  696. case 8: strcat(killdesc, "murdered");
  697. case 9: strcat(killdesc, "wasted");
  698. case 10: strcat(killdesc, "annihilated");
  699. case 11: strcat(killdesc, "dumped");
  700. case 12: strcat(killdesc, "lynched");
  701. case 13: strcat(killdesc, "obliterated");
  702. case 14: strcat(killdesc, "liquidated");
  703. case 15: strcat(killdesc, "put to death");
  704. }
  705. if(!strlen(weap)) strcat(weap, "Blown up");
  706. format(str, sizeof(str), "%s was %s by %s [%s]", str, killdesc, name, weap);
  707. SendClientMessageToAll(0xCC6633FF, str);
  708. }
  709. #endif
  710. return 1;
  711. }
  712. // -----------------------------------------------------------------------------
  713. public OnPlayerConnect(playerid)
  714. {
  715. if(!Initialized) return 1;
  716. if(!IsPlayerNPC(playerid))
  717. {
  718. LastTaxiInteraction[playerid] = GetTickCount() - TAXI_COOLDOWN*1000;
  719. PlayerEnterDriver[playerid] = -1;
  720. }
  721. return 1;
  722. }
  723. public OnPlayerDisconnect(playerid)
  724. {
  725. if(!Initialized) return 1;
  726. return 1;
  727. }
  728. // -----------------------------------------------------------------------------
  729. COMMAND:ds(playerid, params[])
  730. {
  731. if(!Initialized || !IsPlayerAdmin(playerid)) return 0;
  732. new id = -1;
  733. if(!isnull(params) && strlen(params) < 8)
  734. {
  735. if(params[0] == 'r') id = random(DRIVER_AMOUNT);
  736. else
  737. {
  738. id = strval(params);
  739. if(id < 0 || id >= DRIVER_AMOUNT) id = -1;
  740. }
  741. }
  742. if(id == -1) return SendClientMessage(playerid, -1, "Invalid Driver ID");
  743. new Float:x, Float:y, Float:z;
  744. FCNPC_GetPosition(Drivers[id][nNPCID], x, y, z);
  745. if(FCNPC_IsStreamedIn(Drivers[id][nNPCID], playerid)) PutPlayerInVehicle(playerid, Drivers[id][nVehicle], 1);
  746. else
  747. {
  748. SetPlayerPos(playerid, x, y, z);
  749. PlayerEnterDriver[playerid] = id;
  750. }
  751. return 1;
  752. }
  753. COMMAND:dfs_cmds(playerid, params[])
  754. {
  755. if(!Initialized) return 0;
  756. SendClientMessage(playerid, -1, " ");
  757. SendClientMessage(playerid, -1, "{99FF00}Driver FS by NaS & AIped (c) 2015-2017");
  758. if(IsPlayerAdmin(playerid))
  759. {
  760. SendClientMessage(playerid, -1, "{FF9900} Admin Commands - []: required, (): optional");
  761. SendClientMessage(playerid, -1, " /ds [ID] (seat) - Take a seat in the specified Driver's Vehicle.");
  762. SendClientMessage(playerid, -1, " /dfs_info - Prints Driver Updates.");
  763. SendClientMessage(playerid, -1, " /dfs_exit - Slowly kicks all NPCs and stops the Script. Unloading the actual FS crashes! :(");
  764. }
  765. SendClientMessage(playerid, -1, "{FF9900} Player Commands");
  766. SendClientMessage(playerid, -1, " /Taxi - Calls a Taxi to your location.");
  767. return 1;
  768. }
  769. COMMAND:dfs_help(playerid, params[])
  770. {
  771. return cmd_dfs_cmds(playerid, params);
  772. }
  773. COMMAND:dfs_exit(playerid, params[])
  774. {
  775. if(!Initialized || !IsPlayerAdmin(playerid)) return 0;
  776. if(NumRouteCalcs == 0) SendClientMessage(playerid, -1, "Unloading Driver FS ...");
  777. else
  778. {
  779. new text[128];
  780. format(text, sizeof(text), "There are %d Path Calculations left. The Script will unload once they are completed.", NumRouteCalcs);
  781. SendClientMessage(playerid, -1, text);
  782. }
  783. ExitPlayerID = playerid;
  784. Drivers_Exit(0, 0);
  785. return 1;
  786. }
  787. COMMAND:dfs_info(playerid, params[])
  788. {
  789. if(!Initialized || !IsPlayerAdmin(playerid)) return 0;
  790. PrintDriverUpdate();
  791. return 1;
  792. }
  793. COMMAND:taxi(playerid, params[])
  794. {
  795. if(!Initialized) return 0;
  796. if(Taxi[playerid] != -1) return SendClientMessage(playerid, -1, "[Taxi Service]: {990000}Sorry Sir, it seems like you have already ordered a taxi."), 1;
  797. if(GetTickCount() - LastTaxiInteraction[playerid] < TAXI_COOLDOWN*1000) return SendClientMessage(playerid, -1, "[Taxi Service]: {990000}Sorry Sir, we don't have any available cabs right now."), 1;
  798. new taxi = -1, Float:tdist = MAX_TAXI_DIST, Float:node_dist, Float:X, Float:Y, Float:Z, MapNode:destnode;
  799. GetPlayerPos(playerid, X, Y, Z);
  800. GetClosestMapNodeToPoint(X, Y, Z, destnode);
  801. if(!IsValidMapNode(destnode))
  802. {
  803. SendClientMessage(playerid, -1, "[Taxi Service]: {990000}Sorry, we don't have your location on our GPS.");
  804. return 1;
  805. }
  806. GetMapNodeDistanceFromPoint(destnode, X, Y, Z, node_dist);
  807. if(node_dist > TAXI_RANGE)
  808. {
  809. SendClientMessage(playerid, -1, "[Taxi Service]: {990000}Unfortunately our driver is unable to reach your current location.");
  810. SendClientMessage(playerid, -1, "[Taxi Service]: {990000}Please proceed to the closest road.");
  811. return 1;
  812. }
  813. for(new i = 0; i < DRIVER_TAXIS; i ++)
  814. {
  815. if(!Drivers[i][nUsed] || Drivers[i][nOnDuty] || !IsPlayerNPC(Drivers[i][nNPCID])) continue;
  816. if(Drivers[i][nState] != DRIVER_STATE_DRIVE) continue;
  817. new Float:dist = GetPlayerDistanceFromPoint(Drivers[i][nNPCID], X, Y, Z);
  818. if(dist < tdist)
  819. {
  820. taxi = i;
  821. tdist = dist;
  822. }
  823. }
  824. if(taxi == -1) return SendClientMessage(playerid, -1, "[Taxi Service]: {990000}Sorry Sir, we don't have an available cab near you."), 1;
  825. new MapNode:startnode, npcid = Drivers[taxi][nNPCID];
  826. FCNPC_GetPosition(npcid, X, Y, Z);
  827. GetClosestMapNodeToPoint(X, Y, Z, startnode);
  828. if(!IsValidMapNode(startnode))
  829. {
  830. SendClientMessage(playerid, -1, "[Taxi Service]: {990000}Sorry, we don't have an available cab near your location.");
  831. return 1;
  832. }
  833. Drivers[taxi][nState] = DRIVER_STATE_NONE;
  834. if(FCNPC_IsMoving(npcid)) FCNPC_Stop(npcid);
  835. Drivers[taxi][nOnDuty] = true;
  836. Drivers[taxi][nPlayer] = playerid;
  837. if(tdist < TAXI_RANGE)
  838. {
  839. TaxiState[playerid] = TAXI_STATE_WAIT1;
  840. SetVehicleParamsForPlayer(Drivers[taxi][nVehicle], playerid, 1, 0);
  841. Drivers[taxi][nLastStart] = Drivers[taxi][nLastDest];
  842. Drivers[taxi][nLastDest] = startnode;
  843. SendClientMessage(playerid, -1, "[Taxi Service]: {009900}We got a driver right around the corner!");
  844. }
  845. else
  846. {
  847. pubCalculatePath(taxi, startnode, destnode);
  848. TaxiState[playerid] = TAXI_STATE_DRIVE1;
  849. }
  850. Taxi[playerid] = taxi;
  851. return 1;
  852. }
  853. public OnPlayerCommandText(playerid, cmdtext[])
  854. {
  855. if(!Initialized) return 0;
  856. return 0;
  857. }
  858. public OnRconCommand(cmd[])
  859. {
  860. if(strcmp(cmd, "dfs_exit", true) == 0)
  861. {
  862. if(!Initialized) return print("[DRIVERS] Warning: Driver FS is not initialized (GMX?)."), 1;
  863. else
  864. {
  865. ExitPlayerID = -1;
  866. Drivers_Exit(0, 0);
  867. }
  868. return 1;
  869. }
  870. return 0;
  871. }
  872. // -----------------------------------------------------------------------------
  873. public OnPlayerStateChange(playerid, newstate, oldstate)
  874. {
  875. if(IsPlayerNPC(playerid)) return 1;
  876. if(!Initialized) return 1;
  877. if(newstate == PLAYER_STATE_PASSENGER)
  878. {
  879. if(Taxi[playerid] >= 0 && Taxi[playerid] < DRIVER_TAXIS && TaxiState[playerid] == TAXI_STATE_WAIT1)
  880. {
  881. if(IsPlayerNPC(Drivers[Taxi[playerid]][nNPCID]))
  882. {
  883. if(GetPlayerVehicleID(playerid) == Drivers[Taxi[playerid]][nVehicle])
  884. {
  885. ShowPlayerDialog(playerid, DID_TAXI, DIALOG_STYLE_LIST, "Choose a destination", gDestinationDialogSTR, "Go", "Cancel");
  886. SetVehicleParamsEx(Drivers[Taxi[playerid]][nVehicle], 1, 0, 0, 0, 0, 0, 0);
  887. new Float:cX, Float:cY, Float:cZ, Float:A, Float:tX, Float:tY;
  888. FCNPC_GetPosition(Drivers[Taxi[playerid]][nNPCID], cX, cY, cZ);
  889. A = FCNPC_GetAngle(Drivers[Taxi[playerid]][nNPCID]);
  890. tX = cX;
  891. tY = cY;
  892. if(GetVehicleModel(Drivers[Taxi[playerid]][nVehicle]) == 420)
  893. {
  894. GetXYInFrontOfPoint(cX, cY, A+180.0, cX, cY, 1.1);
  895. GetXYInFrontOfPoint(cX, cY, A+90.0, cX, cY, 0.15);
  896. cZ += 0.5;
  897. GetXYInFrontOfPoint(tX, tY, A, tX, tY, 2.5);
  898. }
  899. else
  900. {
  901. GetXYInFrontOfPoint(cX, cY, A+180.0, cX, cY, 0.5);
  902. GetXYInFrontOfPoint(cX, cY, A+90.0, cX, cY, 0.15);
  903. cZ += 0.3;
  904. GetXYInFrontOfPoint(tX, tY, A, tX, tY, 2.5);
  905. }
  906. SetPlayerCameraPos(playerid, cX, cY, cZ);
  907. SetPlayerCameraLookAt(playerid, tX, tY, cZ-0.5);
  908. InTaxiView[playerid] = true;
  909. return 1;
  910. }
  911. }
  912. }
  913. }
  914. if(oldstate == PLAYER_STATE_PASSENGER)
  915. {
  916. if(Taxi[playerid] >= 0 && Taxi[playerid] < DRIVER_TAXIS && TaxiState[playerid] == TAXI_STATE_DRIVE2)
  917. {
  918. SetTimerEx("ResetTaxi", 5000, 0, "dd", Taxi[playerid], 3000);
  919. }
  920. }
  921. return 1;
  922. }
  923. // -----------------------------------------------------------------------------
  924. public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
  925. {
  926. if(!Initialized) return 0;
  927. if(dialogid == DID_TAXI)
  928. {
  929. if(Taxi[playerid] == -1 || TaxiState[playerid] != TAXI_STATE_WAIT1) return 1;
  930. if(response)
  931. {
  932. if(gDestinationList[listitem][destName] == '-')
  933. {
  934. ShowPlayerDialog(playerid, DID_TAXI, DIALOG_STYLE_LIST, "Choose a destination", gDestinationDialogSTR, "Go", "Cancel");
  935. return SendClientMessage(playerid, -1, "[Taxi Driver]: {990000}Excuse me, can you re-phrase that?"), 1;
  936. }
  937. new MapNode:destnode;
  938. GetClosestMapNodeToPoint(gDestinationList[listitem][destX], gDestinationList[listitem][destY], gDestinationList[listitem][destZ], destnode);
  939. if(!IsValidMapNode(destnode))
  940. {
  941. ShowPlayerDialog(playerid, DID_TAXI, DIALOG_STYLE_LIST, "Choose a destination", gDestinationDialogSTR, "Go", "Cancel");
  942. SendClientMessage(playerid, -1, "[Taxi Driver]: {990000}Weird. I can't find that spot on my map!");
  943. return 1;
  944. }
  945. Drivers[Taxi[playerid]][nState] = DRIVER_STATE_NONE;
  946. if(FCNPC_IsMoving(Drivers[Taxi[playerid]][nNPCID])) FCNPC_Stop(Drivers[Taxi[playerid]][nNPCID]);
  947. SetTimerEx("pubCalculatePath", 1000 + random(1000), 0, "ddd", Taxi[playerid], _:Drivers[Taxi[playerid]][nLastDest], _:destnode);
  948. TaxiState[playerid] = TAXI_STATE_DRIVE2;
  949. SetVehicleParamsEx(Drivers[Taxi[playerid]][nVehicle], 1, 0, 0, 0, 0, 0, 0);
  950. SetCameraBehindPlayer(playerid);
  951. InTaxiView[playerid] = false;
  952. LastTaxiInteraction[playerid] = GetTickCount();
  953. }
  954. else
  955. {
  956. SetCameraBehindPlayer(playerid);
  957. InTaxiView[playerid] = false;
  958. ResetTaxi(Taxi[playerid], 5000);
  959. }
  960. return 1;
  961. }
  962. return 0;
  963. }
  964. // -----------------------------------------------------------------------------
  965. forward pubCalculatePath(driverid, MapNode:startnode, MapNode:endnode);
  966. public pubCalculatePath(driverid, MapNode:startnode, MapNode:endnode)
  967. {
  968. if(!Initialized) return 1;
  969. if(driverid < 0 || driverid >= DRIVER_AMOUNT) return 1;
  970. if(!Drivers[driverid][nUsed]) return 1;
  971. if(FCNPC_IsDead(Drivers[driverid][nNPCID]) || !FCNPC_IsSpawned(Drivers[driverid][nNPCID])) return 1;
  972. if(Drivers[driverid][nState] != DRIVER_STATE_NONE) return 1;
  973. Drivers[driverid][nLT] = GetTickCount();
  974. //CalculatePath(startnode, endnode, DRIVERS_ROUTE_ID + driverid, false, _, true);
  975. FindPathThreaded(startnode, endnode, "OnPathCalculated", "i", DRIVERS_ROUTE_ID + driverid);
  976. NumRouteCalcs ++;
  977. Drivers[driverid][nLastStart] = startnode;
  978. Drivers[driverid][nLastDest] = endnode;
  979. return 1;
  980. }
  981. forward pub_RemovePlayerFromVehicle(playerid);
  982. public pub_RemovePlayerFromVehicle(playerid)
  983. {
  984. RemovePlayerFromVehicle(playerid);
  985. return 1;
  986. }
  987. // ----------------------------------------------------------------------------- Resets a Taxi after aborted ride, or reaching destination
  988. forward ResetTaxi(driverid, calcdelay);
  989. public ResetTaxi(driverid, calcdelay)
  990. {
  991. if(!Initialized) return 1;
  992. if(driverid < 0 || driverid >= DRIVER_TAXIS) return 1;
  993. if(!Drivers[driverid][nUsed] || !Drivers[driverid][nOnDuty] || Drivers[driverid][nType] != DRIVER_TYPE_TAXI) return 1;
  994. if(!FCNPC_IsSpawned(Drivers[driverid][nNPCID]) || FCNPC_IsDead(Drivers[driverid][nNPCID])) return 1;
  995. new playerid = Drivers[driverid][nPlayer];
  996. if(playerid >= 0 && playerid < MAX_PLAYERS)
  997. {
  998. TaxiState[playerid] = TAXI_STATE_NONE;
  999. Taxi[playerid] = -1;
  1000. LastTaxiInteraction[playerid] = GetTickCount();
  1001. if(InTaxiView[playerid])
  1002. {
  1003. SetCameraBehindPlayer(playerid);
  1004. HidePlayerDialog(playerid);
  1005. InTaxiView[playerid] = false;
  1006. }
  1007. for(new i = 0; i < MAX_PLAYERS; i ++) if(!IsPlayerNPC(i) && GetPlayerVehicleID(i) == Drivers[driverid][nVehicle]) RemovePlayerFromVehicle(i);
  1008. }
  1009. if(GetPlayerVehicleID(playerid) == Drivers[driverid][nVehicle]) SetTimerEx("pub_RemovePlayerFromVehicle", 1000, 0, "d", playerid);
  1010. SetVehicleParamsEx(Drivers[driverid][nVehicle], 1, 0, 0, 0, 0, 0, 0);
  1011. if(FCNPC_IsMoving(Drivers[driverid][nNPCID])) FCNPC_Stop(Drivers[driverid][nNPCID]);
  1012. Drivers[driverid][nState] = DRIVER_STATE_NONE;
  1013. Drivers[driverid][nOnDuty] = false;
  1014. Drivers[driverid][nLT] = GetTickCount();
  1015. new Float:X, Float:Y, Float:Z;
  1016. FCNPC_GetPosition(Drivers[driverid][nNPCID], X, Y, Z);
  1017. new MapNode:startnode, MapNode:endnode, Float:dist;
  1018. GetClosestMapNodeToPoint(X, Y, Z, startnode);
  1019. do
  1020. {
  1021. endnode = GetRandomStartEndPathNode();
  1022. GetDistanceBetweenMapNodes(startnode, endnode, dist);
  1023. }
  1024. while(dist < ROUTE_MIN_DIST || dist > ROUTE_MAX_DIST);
  1025. if(calcdelay > 0) SetTimerEx("pubCalculatePath", calcdelay, 0, "ddd", driverid, _:startnode, _:endnode);
  1026. else pubCalculatePath(driverid, startnode, endnode);
  1027. return 1;
  1028. }
  1029. // ----------------------------------------------------------------------------- This resets NPCs that are dead for a while, or stuck for some reason (eg when a bad GPS.dat was used)
  1030. forward RescueTimer();
  1031. public RescueTimer()
  1032. {
  1033. if(!Initialized) return 1;
  1034. if(avgtickidx >= sizeof(avgticks)) avgtickidx = 0;
  1035. avgticks[avgtickidx] = GetServerTickRate();
  1036. avgtickidx ++;
  1037. new tick = GetTickCount();
  1038. for(new i = 0; i < 20; i ++)
  1039. {
  1040. if(Drivers[rescueid][nUsed])
  1041. {
  1042. SetPlayerColor(Drivers[rescueid][nNPCID], 0);
  1043. if(!FCNPC_IsDead(Drivers[rescueid][nNPCID]))
  1044. {
  1045. if(tick - Drivers[rescueid][nLT] > TAXI_TIMEOUT*1000)
  1046. {
  1047. if(FCNPC_IsMoving(Drivers[rescueid][nNPCID])) Drivers[rescueid][nLT] = tick;
  1048. else if(Drivers[rescueid][nType] == DRIVER_TYPE_TAXI && Drivers[rescueid][nOnDuty]) ResetTaxi(rescueid, 10000);
  1049. #if DEBUG_BUBBLE == true
  1050. new str[40];
  1051. format(str, sizeof(str), "{888888}[%d]\n{DD0000}Timed out", rescueid);
  1052. SetPlayerChatBubble(Drivers[rescueid][nNPCID], str, -1, 10.0, TAXI_TIMEOUT*1000);
  1053. #endif
  1054. }
  1055. }
  1056. else if(Drivers[rescueid][nState] == DRIVER_STATE_NONE)
  1057. {
  1058. if(tick - Drivers[rescueid][nDeathTick] > 10000 && !Drivers[rescueid][nResetVeh]) // Respawns Vehicle
  1059. {
  1060. Drivers[rescueid][nResetVeh] = true;
  1061. SetVehicleToRespawn(Drivers[rescueid][nVehicle]);
  1062. }
  1063. else if(tick - Drivers[rescueid][nDeathTick] > 20000) // Resets the NPC and spawns it
  1064. {
  1065. Drivers[rescueid][nCurNode] = 0;
  1066. Drivers[rescueid][nLT] = tick;
  1067. Drivers[rescueid][nDeathTick] = tick + 10000;
  1068. new MapNode:startnode = GetRandomStartEndPathNode(), MapNode:endnode, Float:dist, tries = 45;
  1069. do
  1070. {
  1071. endnode = GetRandomStartEndPathNode();
  1072. GetDistanceBetweenMapNodes(startnode, endnode, dist);
  1073. tries --;
  1074. }
  1075. while(dist < ROUTE_MIN_DIST || (dist > ROUTE_MAX_DIST && tries > 0)); // tries is used to prevent a long loop (which can happen, because of random - it's veeery unlikely though)
  1076. new Float:X, Float:Y, Float:Z;
  1077. GetMapNodePos(startnode, X, Y, Z);
  1078. FCNPC_Respawn(Drivers[rescueid][nNPCID]);
  1079. FCNPC_PutInVehicle(Drivers[rescueid][nNPCID], Drivers[rescueid][nVehicle], 0);
  1080. FCNPC_SetPosition(Drivers[rescueid][nNPCID], X, Y, Z + VehicleZOffsets[Drivers[rescueid][nVehicleModel] - 400]);
  1081. SetTimerEx("pubCalculatePath", 1000, 0, "ddd", rescueid, _:startnode, _:endnode);
  1082. }
  1083. }
  1084. }
  1085. rescueid ++;
  1086. if(rescueid >= DRIVER_AMOUNT) rescueid = 0;
  1087. }
  1088. return 1;
  1089. }
  1090. // -----------------------------------------------------------------------------
  1091. #if INFO_PRINTS == true
  1092. forward PrintDriverUpdate();
  1093. public PrintDriverUpdate()
  1094. #else
  1095. PrintDriverUpdate()
  1096. #endif
  1097. {
  1098. new curtick = GetTickCount();
  1099. new maxnpc = GetServerVarAsInt("maxnpc"), othernpcs = -DRIVER_AMOUNT, idlenpcs = 0;
  1100. for(new i = 0; i < MAX_PLAYERS; i ++)
  1101. {
  1102. if(IsPlayerNPC(i)) othernpcs ++;
  1103. if(i >= DRIVER_AMOUNT) continue;
  1104. if(!Drivers[i][nUsed] || !IsPlayerNPC(Drivers[i][nNPCID])) continue;
  1105. if(curtick - Drivers[i][nLT] > 60000)
  1106. {
  1107. printf("Driver %d idle!", i);
  1108. idlenpcs ++;
  1109. }
  1110. }
  1111. new Float:avgcalctime = 1.0*avgcalctimes[0];
  1112. for(new i = 1; i < sizeof(avgcalctimes); i ++) avgcalctime += 1.0*avgcalctimes[i];
  1113. avgcalctime = avgcalctime / (1.0*sizeof(avgcalctimes));
  1114. new Float:avgtick = 1.0*avgticks[0];
  1115. for(new i = 1; i < sizeof(avgticks); i ++) avgtick += 1.0*avgticks[i];
  1116. avgtick = avgtick / (1.0*sizeof(avgticks));
  1117. new rtms = curtick - InitialCalculationStart, rts, rtm, rth;
  1118. rts = (rtms / 1000) % 60;
  1119. rtm = (rtms / 60000) % 60;
  1120. rth = rtms / (1000*60*60);
  1121. printf("\n Total Drivers: %d, Random Drivers: %d, Taxi Drivers: %d, Cops: %d\n maxnpc: %d, Other NPCs: %d, Idle NPCs: %d\n MaxPathLen: %d/%d, Uptime: %02d:%02d:%02d\n - - - Avg. calc. time: %.02fms, Avg. Server Tick: %.02f\n", DRIVER_AMOUNT, (DRIVER_AMOUNT - DRIVER_TAXIS - DRIVER_COPS), DRIVER_TAXIS, DRIVER_COPS, maxnpc, othernpcs, idlenpcs, MaxPathLen, MAX_PATH_LEN, rth, rtm, rts, avgcalctime, avgtick);
  1122. return 1;
  1123. }
  1124. // ----------------------------------------------------------------------------- Called when a route was calculated
  1125. forward OnPathCalculated(Path:pathid, routeid);
  1126. public OnPathCalculated(Path:pathid, routeid)
  1127. {
  1128. NumRouteCalcs --;
  1129. if(!Initialized) return 1;
  1130. if(InitialCalculations < DRIVER_AMOUNT) InitialCalculations ++;
  1131. if(!IsValidPath(pathid)) return 1;
  1132. new t = GetTickCount();
  1133. if(routeid >= DRIVERS_ROUTE_ID && routeid < DRIVERS_ROUTE_ID + DRIVER_AMOUNT) // the routeid given comes from this script
  1134. {
  1135. new driverid = routeid - DRIVERS_ROUTE_ID;
  1136. if(!Drivers[driverid][nUsed]) return 1;
  1137. if(!IsPlayerNPC(Drivers[driverid][nNPCID]) || FCNPC_IsDead(Drivers[driverid][nNPCID]) || !FCNPC_IsSpawned(Drivers[driverid][nNPCID])) return 1;
  1138. if(Drivers[driverid][nState] != DRIVER_STATE_NONE) return 1;
  1139. new amount_of_nodes;
  1140. GetPathSize(pathid, amount_of_nodes);
  1141. if(amount_of_nodes < 3)
  1142. {
  1143. #if DEBUG_PRINTS == true
  1144. print("[DRIVERS] Error: Failed calculating path for Driver ID %d", driverid);
  1145. #endif
  1146. Drivers[driverid][nCalcFails] ++;
  1147. return 1;
  1148. }
  1149. new Float:NodePosX[MAX_PATH_LEN], Float:NodePosY[MAX_PATH_LEN], Float:NodePosZ[MAX_PATH_LEN],
  1150. MapNode:nodeid,
  1151. Float:distance;
  1152. GetPathLength(pathid, distance);
  1153. for(new i = 0; i < amount_of_nodes && i < MAX_PATH_LEN; i ++)
  1154. {
  1155. GetPathNode(pathid, i, nodeid);
  1156. GetMapNodePos(nodeid, NodePosX[i], NodePosY[i], NodePosZ[i]);
  1157. }
  1158. Drivers[driverid][nCalcFails] = 0;
  1159. Drivers[driverid][nLT] = t;
  1160. new arrayid = 0, Float:newpath[MAX_PATH_LEN][2];
  1161. newpath[0][0] = NodePosX[0];
  1162. newpath[0][1] = NodePosY[0];
  1163. DriverPath[driverid][0][2] = NodePosZ[0];
  1164. DriverPathLen[driverid] = 1;
  1165. /*
  1166. Loop explanation (below)
  1167. i is the index to write (for newpath array)
  1168. arrayid is the index to read (for node_id_array)
  1169. The target node will stay as long as the distance is too high.
  1170. The target node will skip if the distance is too low.
  1171. */
  1172. for(new i = 1; arrayid < amount_of_nodes && i < MAX_PATH_LEN; i ++)
  1173. {
  1174. if(arrayid == amount_of_nodes-1)
  1175. {
  1176. newpath[i][0] = NodePosX[amount_of_nodes - 1];
  1177. newpath[i][1] = NodePosY[amount_of_nodes - 1];
  1178. DriverPath[driverid][i][2] = NodePosZ[amount_of_nodes - 1];
  1179. DriverPathLen[driverid] ++;
  1180. break;
  1181. }
  1182. new Float:dis = floatsqroot(floatpower(NodePosX[arrayid] - newpath[i-1][0], 2) + floatpower(NodePosY[arrayid] - newpath[i-1][1], 2) + floatpower(NodePosZ[arrayid] - DriverPath[driverid][i-1][2], 2));
  1183. new Float:ndis = MAX_NODE_DIST;
  1184. if(i >= 3 && arrayid < amount_of_nodes - 2)
  1185. {
  1186. new Float:a1 = floatangledistdir(-atan2(NodePosX[arrayid]-newpath[i-1][0], NodePosY[arrayid]-newpath[i-1][1]), -atan2(NodePosX[arrayid+1]-NodePosX[arrayid], NodePosY[arrayid+1]-NodePosY[arrayid]));
  1187. if(a1 < 0.0) a1 = -a1;
  1188. #define SP_ANGLE 25.0
  1189. if(a1 > SP_ANGLE) a1 = SP_ANGLE;
  1190. ndis -= (a1/SP_ANGLE) * MAX_NODE_DIST;
  1191. #undef SP_ANGLE
  1192. new Float: Zrel = (NodePosZ[arrayid] - DriverPath[driverid][i-1][2]) / dis;
  1193. if(Zrel < 0.0) Zrel *= -3.0;
  1194. else Zrel *= 3.0;
  1195. if(Zrel > 0.9) Zrel = 0.9;
  1196. ndis -= (Zrel * MAX_NODE_DIST * 0.7);
  1197. }
  1198. else ndis = MAX_NODE_DIST/2.0;
  1199. if(ndis < MIN_NODE_DIST) ndis = MIN_NODE_DIST;
  1200. if(ndis > MAX_NODE_DIST) ndis = MAX_NODE_DIST;
  1201. if(dis > ndis || arrayid >= amount_of_nodes-2)
  1202. {
  1203. new Float:fact = (dis/ndis);
  1204. newpath[i][0] = newpath[i-1][0] + ((NodePosX[arrayid] - newpath[i-1][0]) / fact);
  1205. newpath[i][1] = newpath[i-1][1] + ((NodePosY[arrayid] - newpath[i-1][1]) / fact);
  1206. DriverPath[driverid][i][2] = DriverPath[driverid][i-1][2] + ((NodePosZ[arrayid] - DriverPath[driverid][i-1][2]) / fact);
  1207. DriverPathLen[driverid] ++;
  1208. if(dis - ndis < MIN_NODE_DIST)
  1209. {
  1210. arrayid ++;
  1211. continue;
  1212. }
  1213. }
  1214. else
  1215. {
  1216. if(i > 0) i --;
  1217. arrayid ++;
  1218. continue;
  1219. }
  1220. }
  1221. if(arrayid < amount_of_nodes - 1) print("[DRIVERS] Error: Could not finish path. Higher MAX_PATH_LEN or MAX_NODE_DIST!");
  1222. newpath = OffsetPath(newpath, DriverPathLen[driverid], -SIDE_DIST); // Offset (right side) - Quick!
  1223. newpath = SmoothPath(newpath, DriverPathLen[driverid]); // Smoothing - heaviest part here
  1224. new Float:MapZd, Float:MapZu;
  1225. for(new i = 0; i < DriverPathLen[driverid]; i ++)
  1226. {
  1227. MapZd = RayCastLineZ(newpath[i][0], newpath[i][1], DriverPath[driverid][i][2], -10.0);
  1228. MapZu = RayCastLineZ(newpath[i][0], newpath[i][1], DriverPath[driverid][i][2], 30.0);
  1229. if(MapZd == 0.0) MapZd = -990.0;
  1230. if(MapZu == 0.0) MapZu = -990.0;
  1231. if(MapZd > -900.0 && MapZu > -900.0)
  1232. {
  1233. new Float:difd = DriverPath[driverid][i][2] - MapZd, Float:difu = (MapZu - DriverPath[driverid][i][2]);
  1234. if(difu < difd) DriverPath[driverid][i][2] = MapZu;
  1235. else DriverPath[driverid][i][2] = MapZd;
  1236. }
  1237. else if(MapZd > -900.0) DriverPath[driverid][i][2] = MapZd;
  1238. else if(MapZu > -900.0) DriverPath[driverid][i][2] = MapZu;
  1239. DriverPath[driverid][i][2] = DriverPath[driverid][i][2] + VehicleZOffsets[Drivers[driverid][nVehicleModel]-400];
  1240. DriverPath[driverid][i][0] = newpath[i][0];
  1241. DriverPath[driverid][i][1] = newpath[i][1];
  1242. }
  1243. Drivers[driverid][nCurNode] = 0;
  1244. Drivers[driverid][nState] = DRIVER_STATE_DRIVE;
  1245. Drivers[driverid][nSpeed] = (MIN_SPEED + MAX_SPEED) / 3.0;
  1246. Drivers[driverid][nDistance] = distance;
  1247. SetTimerEx("FCNPC_OnReachDestination", 50, 0, "d", Drivers[driverid][nNPCID]);
  1248. if(Drivers[driverid][nType] == DRIVER_TYPE_TAXI && Drivers[driverid][nOnDuty] && IsPlayerConnected(Drivers[driverid][nPlayer]))
  1249. {
  1250. if(TaxiState[Drivers[driverid][nPlayer]] == TAXI_STATE_DRIVE1)
  1251. {
  1252. if(distance < 250.0) SendClientMessage(Drivers[driverid][nPlayer], -1, "[Taxi Service]: {009900}Stay where you are. A driver is on his way!");
  1253. else if(distance < 1000.0) SendClientMessage(Drivers[driverid][nPlayer], -1, "[Taxi Service]: {DD9900}Please be patient, our driver may need some time to approach your location.");
  1254. else if(distance < 2000.0) SendClientMessage(Drivers[driverid][nPlayer], -1, "[Taxi Service]: {DD5500}We don't have a taxi close to you. Please wait a few minutes.");
  1255. else SendClientMessage(Drivers[driverid][nPlayer], -1, "[Taxi Service]: {DD5500}We hope you're not in a hurry. Our driver will need quite a while to come to you.");
  1256. }
  1257. else if(TaxiState[Drivers[driverid][nPlayer]] == TAXI_STATE_DRIVE2)
  1258. {
  1259. new roughmins = floatround((distance / (8.3 * MAX_SPEED * DUTY_SPEED_BOOST)) / 60.0);
  1260. if(roughmins <= 1) SendClientMessage(Drivers[driverid][nPlayer], -1, "[Taxi Driver]: {009900}I don't like these short ways...");
  1261. else
  1262. {
  1263. new str[115];
  1264. format(str, sizeof(str), "[Taxi Driver]: {009900}Most would say this takes more than %d minutes. But I'll get you there in about %d!", roughmins + 2 + random(4), roughmins);
  1265. SendClientMessage(Drivers[driverid][nPlayer], -1, str);
  1266. }
  1267. }
  1268. }
  1269. #if DEBUG_PRINTS == true
  1270. if(InitialCalculations <= DRIVER_AMOUNT) printf("[DRIVERS] Debug: (%d ms) - PathLen: %d - Nr %d/%d", GetTickCount() - t, DriverPathLen[driverid], InitialCalculations, DRIVER_AMOUNT);
  1271. else printf("[DRIVERS] Debug: (%d ms) - PathLen: %d", GetTickCount() - t, DriverPathLen[driverid]);
  1272. #endif
  1273. if(InitialCalculations == DRIVER_AMOUNT) { printf("\n[DRIVERS] Initial calculations completed after %.02fs.", (GetTickCount() - InitialCalculationStart) / 1000.0); PrintDriverUpdate(); InitialCalculations = DRIVER_AMOUNT+1; }
  1274. if(DriverPathLen[driverid] > MaxPathLen) MaxPathLen = DriverPathLen[driverid];
  1275. if(avgcalcidx >= sizeof(avgcalctimes)) avgcalcidx = 0;
  1276. avgcalctimes[avgcalcidx] = GetTickCount() - t;
  1277. avgcalcidx ++;
  1278. return 1;
  1279. }
  1280. return 1;
  1281. }
  1282. // ----------------------------------------------------------------------------- Main code
  1283. public FCNPC_OnReachDestination(npcid)
  1284. {
  1285. if(!Initialized) return 1;
  1286. if(!FCNPC_IsSpawned(npcid) || FCNPC_IsDead(npcid)) return 1;
  1287. new driverid = GetDriverID(npcid);
  1288. if(driverid != -1)
  1289. {
  1290. #if MAP_ZONES == true
  1291. if(Drivers[driverid][nGangZone] != -1) { GangZoneDestroy(Drivers[driverid][nGangZone]); Drivers[driverid][nGangZone] = -1; }
  1292. #endif
  1293. Drivers[driverid][nLT] = GetTickCount();
  1294. Drivers[driverid][nCurNode] ++;
  1295. if(Drivers[driverid][nType] == DRIVER_TYPE_COP && random(100) <= 2)
  1296. {
  1297. if(Drivers[driverid][nLT] - Drivers[driverid][nCopStuffTick] > 9000 && Drivers[driverid][nOnDuty])
  1298. {
  1299. FCNPC_UseVehicleSiren(npcid, false);
  1300. Drivers[driverid][nOnDuty] = false;
  1301. }
  1302. else if(Drivers[driverid][nLT] - Drivers[driverid][nCopStuffTick] > 90000 && !Drivers[driverid][nOnDuty])
  1303. {
  1304. Drivers[driverid][nCopStuffTick] = Drivers[driverid][nLT];
  1305. FCNPC_UseVehicleSiren(npcid, true);
  1306. Drivers[driverid][nOnDuty] = true;
  1307. }
  1308. }
  1309. if(Drivers[driverid][nCurNode] == DriverPathLen[driverid]) // Final Destination! >:D
  1310. {
  1311. if(Drivers[driverid][nType] == DRIVER_TYPE_RANDOM || (Drivers[driverid][nType] == DRIVER_TYPE_TAXI && !Drivers[driverid][nOnDuty]) || Drivers[driverid][nType] == DRIVER_TYPE_COP)
  1312. {
  1313. Drivers[driverid][nState] = DRIVER_STATE_NONE;
  1314. new Float:X, Float:Y, Float:Z;
  1315. FCNPC_GetPosition(npcid, X, Y, Z);
  1316. new MapNode:startnode, MapNode:endnode, Float:dist;
  1317. GetClosestMapNodeToPoint(X, Y, Z, startnode);
  1318. do
  1319. {
  1320. endnode = GetRandomStartEndPathNode();
  1321. GetDistanceBetweenMapNodes(startnode, endnode, dist);
  1322. }
  1323. while(dist < ROUTE_MIN_DIST || dist > ROUTE_MAX_DIST);
  1324. SetTimerEx("pubCalculatePath", 10000 + random(10000), 0, "ddd", driverid, _:startnode, _:endnode);
  1325. }
  1326. else if(Drivers[driverid][nType] == DRIVER_TYPE_TAXI && Drivers[driverid][nOnDuty])
  1327. {
  1328. Drivers[driverid][nState] = DRIVER_STATE_NONE;
  1329. new playerid = Drivers[driverid][nPlayer];
  1330. if(TaxiState[playerid] == TAXI_STATE_DRIVE1)
  1331. {
  1332. SetVehicleParamsForPlayer(Drivers[driverid][nVehicle], playerid, 1, 0);
  1333. TaxiState[playerid] = TAXI_STATE_WAIT1;
  1334. }
  1335. if(TaxiState[playerid] == TAXI_STATE_DRIVE2)
  1336. {
  1337. SetVehicleParamsForPlayer(Drivers[driverid][nVehicle], playerid, 0, 1);
  1338. SetTimerEx("pub_RemovePlayerFromVehicle", 1000, 0, "d", playerid);
  1339. SetTimerEx("ResetTaxi", 10000, 0, "dd", driverid, 0);
  1340. SendClientMessage(playerid, -1, "[Taxi Driver]: {009900}Hope you enjoyed the ride! Have a nice day.");
  1341. }
  1342. }
  1343. FCNPC_SetKeys(npcid, 0, 0, 0);
  1344. #if DEBUG_BUBBLE == true
  1345. new str[40];
  1346. format(str, sizeof(str), "{888888}[%d]\n{880000}Finished!", driverid);
  1347. SetPlayerChatBubble(npcid, str, -1, 10.0, 60000);
  1348. #endif
  1349. return 1;
  1350. }
  1351. new cnode = Drivers[driverid][nCurNode];
  1352. #if MAP_ZONES == true
  1353. new Float:X, Float:Y, Float:Z;
  1354. FCNPC_GetPosition(npcid, X, Y, Z);
  1355. Drivers[driverid][nGangZone] = GangZoneCreate(X-4.5, Y-4.5, X+4.5, Y+4.5);
  1356. GangZoneShowForAll(Drivers[driverid][nGangZone], 0x66FF00FF);
  1357. #endif
  1358. if(!FCNPC_IsStreamedInForAnyone(npcid))
  1359. {
  1360. if(Drivers[driverid][nCurNode] < DriverPathLen[driverid]-10)
  1361. {
  1362. Drivers[driverid][nCurNode] += 3;
  1363. cnode += 3;
  1364. }
  1365. FCNPC_GoTo(npcid, DriverPath[driverid][cnode][0], DriverPath[driverid][cnode][1], DriverPath[driverid][cnode][2], FCNPC_MOVE_TYPE_DRIVE, MAX_SPEED*0.8, .pathfinding = FCNPC_MOVE_PATHFINDING_NONE, .radius = 0.0, .set_angle = true, .min_distance = 0.0, .stopdelay = 0);
  1366. Drivers[driverid][nSpeed] = MAX_SPEED*0.7;
  1367. Drivers[driverid][nActive] = false;
  1368. return 1;
  1369. }
  1370. Drivers[driverid][nActive] = true;
  1371. #if MAP_ZONES != true
  1372. new Float:X, Float:Y, Float:Z;
  1373. FCNPC_GetPosition(npcid, X, Y, Z);
  1374. #endif
  1375. if(X < -3000.0) X = -3000.0;
  1376. if(X > 3000.0) X = 3000.0;
  1377. if(Y < -3000.0) Y = -3000.0;
  1378. if(Y > 3000.0) Y = 3000.0;
  1379. Drivers[driverid][nZoneX] = floatround((X + 3000.0) / 6000.0 * ZONES_NUM);
  1380. Drivers[driverid][nZoneY] = floatround((Y + 3000.0) / 6000.0 * ZONES_NUM);
  1381. new Float:A1, Float:A2, bool:blocked = false, Float:x2, Float:y2, Float:z2, Float:dist;
  1382. GetVehicleZAngle(Drivers[driverid][nVehicle], A1);
  1383. for(new i = 0; i < DRIVER_AMOUNT; i ++)
  1384. {
  1385. if(!Drivers[i][nUsed] || !Drivers[i][nActive] || i == driverid) continue;
  1386. if(!FCNPC_IsValid(Drivers[i][nNPCID])) continue;
  1387. if(Drivers[driverid][nZoneX] != Drivers[i][nZoneX] || Drivers[driverid][nZoneY] != Drivers[i][nZoneY]) continue;
  1388. FCNPC_GetPosition(Drivers[i][nNPCID], x2, y2, z2);
  1389. dist = floatsqroot(floatpower(x2-X, 2) + floatpower(y2-Y, 2));
  1390. if(dist >= JAM_DIST) continue; // Distance between both NPCs
  1391. GetVehicleZAngle(Drivers[driverid][nVehicle], A2);
  1392. if(floatangledist(A1, A2) >= JAM_ANGLE) continue; // Angle distance between both NPCs (do they face the same direction?)
  1393. if(floatangledist(A1, -atan2(x2-X, y2-Y)) >= JAM_ANGLE) continue; // Angle distance between NPC1 and the direction to NPC2 (is NPC1 going in NPC2's direction?) - Criteria for being behind!
  1394. blocked = true;
  1395. if(Drivers[driverid][nSpeed] > Drivers[i][nSpeed]) Drivers[driverid][nSpeed] = Drivers[i][nSpeed] - 0.15;
  1396. else if(dist < (JAM_DIST*0.3)) Drivers[driverid][nSpeed] -= 0.1;
  1397. if(Drivers[driverid][nSpeed] < MIN_SPEED) Drivers[driverid][nSpeed] = MIN_SPEED;
  1398. break;
  1399. }
  1400. if(!blocked)
  1401. {
  1402. new Float:AimedSpeed;
  1403. if(cnode > 1 && cnode < DriverPathLen[driverid]-4)
  1404. {
  1405. new Float:Xdif = DriverPath[driverid][cnode][0] - X, Float:Ydif = DriverPath[driverid][cnode][1] - Y, Float:Zdif = DriverPath[driverid][cnode][2] - Z;
  1406. new Float:dif = floatsqroot(Xdif*Xdif + Ydif*Ydif);
  1407. if(dif == 0.0) dif = 1.0;
  1408. else dif = Zdif / dif;
  1409. if(dif < 0.0) dif *= -1.0;
  1410. if(dif > 1.0) dif = 1.0;
  1411. AimedSpeed = MAX_SPEED - (1.7*dif*(MAX_SPEED-MIN_SPEED)); // base speed based on steepness
  1412. new Adif = floatangledist(0.0, Get2DAngleOf3Points(DriverPath[driverid][cnode-1][0], DriverPath[driverid][cnode-1][1], DriverPath[driverid][cnode][0], DriverPath[driverid][cnode][1], DriverPath[driverid][cnode+1][0], DriverPath[driverid][cnode+1][1]));
  1413. if(Adif > 40) Adif = 40;
  1414. AimedSpeed = AimedSpeed - ((AimedSpeed/80.0) * (Adif)); // turning angle
  1415. }
  1416. else if(Drivers[driverid][nOnDuty] && Drivers[driverid][nType] == DRIVER_TYPE_TAXI) AimedSpeed = Drivers[driverid][nSpeed] * 0.8;
  1417. else AimedSpeed = (MIN_SPEED + MAX_SPEED) / 2.0;
  1418. if(AimedSpeed < Drivers[driverid][nSpeed]) Drivers[driverid][nSpeed] = (Drivers[driverid][nSpeed] + AimedSpeed*4.5) / 5.5;
  1419. else Drivers[driverid][nSpeed] += (AimedSpeed - Drivers[driverid][nSpeed]) / (Drivers[driverid][nSpeed]*10.0) + 0.02;
  1420. if(Drivers[driverid][nSpeed] < MIN_SPEED) Drivers[driverid][nSpeed] = MIN_SPEED;
  1421. }
  1422. if(Drivers[driverid][nSpeed] > MAX_SPEED) Drivers[driverid][nSpeed] = MAX_SPEED;
  1423. if(!blocked && Drivers[driverid][nOnDuty] && cnode < DriverPathLen[driverid]-6) Drivers[driverid][nSpeed] *= DUTY_SPEED_BOOST;
  1424. new Float:Qw, Float:Qx, Float:Qy, Float:Qz;
  1425. FCNPC_GetPosition(npcid, X, Y, Z);
  1426. #if DEBUG_BUBBLE == true
  1427. new bool:complex, Float:z_angle;
  1428. #endif
  1429. if(cnode > 0 && cnode < DriverPathLen[driverid]-1)
  1430. {
  1431. new Float:z_angle1, Float:z_angle2, Float:waste, Float:surface_rx, Float:surface_ry;
  1432. GetRZFromVectorXY(DriverPath[driverid][cnode][0] - DriverPath[driverid][cnode - 1][0], DriverPath[driverid][cnode][1] - DriverPath[driverid][cnode - 1][1], z_angle1);
  1433. GetRZFromVectorXY(DriverPath[driverid][cnode + 1][0] - DriverPath[driverid][cnode][0], DriverPath[driverid][cnode + 1][1] - DriverPath[driverid][cnode][1], z_angle2);
  1434. #if DEBUG_BUBBLE == true
  1435. z_angle = z_angle1;
  1436. #endif
  1437. new Float:angle_dif = floatangledistdir(z_angle1, z_angle2);
  1438. FCNPC_SetKeys(npcid, 0, (angle_dif <= -STEER_ANGLE ? 128 : (angle_dif >= STEER_ANGLE ? -128 : 0)), 0);
  1439. CA_RayCastLineAngle(X, Y, Z + 0.75, X, Y, Z - 3.0, waste, waste, waste, surface_rx, surface_ry, waste);
  1440. if(Drivers[driverid][nVehicleIsBike])
  1441. {
  1442. angle_dif = -angle_dif / 15.0;
  1443. if(angle_dif < -1.0) angle_dif = -1.0;
  1444. else if(angle_dif > 1.0) angle_dif = 1.0;
  1445. angle_dif = (angle_dif + Drivers[driverid][nVehicleLastLean] + Drivers[driverid][nVehicleLastLean]) / 3.0;
  1446. Drivers[driverid][nVehicleLastLean] = angle_dif;
  1447. GetRotForSurface(Qw, Qx, Qy, Qz, surface_rx, surface_ry, 0.0, angle_dif * 30.0, z_angle1);
  1448. }
  1449. else GetRotForSurface(Qw, Qx, Qy, Qz, surface_rx, surface_ry, 0.0, 0.0, z_angle1);
  1450. }
  1451. else
  1452. {
  1453. FCNPC_SetKeys(npcid, 0, 0, 0);
  1454. if(cnode > 0)
  1455. {
  1456. new Float:z_angle1, Float:rx;
  1457. GetRZFromVectorXY(DriverPath[driverid][cnode][0] - DriverPath[driverid][cnode - 1][0], DriverPath[driverid][cnode][1] - DriverPath[driverid][cnode - 1][1], z_angle1);
  1458. GetRXFromVectorZ(DriverPath[driverid][cnode][2] - DriverPath[driverid][cnode - 1][2], rx);
  1459. GetRotForSurface(Qw, Qx, Qy, Qz, 0.0, 0.0, rx, 0.0, z_angle1);
  1460. }
  1461. else GetRotForSurface(Qw, Qx, Qy, Qz, 0.0, 0.0, 0.0, 0.0, FCNPC_GetAngle(npcid));
  1462. }
  1463. FCNPC_GoTo(npcid, DriverPath[driverid][cnode][0], DriverPath[driverid][cnode][1], DriverPath[driverid][cnode][2], FCNPC_MOVE_TYPE_DRIVE, Drivers[driverid][nSpeed], .pathfinding = FCNPC_MOVE_PATHFINDING_NONE, .radius = 0.0, .set_angle = true, .min_distance = 0.0, .stopdelay = 0);
  1464. FCNPC_SetQuaternion(npcid, Qw, Qx, Qy, Qz);
  1465. #if DEBUG_BUBBLE == true
  1466. new str[65];
  1467. format(str, sizeof(str), "{888888}[%d]\nX:%d Y:%d B:%b C:%b\n %d {666666}Speed: %.02f\nangle: %f", driverid, Drivers[driverid][nZoneX], Drivers[driverid][nZoneY], blocked, complex, cnode, Drivers[driverid][nSpeed], z_angle);
  1468. SetPlayerChatBubble(npcid, str, -1, 10.0, 5000);
  1469. #endif
  1470. return 1;
  1471. }
  1472. return 1;
  1473. }
  1474. // ----------------------------------------------------------------------------- Some random functions.
  1475. Float:SmoothPath(const Float:path[][2], len = sizeof path) // Basic Smoothing algorithm I (NaS) converted from Python - All nodes orientate at 2 coords in a relation (defined by weight_data & weight_smooth), the original data and the smooth path
  1476. {
  1477. new Float:npath[MAX_PATH_LEN][2];
  1478. if(len > MAX_PATH_LEN) len = MAX_PATH_LEN;
  1479. for(new i = 0; i < len; i ++)
  1480. {
  1481. npath[i][0] = path[i][0];
  1482. npath[i][1] = path[i][1];
  1483. }
  1484. for(new x = 0; x < SMOOTH_AMOUNT; x ++) for(new i = 1; i < len - 1; i ++) // all nodes except start & end
  1485. {
  1486. npath[i][0] = npath[i][0] + SMOOTH_W_DATA * (path[i][0] - npath[i][0]); // Drag node to original pos (with factor)
  1487. npath[i][0] = npath[i][0] + SMOOTH_W_SMOOTH * (npath[i-1][0] + npath[i+1][0] - (2.0 * npath[i][0])); // Drag node to interpolated pos (with factor)
  1488. npath[i][1] = npath[i][1] + SMOOTH_W_DATA * (path[i][1] - npath[i][1]);
  1489. npath[i][1] = npath[i][1] + SMOOTH_W_SMOOTH * (npath[i-1][1] + npath[i+1][1] - (2.0 * npath[i][1]));
  1490. }
  1491. return npath;
  1492. }
  1493. Float:OffsetPath(const Float:path[MAX_PATH_LEN][2], len, Float:d) // Another classy algorithm for offsetting a 2D path - d = distance, negative = right
  1494. {
  1495. new Float:H[MAX_PATH_LEN][2], Float:U[MAX_PATH_LEN][2];
  1496. for(new i = 0; i < len-1; i ++)
  1497. {
  1498. new Float:C = path[i+1][0] - path[i][0];
  1499. new Float:S = path[i+1][1] - path[i][1];
  1500. new Float:L = floatsqroot(C*C+S*S);
  1501. U[i][0] = C/L;
  1502. U[i][1] = S/L;
  1503. }
  1504. H[0][0] = path[0][0] - d*U[0][1];
  1505. H[0][1] = path[0][1] + d*U[0][0];
  1506. for(new i = 1; i < len-1; i ++)
  1507. {
  1508. new Float:v = (1.0 + U[i][0]*U[i-1][0] + U[i][1]*U[i-1][1]);
  1509. new Float:L = d/(v == 0.0 ? 0.0001 : v);
  1510. H[i][0] = path[i][0] - L*(U[i][1] + U[i-1][1]);
  1511. H[i][1] = path[i][1] + L*(U[i][0] + U[i-1][0]);
  1512. }
  1513. H[len-1][0] = path[len-1][0] - d*U[len-2][1];
  1514. H[len-1][1] = path[len-1][1] + d*U[len-2][0];
  1515. return H;
  1516. }
  1517. GetXYInFrontOfPoint(Float:gX, Float:gY, Float:R, &Float:x, &Float:y, Float:distance)
  1518. { // Created by Y_Less
  1519. x = gX + (distance * floatsin(-R, degrees));
  1520. y = gY + (distance * floatcos(-R, degrees));
  1521. }
  1522. HidePlayerDialog(playerid) return ShowPlayerDialog(playerid,-1,0," "," "," "," ");
  1523. MapNode:GetRandomStartEndPathNode()
  1524. {
  1525. if(PathNodesNum < 1 || PathNodesNum > MAX_PATH_NODES) return INVALID_MAP_NODE_ID;
  1526. return PathNodes[random(PathNodesNum)];
  1527. }
  1528. Float:Get2DAngleOf3Points(Float:x1, Float:y1, Float:x2, Float:y2, Float:x3, Float:y3)
  1529. {
  1530. return floatangledistdir(-atan2(x2-x1, y2-y1), -atan2(x3-x2, y3-y2));
  1531. }
  1532. Float:RayCastLineZ(Float:X, Float:Y, Float:Z, Float:dist)
  1533. {
  1534. if(CA_RayCastLine(X, Y, Z, X, Y, Z + dist, X, Y, Z)) return Z;
  1535. else return -999.0;
  1536. }
  1537. floatangledist(Float:alpha, Float:beta) // Ranging from 0 to 180, not directional
  1538. {
  1539. new phi = floatround(floatabs(beta - alpha), floatround_floor) % 360;
  1540. new distance = phi > 180 ? 360 - phi : phi;
  1541. return distance;
  1542. }
  1543. Float:floatangledistdir(Float:firstAngle, Float:secondAngle) // Ranging from -180 to 180 (directional)
  1544. {
  1545. new Float:difference = secondAngle - firstAngle;
  1546. while(difference < -180.0) difference += 360.0;
  1547. while(difference > 180.0) difference -= 360.0;
  1548. return difference;
  1549. }
  1550. GetDriverID(npcid) // Fast NPCID -> DriverID
  1551. {
  1552. if(!FCNPC_IsValid(npcid) || npcid < 0 || npcid >= MAX_PLAYERS) return -1;
  1553. new id = NPCDriverID[npcid];
  1554. if(id >= 0 && id < DRIVER_AMOUNT) if(Drivers[id][nUsed] && Drivers[id][nNPCID] == npcid) return id;
  1555. for(new i = 0; i < DRIVER_AMOUNT; i ++) // Note: This will only be executed if the Array doesn't hold the ID for some reason. Never happened yet.
  1556. {
  1557. if(npcid != Drivers[i][nNPCID] || !Drivers[i][nUsed]) continue;
  1558. return i;
  1559. }
  1560. return -1;
  1561. }
  1562. public OnPlayerWeaponShot(playerid, weaponid, hittype, hitid, Float:fX, Float:fY, Float:fZ) // Fixes NPC Car Damage
  1563. {
  1564. return 1;
  1565. }
  1566. public FCNPC_OnTakeDamage(npcid, issuerid, Float:amount, weaponid, bodypart) // Fixes NPC Body Damage
  1567. {
  1568. return 1;
  1569. }
  1570. public OnPlayerTakeDamage(playerid, issuerid, Float: amount, weaponid, bodypart)
  1571. {
  1572. return 1;
  1573. }
  1574. public OnPlayerGiveDamage(playerid, damagedid, Float: amount, weaponid, bodypart)
  1575. {
  1576. return 1;
  1577. }
  1578. GetRotForSurface(&Float:qw, &Float:qx, &Float:qy, &Float:qz, Float:surface_rx, Float:surface_ry, Float:offset_rx = 0.0, Float:offset_ry = 0.0, Float:offset_rz = 0.0) // By NaS
  1579. {
  1580. const eulermode:rot_mode = euler_zxy;
  1581. new Float:matrix[4][4];
  1582. GetRotationMatrixFromEuler(matrix, offset_rx, offset_ry, offset_rz, rot_mode);
  1583. if(surface_rx != 0.0 || surface_ry != 0.0) RotateMatrixWithEuler(matrix, surface_rx, surface_ry, 0.0, rot_mode);
  1584. new Float:euler_rx, Float:euler_ry, Float:euler_rz;
  1585. GetEulerFromMatrix(matrix, euler_rx, euler_ry, euler_rz);
  1586. GetQuatFromEuler(euler_rx, euler_ry, euler_rz, qw, qx, qy, qz, rot_mode);
  1587. return 1;
  1588. }
  1589. GetRZFromVectorXY(Float:vx, Float:vy, &Float:a)
  1590. {
  1591. if(vx == 0.0 && vy == 0.0) return 0;
  1592. new Float:len = VectorSize(vx, vy, 0.0);
  1593. vx = vx / len;
  1594. vy = vy / len;
  1595. a = atan2(vy, vx) - 90.0;
  1596. return 1;
  1597. }
  1598. GetRXFromVectorZ(Float:vz, &Float:rx)
  1599. {
  1600. rx = -(acos(vz) - 90.0);
  1601. return 1;
  1602. }
  1603. // #EOF