HLMOD.HU Forrás Megtekintés - www.hlmod.hu
  1. new const PLUGINNAME[] = "Sentry guns"
  2. new const VERSION[] = "0.5.3"
  3. new const AUTHOR[] = "JGHG & GlobalModders.net Scripting Team"
  4.  
  5. /*
  6. Copyleft 2004-2008
  7. Plugin topic: http://forums.alliedmods.net/showthread.php?p=696478#post696478
  8.  
  9. SENTRY GUNS
  10. ===========
  11. Sentries in TFC were cool. Sentries in CS are cool.
  12. Sentry guns are stationary engineering wonders that fire bullets at and kill your enemies.
  13. Sentry guns can be upgraded twice, with the help of a team member, to be bigger and meaner.
  14. You can remotely choose to detonate a specific sentry, or all of them.
  15. You can also remotely look through the spycam mounted on your sentry.
  16.  
  17.  
  18. INSTALLATION
  19. ============
  20. 1. Make sure the folling modules are running on your server: CSTRIKE, FUN, ENGINE, FAKEMETA (check configs/modules.ini)
  21. 2. Copy all models and sounds in place.
  22. 3. Scroll down to "Adjust below settings to your liking", do your dirty stuff, scroll up here and continue.
  23. 4. Compile and install plugin. (configs/plugins.ini)
  24. - Done.
  25.  
  26.  
  27. USAGE
  28. =====
  29. client command: sentry_menu
  30. - Build/detonate/upgrade sentry guns with this one-for-all menu. You can also spy through your sentries using this menu.
  31.  
  32. client command: sentry_build
  33. - Quickie to build a sentry without going through menu. Since version 0.2 you can now also point at a sentry and upgrade it with this command, if the sentry is close enough.
  34.  
  35. client command: "/sentryhelp", or just "sentryhelp" in chat
  36. - Displays info on how to bind buttons, what they do, etc.
  37.  
  38.  
  39. VERSIONS
  40. ========
  41. Released Version Comment
  42. 041102 0.5.3 Always some bugs with new features eh? There were some issues with the spycam, those should be fixed.
  43. When a sentry explodes the spycam now also does so that if the owner is looking through it his view will be
  44. set back directly.
  45.  
  46. 041030 0.5.2 With several sentries built you can now cycle through them using the menu, and then select an appropriate action to perform on that sentry.
  47.  
  48. You can now spy through your sentries. A small camera is mounted on all sentries. Activate through menu.
  49.  
  50. Fixed a couple of run time errors occuring if you shot the sentry base to pieces before the sentry head was built.
  51.  
  52. Level typo fixed in hud msg.
  53.  
  54. 041027 0.5.1 Lots of fixes and tweaks as always, I'm listing the ones I can remember:
  55.  
  56. When someone on your team builds a sentry, the location for the construction will flash shortly on radar.
  57. Also when using menu to remotely detonate the closest of your sentries, you can see the closest sentry flashing on your radar.
  58.  
  59. You now get money if your sentry kills an enemy. You can define this, but right now the builder gets $300, and each upgrader $150,
  60. so if you are using the settings where you can upgrade your sentry all the way to level 3 you will get $600 for each kill. :-)
  61. Maybe an incentive for people to upgrade sentries even if they are low on money... It's a gamble! :-)
  62.  
  63. Bots shouldn't build large clusters of sentries anymore but spread them out more evenly across maps...
  64. Having bots be able to fire at the sentries seems now more impossible than ever, I had a chat with the guys at Bots United and
  65. possibly the easiest solution would be to build my own bot, or modify an existing opensource bot. Doh.
  66.  
  67. 041026 0.5 Now uses fakemeta again. Long time no see! :-)
  68.  
  69. Sentry doesn't lose its target if another enemy runs into the line of fire; the other enemy will now be the current target of the sentry.
  70.  
  71. Should now not be able to run onto a sentry base while building to mess things up.
  72.  
  73. Upgrade sentry text in center was off by one level...
  74.  
  75. Rewrote some parts to make for smoother sentry rotation... and stuff... :-)
  76.  
  77. Fatter explosions.
  78.  
  79. Can now only upgrade sentries your team has built. :-)
  80.  
  81. You can now just run into a sentry and it will be upgraded if you have the money for it.
  82.  
  83. Look at a sentry your team has built and you will see its health and level printed in center of screen.
  84.  
  85. Bots can and will now build sentries. Too bad they can't decide to shoot at them yet. :-P This was mostly created so I
  86. can easier stress test this plugin, so don't expect too much but generally bots will build sentries randomly across maps
  87. AND build sentries at objective critical places like bomb targets, hostage rescue points and such.
  88.  
  89. 041021 0.4.3 Fixed sentryhelp page which was messed up
  90.  
  91. 041021 0.4.2 Fixed a few bugs:
  92. - Fatal bug crashing server if a sentry's base was destroyed at end of round.
  93. - DISALLOW_* defines didn't work as stated.
  94. - Sentry builder's death count would get messed up when sentry killed an enemy
  95.  
  96. 041021 0.4.1 You can now shoot a sentry's base for some funny action. Watch your sentry go nuts.
  97.  
  98. In case you didn't notice not all weapons were blown away in previous versions (weapons default on map). They are now also blasted away... :-P
  99.  
  100. 041021 0.4 Removed use of fakemeta, however compatibility with AMX Mod X 0.16 cannot be achieved because a lot of math functions in core and engine modules aren't there.
  101. Upgrade to AMX Mod X 0.20 strongly encouraged.
  102.  
  103. Doubled default health of sentries. Should make'em a little more useful?
  104.  
  105. Menu now always closes when an option is picked
  106.  
  107. Sentries should be able to find targets a lot better. Earlier it couldn't really find a target very well if it was below the sentry.
  108. A sentry now literally fires through its own base when necessary...
  109.  
  110. 041020 0.3 *** Warning: Paths to models and sounds changed! Be sure to update your directories: sound/turrets is now sound/sentries, and models/turrets is now models/sentries.
  111.  
  112. Money now only payed when you actually build a sentry.
  113.  
  114. Fixed a bug that caused this error message: [AMXX] Run time error 10 (native) on line 824
  115.  
  116. By default breaking sentries now explode and push nearby people away and hurt them. Possibly removed what was causing a crash with this in previous version.
  117.  
  118. You must now stand on ground to build a sentry.
  119.  
  120. Added info to player when they log on so they know how they can build sentries (added /sentryhelp)
  121.  
  122. You can choose to have sentries survive rounds by uncommenting the new SENTRIES_SURVIVE_ROUNDS define. Note that they can build sentries in your spawn, so
  123. you might want to leave this off. ;-)
  124.  
  125. Tracers (visual representation of sentry fire) weren't showing in 0.2 and maybe also 0.1, they should now show.
  126.  
  127. More small fixes and tweaks...
  128.  
  129. 041019 0.2 Commands changed! You can now build and upgrade sentries with the one command sentry_build, so you don't have to use the menu.
  130. Point at a sentry and press sentry_build to upgrade it, if you are near enough and have the money.
  131. You can also upgrade using the menu, just point at the sentry first beforing running sentry_menu cmd.
  132.  
  133. Sentries and upgrades now cost money! I do make these plugins for free, but I will charge you for each sentry you build. ;-)
  134.  
  135. Colors: Sentries have different colors, based on team and if you want also a random color. You can specify how you want this with the defines...
  136.  
  137. Bug fixes:
  138. - Fixed upgrading sentries far from them by invoking the menu and running away from them...
  139. - Removed debug info when building a sentry
  140. - Lots more forgotten fixes and tweaks, check all the defines below again
  141.  
  142. Note: Sentries don't explode + generate shock effect no more when they break, because there were some problems with this that caused crashing.
  143. I'll have to fix this later. You can still turn exploding and all on, but do so at your own risk. It usually works OK, but crashes sometimes,
  144. mostly when you break sentry with your knife.
  145.  
  146. 041019 0.1 First release
  147.  
  148.  
  149. TO DO / WISH LIST
  150. =================
  151. - lots of different weapons/powerups for sentries (normal bullets, rockets, tesla coils, firethrowers, grenade lobbers, webs (gets stuck) etc)
  152. - vicinity alarms (aside from sentries) (you buy a small cheap device, and throw it somewhere, and it activates in the next 3 seconds. If anyone moves within its
  153. vicinity a terrible alarm goes off and sounds for a while until its batteries run out ;-). You could buy a more expensive device that will only trigger when the opposite team
  154. gets near it) also vicinity grenades with: explosions, gas, flashes and stuff could be made. (hmm this is a different plugin)
  155. - wall/ceiling-mounted sentries
  156. - fire from sentry should obey shields and also know what bodypart it hit (hs more dmg etc)
  157. - dismantle sentry (nicely without detonating)
  158. - turn it remotely and make it react slower to enemies moving behind its current aim angle
  159. - obey ff, so you can't destroy your teammates' sentries (maybe this isn't possible ;-( )
  160. - repair a sentry
  161. - building in tight places should not get sentries stuck in ceiling (TraceCheckCollides)
  162. - remote cam view from sentry for builder... or something
  163.  
  164. - regenarate hp slowly to 100 when hit.
  165. - make a lot of settings adjustable ingame...
  166.  
  167.   - Johnny got his gun
  168. */
  169. //#define DEBUG
  170.  
  171. #include <amxmodx>
  172. #include <amxmisc>
  173. #include <engine>
  174. #include <fun>
  175. #include <cstrike>
  176. #include <fakemeta>
  177. #if defined DEBUG
  178. #include <amxmisc>
  179. #endif
  180.  
  181. new sentry_max, sentry_cost1, sentry_cost2, sentry_cost3, sentry_team;
  182. #define MAXSENTRIESTOTAL 20
  183. // ---------- Adjust below settings to your liking ---------------------------------------------------------------------------------------------------------------------------------
  184. //#define MAXPLAYERSENTRIES 3 // how many sentries each player can build
  185. #define MAXPLAYERSENTRIES get_pcvar_num(sentry_max) // how many sentries each player can build
  186. #define DMG_EXPLOSION_TAKE 90 // how much HP at most an exploding sentry takes from a player - the further away the less dmg is dealt to player
  187. #define SENTRYEXPLODERADIUS 250.0 // how far away it is safe to be from an exploding sentry without getting kicked back and hurt
  188. #define THINKFIREFREQUENCY 0.1 // the rate in seconds between each bullet when firing at a locked target
  189. #define SENTRYTILTRADIUS 830.0 // likely you won't need to touch this. it's how accurate the cannon will aim at the target vertically (up/down, just for looks, aim is calculated differently)
  190. #if !defined DEBUG
  191. #define DISALLOW_OWN_UPGRADES // you cannot upgrade your own sentry to level 2 (only to level 3 if someone else upgraded it already) (only have this commented in debug mode)
  192. #define DISALLOW_TWO_UPGRADES // the upgrader cannot upgrade again, builder must upgrade to level 3 (only have this commented in debug mode)
  193. #endif
  194. //#define SENTRIES_SURVIVE_ROUNDS // comment this define to have sentries removed between rounds, else they will stay where they are.
  195. //#define RANDOM_TOPCOLOR // sentries have two colors, one top and one bottom. The top one will be random if you leave this define be, else always red for T and blue for CT.
  196. //#define RANDOM_BOTTOMCOLOR // sentries have two colors, one top and one bottom. The bottom one will be random if you leave this define be, else always red for T and blue for CT.
  197. #define EXPLODINGSENTRIES // comment this out if you don't want the sentries to explode, push people away and hurt them (should now be stable!)
  198. // Bots will build sentries at objective critical locations (around dropped bombs, at bomb targets, near hostages etc)
  199. // They can also build randomly around maps using these values:
  200. #define BOT_WAITTIME_MIN 0.0 // waittime = the time a bot will wait after he's decided to build a sentry, before actually building (seconds)
  201. #define BOT_WAITTIME_MAX 15.0
  202. #define BOT_NEXT_MIN 0.0 // next = after building a sentry, this specifies the time a bot will wait until considering about waittime again (seconds)
  203. #define BOT_NEXT_MAX 120.0
  204. // These are per sentry level, 1-3
  205. new const g_SENTRYFRAGREWARDS[3] = {300, 150, 150} // how many $ you get if your sentry frags someone. If you built and upgraded to level 3 you would get $300 + $150 = $450. If built and upgraded all you would get $600.
  206. new const g_DMG[3] = {5, 10, 15} // how much damage a bullet from a sentry does per hit
  207. new const Float:g_THINKFREQUENCIES[3] = {2.0, 1.0, 0.5} // how often, in seconds, a sentry searches for targets when not locked at a target, a lower value means a sentry will lock on targets faster
  208. new const Float:g_HITRATIOS[3] = {0.6, 0.75, 0.85} // how good a sentry is at hitting its target. 1.0 = always hit, 0.0 = never hit
  209. new const Float:g_HEALTHS[3] = {400.0, 800.0, 1600.0} // how many HP a sentry has. Increase to make sentry sturdier
  210. //new const g_COST[3] = {1000, 500, 250} // fun has a price, first is build cost, the next two upgrade costs
  211. #define COST_INIT get_pcvar_num(sentry_cost1)
  212. #define COST_UP get_pcvar_num(sentry_cost2)
  213. #define COST_UPTWO get_pcvar_num(sentry_cost3)
  214. // ---------- Adjust above settings to your liking ---------------------------------------------------------------------------------------------------------------------------------
  215.  
  216. #if !defined PI
  217. #define PI 3.141592654 // feel free to find a PI more exact than this
  218. #endif
  219.  
  220. #define MENUBUTTON1 (1<<0)
  221. #define MENUBUTTON2 (1<<1)
  222. #define MENUBUTTON3 (1<<2)
  223. #define MENUBUTTON4 (1<<3)
  224. #define MENUBUTTON5 (1<<4)
  225. #define MENUBUTTON6 (1<<5)
  226. #define MENUBUTTON7 (1<<6)
  227. #define MENUBUTTON8 (1<<7)
  228. #define MENUBUTTON9 (1<<8)
  229. #define MENUBUTTON0 (1<<9)
  230. #define MENUSELECT1 0
  231. #define MENUSELECT2 1
  232. #define MENUSELECT3 2
  233. #define MENUSELECT4 3
  234. #define MENUSELECT5 4
  235. #define MENUSELECT6 5
  236. #define MENUSELECT7 6
  237. #define MENUSELECT8 7
  238. #define MENUSELECT9 8
  239. #define MENUSELECT0 9
  240. #define MAXHTMLSIZE 1536
  241.  
  242. #define MAXSENTRIES 32 * MAXSENTRIESTOTAL
  243.  
  244. #define SENTRY_VEC_PEOPLE EV_VEC_vuser1
  245. #define OWNER 0
  246. #define UPGRADER_1 1
  247. #define UPGRADER_2 2
  248. //#define SENTRY_VECENT_OWNER SENTRY_VEC_PEOPLE + OWNER
  249. //#define SENTRY_VECENT_UPGRADER_1 SENTRY_VEC_PEOPLE + UPGRADER_1
  250. //#define SENTRY_VECENT_UPGRADER_2 SENTRY_VEC_PEOPLE + UPGRADER_2
  251.  
  252. GetSentryPeople(sentry, who) {
  253. new Float:people[3]
  254. entity_get_vector(sentry, SENTRY_VEC_PEOPLE, people)
  255. return floatround(people[who])
  256. }
  257. SetSentryPeople(sentry, who, is) {
  258. new Float:people[3]
  259. entity_get_vector(sentry, SENTRY_VEC_PEOPLE, people)
  260. people[who] = is + 0.0
  261. entity_set_vector(sentry, SENTRY_VEC_PEOPLE, people)
  262. }
  263.  
  264. #define SENTRY_ENT_TARGET EV_ENT_euser1
  265. #define SENTRY_ENT_BASE EV_ENT_euser2
  266. #define SENTRY_ENT_SPYCAM EV_ENT_euser3
  267. #define SENTRY_INT_FIRE EV_INT_iuser1
  268. #define SENTRY_INT_TEAM EV_INT_iuser2
  269. #define SENTRY_INT_LEVEL EV_INT_iuser3
  270. #define SENTRY_INT_PENDDIR EV_INT_iuser4 // 1st bit: sentry cannon, 2nd bit: radar
  271. #define SENTRY_FL_ANGLE EV_FL_fuser1
  272. #define SENTRY_FL_SPINSPEED EV_FL_fuser2
  273. #define SENTRY_FL_MAXSPIN EV_FL_fuser3
  274. #define SENTRY_FL_RADARANGLE EV_FL_fuser4
  275.  
  276. // These are bits used in SENTRY_INT_PENDDIR
  277. #define SENTRY_DIR_CANNON 0
  278. #define SENTRY_DIR_RADAR 1
  279.  
  280. #define BASE_ENT_SENTRY EV_ENT_euser1
  281. #define BASE_INT_TEAM EV_INT_iuser1
  282.  
  283. #define SENTRY_LEVEL_1 0
  284. #define SENTRY_LEVEL_2 1
  285. #define SENTRY_LEVEL_3 2
  286. #define SENTRY_FIREMODE_NO 0
  287. #define SENTRY_FIREMODE_YES 1
  288. #define SENTRY_FIREMODE_NUTS 2
  289. #define TASKID_SENTRYFIRE 1000
  290. #define TASKID_BOTBUILDRANDOMLY 2000
  291. #define TASKID_SENTRYSTATUS 3000
  292. #define TASKID_THINK 4000
  293. #define TASKID_THINKPENDULUM 5000
  294. #define TASKID_SENTRYONRADAR 6000
  295. #define TASKID_SPYCAM 7000
  296. #define DUCKINGPLAYERDIFFERENCE 18.0
  297. #define TARGETUPMODIFIER DUCKINGPLAYERDIFFERENCE // if player ducks on ground, traces don't hit...
  298. #define DMG_BULLET (1<<1) // shot
  299. #define DMG_BLAST (1<<6) // explosive blast damage
  300. #define TE_EXPLFLAG_NONE 0
  301. #define TE_EXPLOSION 3
  302. #define TE_TRACER 6
  303. #define TE_BREAKMODEL 108
  304. #define BASESENTRYDELAY 2.0 // seconds from base is built until sentry top appears
  305. #define PENDULUM_MAX 45.0 // how far sentry turret turns in each direction when idle, before turning back
  306. #define PENDULUM_INCREMENT 10.0 // speed of turret turning...
  307. #define RADAR_INCREMENT 1.5 // speed of small radar turning on top of sentry level 3...
  308. #define MAXUPGRADERANGE 75.0 // farthest distance to sentry you can upgrade using upgrade command
  309. #define COLOR_BOTTOM_CT 160 // default bottom colour of CT:s sentries
  310. #define COLOR_TOP_CT 150 // default top colour of CT:s sentries
  311. #define COLOR_BOTTOM_T 0 // default bottom colour of T:s sentries
  312. #define COLOR_TOP_T 0 // default top colour of T:s sentries
  313. #define SENTRYSHOCKPOWER 3.0 // multiplier, increase to make exploding sentries throw stuff further away
  314. #define CANNONHEIGHTFROMFEET 20.0 // tweakable to make tracer originate from the same height as the sentry's cannon. Also traces rely on this Y-wise offset.
  315. #define PLAYERORIGINHEIGHT 36.0 // this is the distance from a player's EV_VEC_origin to ground, if standing up
  316. #define HEIGHTDIFFERENCEALLOWED 20.0 // increase value to allow building in slopes with higher angles. You can set to 0.0 and you will only be able to build on exact flat ground. note: mostly applies to downhill building, uphill is still likely to "collide" with ground...
  317.  
  318. // This cannot account for sentries which are still under construction (only base, no sentry head yet):
  319. // How many (or more) sentries:
  320. #define BOT_MAXSENTRIESNEAR 1
  321. // cannot be in the vicinity of this radius:
  322. #define BOT_MAXSENTRIESDISTANCE 1500.0
  323. // for a bot to build at a location. Use higher values and bots will build sentries less close to other sentries on same team.
  324.  
  325. #define BOT_OBJECTIVEWAIT 10 // nr of seconds that must pass after a bot has built an objective related sentry until he can build such a sentry again.
  326.  
  327.  
  328. #define SENTRY_TILT_TURRET EV_BYTE_controller2
  329. #define SENTRY_TILT_LAUNCHER EV_BYTE_controller3
  330. #define SENTRY_TILT_RADAR EV_BYTE_controller4
  331. #define PEV_SENTRY_TILT_TURRET pev_controller_1
  332. #define PEV_SENTRY_TILT_LAUNCHER pev_controller_2
  333. #define PEV_SENTRY_TILT_RADAR pev_controller_3
  334.  
  335. #define STATUSINFOTIME 0.5 // the frequency of hud message updates when spectating a sentry, don't set too low or it could overflow clients. Data should now always send again as soon as it updates though.
  336. #define SENTRY_RADAR 20 // use as high as possible but should still be working (ie be able to see sentries plotted on radar while in menu, too high values doesn't seem to work)
  337. #define SENTRY_RADAR_TEAMBUILT 21 // same as above
  338.  
  339. #define SPYCAMTIME 5.0 // nr of seconds the spycam is active
  340.  
  341. enum OBJECTTYPE {
  342. OBJECT_GENERIC,
  343. OBJECT_GRENADE,
  344. OBJECT_PLAYER,
  345. OBJECT_ARMOURY
  346. }
  347.  
  348. // Global vars below
  349. new g_sentriesNum = 0
  350. new g_sentries[MAXSENTRIES]
  351. new g_playerSentries[32] = {0, ...}
  352. new g_playerSentriesEdicts[32][MAXSENTRIESTOTAL]
  353. new g_sModelIndexFireball
  354. new g_msgDamage
  355. new g_msgDeathMsg
  356. new g_msgScoreInfo
  357. new g_msgHostagePos
  358. new g_msgHostageK
  359. new g_MAXPLAYERS
  360. //new g_MAXENTITIES
  361. new Float:g_ONEEIGHTYTHROUGHPI
  362. //new g_hasSentries = 0
  363. new Float:g_sentryOrigins[32][3]
  364. new g_aimSentry[32]
  365. new bool:g_inBuilding[32]
  366. new bool:g_resetArmouryThisRound = true
  367. new bool:g_hasArmouries = false
  368. new Float:g_lastGameTime = 1.0 // dunno, looks like get_systime() is always 1.0 first time...
  369. new Float:g_gameTime
  370. new Float:g_deltaTime
  371. new g_sentryStatusBuffer[32][256]
  372. new g_sentryStatusTrigger
  373. new g_selectedSentry[32] = {-1, ...}
  374. new g_menuId // used to store index of menu
  375. new g_lastObjectiveBuild[32]
  376. new g_inSpyCam[32]
  377. new Float:g_spyCamOffset[3] = {26.0, 29.0, 26.0} // for the three levels, just what looks good...
  378. //Shaman: For disabling building until some time passes after the new round
  379. new bool:g_allowBuild //Building, upgrading and reparing is not allowed if this is false
  380. new sentry_wait //The cvar
  381. // Global vars above
  382.  
  383.  
  384. //Shaman: Event function that removes sentries of a player after team change
  385. public jointeam_event()
  386. {
  387. //Get the di from the event data
  388. new id= read_data(1)
  389.  
  390. //Remove sentries
  391. while(GetSentryCount(id)>0)
  392. sentry_detonate_by_owner(id, true)
  393.  
  394. return PLUGIN_CONTINUE
  395. }
  396.  
  397. //Shaman: The function that enables building
  398. public enablesentrybur()
  399. {
  400. g_allowBuild= true;
  401. }
  402.  
  403. public createsentryhere(id) {
  404. //Shaman: Check if the player is allowed to build
  405. if(!g_allowBuild)
  406. {
  407. client_print(id, print_center, "Megkell varni hogy felepuljon, es akkor lehet javitani vagy fejleszteni!")
  408. return PLUGIN_HANDLED
  409. }
  410.  
  411. new sentry = AimingAtSentry(id, true)
  412. // if a valid sentry
  413. // if within range
  414. // we can try to upgrade/repair...
  415. if (sentry && entity_range(sentry, id) <= MAXUPGRADERANGE)
  416. {
  417. //client_print(id, print_chat, "Sentry level: %d, last upgrader: %d", entity_get_int(sentry, SENTRY_INT_LEVEL) + 1, entity_get_edict(sentry, SENTRY_ENT_LASTUPGRADER))
  418. #if defined DISALLOW_OWN_UPGRADES
  419. // Don't allow builder to upgrade his own sentry first time.
  420. if (entity_get_int(sentry, SENTRY_INT_LEVEL) == SENTRY_LEVEL_1 && id == GetSentryPeople(sentry, OWNER)) {
  421. client_print(id, print_center, "Fejlodott az egyik Sentry fegyvered a 2. szintre!")
  422. return PLUGIN_HANDLED
  423. }
  424. #endif
  425. #if defined DISALLOW_TWO_UPGRADES
  426. // Don't allow upgrader to upgrade again.
  427. if (entity_get_int(sentry, SENTRY_INT_LEVEL) == SENTRY_LEVEL_2 && id == GetSentryPeople(sentry, UPGRADER_1)) {
  428. client_print(id, print_center, "Fejlodott az egyik Sentry fegyvered a 2. szintre!")
  429. return PLUGIN_HANDLED
  430. }
  431. #endif
  432. g_aimSentry[id - 1] = sentry
  433. //if (entity_
  434. sentry_upgrade(id, sentry)
  435. }
  436. else {
  437. sentry_build(id)
  438. }
  439.  
  440. return PLUGIN_HANDLED
  441. }
  442.  
  443. public sentry_build(id) {
  444. //Shaman: Check if the player is allowed to build
  445. if(!g_allowBuild)
  446. {
  447. client_print(id, print_center, "Megkell varni hogy betoltson altalaban 10 masodperc, es akkor lehet epiteni, javitani vagy fejleszteni!")
  448. return
  449. }
  450.  
  451. if (GetSentryCount(id) >= MAXPLAYERSENTRIES) {
  452. new wordnumbers[128]
  453. getnumbers(MAXPLAYERSENTRIES, wordnumbers, 127)
  454. new maxsentries = MAXPLAYERSENTRIES // stupid, but compiler gives warning on next line if a defined constant is used
  455. client_print(id, print_center, "Te most epitettel %s sentry gun%s!", wordnumbers, maxsentries != 1 ? "s" : "")
  456. return
  457. }
  458. else if (g_inBuilding[id - 1]) {
  459. client_print(id, print_center, "Wow, te milyen gyorsan epitessz")
  460. return
  461. }
  462. else if (!is_user_alive(id)) {
  463. return
  464. }
  465. else if (cs_get_user_money(id) < g_COST(0)) {
  466. client_print(id, print_center, "Neked nincs eleg penzed a fegyverre! Ennyi kell: ($%d needed)", g_COST(0))
  467. return
  468. }
  469. else if (!entity_is_on_ground(id)) {
  470. client_print(id, print_center, "Nem jo talaj, meg kell alni a foldon hogy lojon!")
  471. return
  472. }
  473. //else if (entity_get_int(id, EV_INT_flags) & FL_DUCKING) {
  474. else if (entity_get_int(id, EV_INT_bInDuck)) {
  475. client_print(id, print_center, "Jo probald ki a fegyvert! ***!")
  476. return
  477. }
  478. else if ( get_pcvar_num( sentry_team ) ) {
  479. if( get_pcvar_num( sentry_team ) < 0 && !is_user_admin(id) ){
  480. client_print(id, print_center, "Egy Admin epitett!")
  481. return
  482. }
  483. else if( !(abs(get_pcvar_num( sentry_team )) & _:cs_get_user_team(id)) ){
  484. client_print(id, print_center, "A csapatodnak nincs engedelyezve!")
  485. return
  486. }
  487. }
  488.  
  489. new Float:playerOrigin[3]
  490. entity_get_vector(id, EV_VEC_origin, playerOrigin)
  491.  
  492. new Float:vNewOrigin[3]
  493. new Float:vTraceDirection[3]
  494. new Float:vTraceEnd[3]
  495. new Float:vTraceResult[3]
  496. velocity_by_aim(id, 64, vTraceDirection) // get a velocity in the directino player is aiming, with a multiplier of 64...
  497. vTraceEnd[0] = vTraceDirection[0] + playerOrigin[0] // find the new max end position
  498. vTraceEnd[1] = vTraceDirection[1] + playerOrigin[1]
  499. vTraceEnd[2] = vTraceDirection[2] + playerOrigin[2]
  500. trace_line(id, playerOrigin, vTraceEnd, vTraceResult) // trace, something can be in the way, use hitpoint from vTraceResult as new origin, if nothing's in the way it should be same as vTraceEnd
  501. vNewOrigin[0] = vTraceResult[0]// just copy the new result position to new origin
  502. vNewOrigin[1] = vTraceResult[1]// just copy the new result position to new origin
  503. vNewOrigin[2] = playerOrigin[2] // always build in the same height as player.
  504.  
  505. if (CreateSentryBase(vNewOrigin, id)) {
  506. cs_set_user_money(id, cs_get_user_money(id) - g_COST(0))
  507. //SetHasSentry(id, true)
  508. //client_print(id, print_chat, "Done creating a sentry at %f %f %f!", vNewOrigin[0], vNewOrigin[1], vNewOrigin[2])
  509. }
  510. else {
  511. //SetHasSentry(id, false)
  512. client_print(id, print_center, "Nem lehet itt epiteni orszemet!")
  513. }
  514. }
  515.  
  516. GetSentryCount(id) {
  517. return g_playerSentries[id - 1]
  518.  
  519. //else
  520.  
  521. /*
  522. if (id < 1 || id > get_maxplayers())
  523. return false
  524.  
  525. //spambits(id, g_hasSentries)
  526.  
  527. return g_hasSentries & (1<<(id - 1)) ? true : false // g_hasSentries[id - 1] //
  528. */
  529. }
  530.  
  531. bool:GetStatusTrigger(player) {
  532. if (!is_user_alive(player))
  533. return false
  534.  
  535. return g_sentryStatusTrigger & (1<<(player-1)) ? true : false
  536. }
  537. SetStatusTrigger(player, bool:onOrOff) {
  538. if (onOrOff)
  539. g_sentryStatusTrigger |= (1<<(player - 1))
  540. else
  541. g_sentryStatusTrigger &= ~(1<<(player - 1))
  542. }
  543.  
  544. IncreaseSentryCount(id, sentryEntity) {
  545. g_playerSentriesEdicts[id - 1][g_playerSentries[id - 1]] = sentryEntity
  546. g_playerSentries[id - 1] = g_playerSentries[id - 1] + 1
  547. new Float:sentryOrigin[3], iSentryOrigin[3]
  548. entity_get_vector(sentryEntity, EV_VEC_origin, sentryOrigin)
  549. FVecIVec(sentryOrigin, iSentryOrigin)
  550.  
  551. new name[32]
  552. get_user_name(id, name, 31)
  553. new CsTeams:builderTeam = cs_get_user_team(id)
  554. for (new i = 1; i <= g_MAXPLAYERS; i++) {
  555. if (!is_user_connected(i) || !is_user_alive(i) || cs_get_user_team(i) != builderTeam || id == i)
  556. continue
  557. client_print(i, print_center, "%s epitett egy sentry Gunt a ti csapatotokban!", name, floatround(entity_range(i, sentryEntity)))
  558.  
  559. //client_print(parm[0], print_chat, "Plotting closest sentry %d on radar: %f %f %f", parm[1], sentryOrigin[0], sentryOrigin[1], sentryOrigin[2])
  560. message_begin(MSG_ONE, g_msgHostagePos, {0,0,0}, i)
  561. write_byte(i)
  562. write_byte(SENTRY_RADAR_TEAMBUILT)
  563. write_coord(iSentryOrigin[0])
  564. write_coord(iSentryOrigin[1])
  565. write_coord(iSentryOrigin[2])
  566. message_end()
  567.  
  568. message_begin(MSG_ONE, g_msgHostageK, {0,0,0}, i)
  569. write_byte(SENTRY_RADAR_TEAMBUILT)
  570. message_end()
  571. }
  572. //client_print(0, print_chat, "%s has built a sentry gun", name)
  573. }
  574.  
  575. DecreaseSentryCount(id, sentry) {
  576. // Note that sentry does not exist at this moment, it's just an old index that should get zeroed where it occurs in g_playerSentriesEdicts[id - 1][]
  577. g_selectedSentry[id - 1] = -1
  578.  
  579. for (new i = 0; i < g_playerSentries[id - 1]; i++) {
  580. if (g_playerSentriesEdicts[id - 1][i] == sentry) {
  581. // Copy last sentry edict index to this one
  582. g_playerSentriesEdicts[id - 1][i] = g_playerSentriesEdicts[id - 1][g_playerSentries[id - 1] - 1]
  583. // Zero out last sentry index
  584. g_playerSentriesEdicts[id - 1][g_playerSentries[id - 1] - 1] = 0
  585. break
  586. }
  587. }
  588. g_playerSentries[id - 1] = g_playerSentries[id - 1] - 1
  589. }
  590.  
  591. /*
  592. SetHasSentry(id, bool:trueOrFalse) {
  593. //g_hasSentries[id - 1] = trueOrFalse
  594. //spambits(id, g_hasSentries)
  595. if (trueOrFalse) {
  596. g_hasSentries |= (1<<(id - 1))
  597. new name[32]
  598. get_user_name(id, name, 31)
  599. new CsTeams:builderTeam = cs_get_user_team(id)
  600. for (new i = 0; i < g_MAXPLAYERS; i++) {
  601. if (!is_user_connected(i) || !is_user_alive(i) || cs_get_user_team(i) != builderTeam || id == i)
  602. continue
  603. client_print(i, print_center, "%s has built a sentry gun", name)
  604. }
  605. }
  606. else
  607. g_hasSentries &= ~(1<<(id - 1))
  608.  
  609. //spambits(id, g_hasSentries)
  610. }
  611. */
  612.  
  613. stock bool:CreateSentryBase(Float:origin[3], creator) {
  614. // Check contents of point, also trace lines from center to each of the eight ends
  615. if (point_contents(origin) != CONTENTS_EMPTY || TraceCheckCollides(origin, 24.0)) {
  616. return false
  617. }
  618.  
  619. // Check that a trace from origin straight down to ground results in a distance which is the same as player height over ground?
  620. new Float:hitPoint[3], Float:originDown[3]
  621. originDown = origin
  622. originDown[2] = -5000.0 // dunno the lowest possible height...
  623. trace_line(0, origin, originDown, hitPoint)
  624. new Float:baDistanceFromGround = vector_distance(origin, hitPoint)
  625. //client_print(creator, print_chat, "Base distance from ground: %f", baDistanceFromGround)
  626.  
  627. new Float:difference = PLAYERORIGINHEIGHT - baDistanceFromGround
  628. if (difference < -1 * HEIGHTDIFFERENCEALLOWED || difference > HEIGHTDIFFERENCEALLOWED) {
  629. //client_print(creator, print_chat, "You can't build here! %f", difference)
  630. return false
  631. }
  632.  
  633. new entbase = create_entity("func_breakable") // func_wall
  634. if (!entbase)
  635. return false
  636.  
  637. // Set sentrybase health
  638. new healthstring[16]
  639. num_to_str(floatround(g_HEALTHS[0]), healthstring, 15)
  640. DispatchKeyValue(entbase, "health", healthstring)
  641. DispatchKeyValue(entbase, "material", "6")
  642.  
  643. DispatchSpawn(entbase)
  644. // Change classname
  645. entity_set_string(entbase, EV_SZ_classname, "sentrybase")
  646. // Set model
  647. entity_set_model(entbase, "models/sentries/base.mdl") // later set according to level
  648. // Set size
  649. new Float:mins[3], Float:maxs[3]
  650. mins[0] = -16.0
  651. mins[1] = -16.0
  652. mins[2] = 0.0
  653. maxs[0] = 16.0
  654. maxs[1] = 16.0
  655. maxs[2] = 1000.0 // Set to 16.0 later.
  656. entity_set_size(entbase, mins, maxs)
  657. //client_print(creator, print_chat, "Creating sentry %d with bounds %f", ent, BOUNDS)
  658. // Set origin
  659. entity_set_origin(entbase, origin)
  660. // Set starting angle
  661. //entity_get_vector(creator, EV_VEC_angles, origin)
  662. //origin[0] = 0.0
  663. //origin[1] += 180.0
  664. //origin[2] = 0.0
  665. //entity_set_vector(ent, EV_VEC_angles, origin)
  666. // Set solidness
  667. entity_set_int(entbase, EV_INT_solid, SOLID_SLIDEBOX) // SOLID_SLIDEBOX
  668. // Set movetype
  669. entity_set_int(entbase, EV_INT_movetype, MOVETYPE_TOSS) // head flies, base falls
  670.  
  671. // Set team
  672. entity_set_int(entbase, BASE_INT_TEAM, get_user_team(creator))
  673.  
  674. new parms[2]
  675. parms[0] = entbase
  676. parms[1] = creator
  677.  
  678. g_sentryOrigins[creator - 1] = origin
  679.  
  680. emit_sound(creator, CHAN_AUTO, "sentries/building.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
  681.  
  682. set_task(BASESENTRYDELAY, "createsentryhead", 0, parms, 2)
  683. g_inBuilding[creator - 1] = true
  684.  
  685. return true
  686. }
  687.  
  688. public createsentryhead(parms[2]) {
  689. new entbase = parms[0]
  690.  
  691. new creator = parms[1]
  692. if (!g_inBuilding[creator - 1]) {
  693. // g_inBuilding is reset upon new round, then don't continue with building sentry head. Remove base and return.
  694. if (is_valid_ent(entbase))
  695. remove_entity(entbase)
  696. return
  697. }
  698.  
  699. new Float:origin[3]
  700. origin = g_sentryOrigins[creator - 1]
  701.  
  702. new ent = create_entity("func_breakable")
  703. if (!ent) {
  704. if (is_valid_ent(entbase))
  705. remove_entity(entbase)
  706. return
  707. }
  708.  
  709. new Float:mins[3], Float:maxs[3]
  710. // Set true size of base... if it exists!
  711. // Also set sentry <-> base connections, if base still exists
  712. if (is_valid_ent(entbase)) {
  713. mins[0] = -16.0
  714. mins[1] = -16.0
  715. mins[2] = 0.0
  716. maxs[0] = 16.0
  717. maxs[1] = 16.0
  718. maxs[2] = 16.0
  719. entity_set_size(entbase, mins, maxs)
  720.  
  721. entity_set_edict(ent, SENTRY_ENT_BASE, entbase)
  722. entity_set_edict(entbase, BASE_ENT_SENTRY, ent)
  723. }
  724.  
  725. // Store our sentry in array
  726. g_sentries[g_sentriesNum] = ent
  727.  
  728. new healthstring[16]
  729. num_to_str(floatround(g_HEALTHS[0]), healthstring, 15)
  730. DispatchKeyValue(ent, "health", healthstring)
  731. DispatchKeyValue(ent, "material", "6")
  732.  
  733. DispatchSpawn(ent)
  734. // Change classname
  735. entity_set_string(ent, EV_SZ_classname, "sentry")
  736. // Set model
  737. entity_set_model(ent, "models/sentries/sentry1.mdl") // later set according to level
  738. // Set size
  739. mins[0] = -16.0
  740. mins[1] = -16.0
  741. mins[2] = 0.0
  742. maxs[0] = 16.0
  743. maxs[1] = 16.0
  744. maxs[2] = 48.0
  745. entity_set_size(ent, mins, maxs)
  746. //client_print(creator, print_chat, "Creating sentry %d with bounds %f", ent, BOUNDS)
  747. // Set origin
  748. entity_set_origin(ent, origin)
  749. // Set starting angle
  750. entity_get_vector(creator, EV_VEC_angles, origin)
  751. origin[0] = 0.0
  752. origin[1] += 180.0
  753. entity_set_float(ent, SENTRY_FL_ANGLE, origin[1])
  754. origin[2] = 0.0
  755. entity_set_vector(ent, EV_VEC_angles, origin)
  756. // Set solidness
  757. entity_set_int(ent, EV_INT_solid, SOLID_SLIDEBOX) // SOLID_SLIDEBOX
  758. // Set movetype
  759. entity_set_int(ent, EV_INT_movetype, MOVETYPE_TOSS) // head flies, base doesn't
  760. // Set tilt of cannon
  761. set_pev(ent, PEV_SENTRY_TILT_TURRET, 127) //entity_set_byte(ent, SENTRY_TILT_TURRET, 127) // 127 is horisontal
  762. // Tilt of rocket launcher barrels at level 3
  763. set_pev(ent, PEV_SENTRY_TILT_LAUNCHER, 127) //entity_set_byte(ent, SENTRY_TILT_LAUNCHER, 127) // 127 is horisontal
  764. // Angle of small radar at level 3
  765. entity_set_float(ent, SENTRY_FL_RADARANGLE, 127.0)
  766. set_pev(ent, PEV_SENTRY_TILT_RADAR, 127) //entity_set_byte(ent, SENTRY_TILT_RADAR, 127) // 127 is middle
  767.  
  768.  
  769. // Set owner
  770. //entity_set_edict(ent, SENTRY_ENT_OWNER, creator)
  771. SetSentryPeople(ent, OWNER, creator)
  772.  
  773. // Set team
  774. entity_set_int(ent, SENTRY_INT_TEAM, get_user_team(creator))
  775.  
  776. // Set level (not really necessary, but for looks)
  777. entity_set_int(ent, SENTRY_INT_LEVEL, SENTRY_LEVEL_1)
  778.  
  779. // Top color
  780. #if defined RANDOM_TOPCOLOR
  781. new topColor = random_num(0, 255)
  782. #else
  783. new topColor = cs_get_user_team(creator) == CS_TEAM_CT ? COLOR_TOP_CT : COLOR_TOP_T
  784. #endif
  785. // Bottom color
  786. #if defined RANDOM_BOTTOMCOLOR
  787. new bottomColor = random_num(0, 255)
  788. #else
  789. new bottomColor = cs_get_user_team(creator) == CS_TEAM_CT ? COLOR_BOTTOM_CT : COLOR_BOTTOM_T
  790. #endif
  791.  
  792. // Set color
  793. new map = topColor | (bottomColor<<8)
  794. //spambits(creator, topColor)
  795. //spambits(creator, bottomColor)
  796. //spambits(creator, map)
  797. entity_set_int(ent, EV_INT_colormap, map)
  798.  
  799. g_sentriesNum++
  800.  
  801. emit_sound(ent, CHAN_AUTO, "sentries/turrset.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
  802.  
  803. IncreaseSentryCount(creator, ent)
  804.  
  805. new parm[4]
  806. parm[0] = ent
  807. set_task(g_THINKFREQUENCIES[0], "sentry_think", TASKID_THINK + parm[0], parm, 1)
  808.  
  809. parm[1] = random_num(0, 1)
  810. parm[2] = 0
  811. parm[3] = 0
  812. new directions = (random_num(0, 1)<<SENTRY_DIR_CANNON) | (random_num(0, 1)<<SENTRY_DIR_RADAR)
  813. entity_set_int(ent, SENTRY_INT_PENDDIR, directions)
  814.  
  815. //entity_set_float(ent, SENTRY_FL_RADARDIR, random_num(0, 1) + 0.0)
  816.  
  817. g_inBuilding[creator - 1] = false
  818.  
  819. if (!is_valid_ent(entbase)) {
  820. // Someone probably destroyed the base before head was built!
  821. // Sentry should go nuts. :-P
  822. entity_set_int(ent, SENTRY_INT_FIRE, SENTRY_FIREMODE_NUTS)
  823. }
  824. }
  825.  
  826. public server_frame() {
  827. g_gameTime = get_gametime()
  828. g_deltaTime = g_gameTime - g_lastGameTime
  829. //server_print("Gametime: %f, Last gametime: %f", g_gameTime, g_lastGameTime)
  830.  
  831. new tempSentries[MAXSENTRIES], Float:angles[3]
  832.  
  833. new tempSentriesNum = 0
  834. for (new i = 0; i < g_sentriesNum; i++) {
  835. //sentry_pendulum(g_sentries[i], g_deltaTime)
  836. tempSentries[i] = g_sentries[i]
  837. tempSentriesNum++
  838. }
  839.  
  840. for (new i = 0; i < tempSentriesNum; i++) {
  841. //if (entity_get_float(tempSentries[i], EV_FL_nextthink) < g_game
  842. sentry_pendulum(tempSentries[i], g_deltaTime)
  843. if (entity_get_edict(tempSentries[i], SENTRY_ENT_SPYCAM) != 0) {
  844. entity_get_vector(tempSentries[i], EV_VEC_angles, angles)
  845. entity_set_vector(entity_get_edict(tempSentries[i], SENTRY_ENT_SPYCAM), EV_VEC_angles, angles)
  846. }
  847. }
  848.  
  849. g_lastGameTime = g_gameTime
  850. return PLUGIN_CONTINUE
  851. }
  852.  
  853. sentry_pendulum(sentry, Float:deltaTime) {
  854. switch (entity_get_int(sentry, SENTRY_INT_FIRE)) {
  855. case SENTRY_FIREMODE_NO: {
  856. new Float:angles[3]
  857. entity_get_vector(sentry, EV_VEC_angles, angles)
  858. new Float:baseAngle = entity_get_float(sentry, SENTRY_FL_ANGLE)
  859. new directions = entity_get_int(sentry, SENTRY_INT_PENDDIR)
  860. if (directions & (1<<SENTRY_DIR_CANNON)) {
  861. angles[1] -= (PENDULUM_INCREMENT * deltaTime) // PENDULUM_INCREMENT get_cvar_float("pend_inc")
  862. if (angles[1] < baseAngle - PENDULUM_MAX) {
  863. angles[1] = baseAngle - PENDULUM_MAX
  864. directions &= ~(1<<SENTRY_DIR_CANNON)
  865. entity_set_int(sentry, SENTRY_INT_PENDDIR, directions)
  866. }
  867. }
  868. else {
  869. angles[1] += (PENDULUM_INCREMENT * deltaTime) // PENDULUM_INCREMENT get_cvar_float("pend_inc")
  870. if (angles[1] > baseAngle + PENDULUM_MAX) {
  871. angles[1] = baseAngle + PENDULUM_MAX
  872. directions |= (1<<SENTRY_DIR_CANNON)
  873. entity_set_int(sentry, SENTRY_INT_PENDDIR, directions)
  874. }
  875. }
  876.  
  877. entity_set_vector(sentry, EV_VEC_angles, angles);
  878.  
  879. if (entity_get_int(sentry, SENTRY_INT_LEVEL) == SENTRY_LEVEL_3) {
  880. //new radarAngle = entity_get_byte(sentry, SENTRY_TILT_RADAR)
  881. //SENTRY_FL_RADARANGLE
  882. new Float:radarAngle = entity_get_float(sentry, SENTRY_FL_RADARANGLE)
  883.  
  884. if (directions & (1<<SENTRY_DIR_RADAR)) {
  885. radarAngle = radarAngle - RADAR_INCREMENT // get_cvar_float("radar_increment")
  886. if (radarAngle < 0.0) {
  887. radarAngle = 0.0
  888. directions &= ~(1<<SENTRY_DIR_RADAR)
  889. entity_set_int(sentry, SENTRY_INT_PENDDIR, directions)
  890. }
  891. }
  892. else {
  893. radarAngle = radarAngle + RADAR_INCREMENT // get_cvar_float("radar_increment")
  894. if (radarAngle > 255.0) {
  895. radarAngle = 255.0
  896. directions |= (1<<SENTRY_DIR_RADAR)
  897. entity_set_int(sentry, SENTRY_INT_PENDDIR, directions)
  898. }
  899. }
  900. entity_set_float(sentry, SENTRY_FL_RADARANGLE, radarAngle)
  901. set_pev(sentry, PEV_SENTRY_TILT_RADAR, floatround(radarAngle)) //entity_set_byte(sentry, SENTRY_TILT_RADAR, floatround(radarAngle))
  902. }
  903.  
  904. return
  905. }
  906. case SENTRY_FIREMODE_NUTS: {
  907. new Float:angles[3]
  908. entity_get_vector(sentry, EV_VEC_angles, angles)
  909.  
  910. new Float:spinSpeed = entity_get_float(sentry, SENTRY_FL_SPINSPEED)
  911. if (entity_get_int(sentry, SENTRY_INT_PENDDIR) & (1<<SENTRY_DIR_CANNON)) {
  912. angles[1] -= (spinSpeed * deltaTime)
  913. if (angles[1] < 0.0) {
  914. angles[1] = 360.0 + angles[1]
  915. }
  916. }
  917. else {
  918. angles[1] += (spinSpeed * deltaTime)
  919. if (angles[1] > 360.0) {
  920. angles[1] = angles[1] - 360.0
  921. }
  922. }
  923. // Increment speed raise
  924. entity_set_float(sentry, SENTRY_FL_SPINSPEED, (spinSpeed += random_float(1.0, 2.0)))
  925.  
  926. new Float:maxSpin = entity_get_float(sentry, SENTRY_FL_MAXSPIN)
  927. if (maxSpin == 0.0) {
  928. // Set rotation speed to explode at
  929. entity_set_float(sentry, SENTRY_FL_MAXSPIN, maxSpin = random_float(500.0, 750.0))
  930. //client_print(0, print_chat, "parm3 set to %d", parm[3])
  931. }
  932. else if (spinSpeed >= maxSpin) {
  933. //client_print(0, print_chat, "Detonating!")
  934. sentry_detonate(sentry, false, false)
  935. //remove_entity(parm[0])
  936. return
  937. }
  938. entity_set_vector(sentry, EV_VEC_angles, angles);
  939.  
  940. return
  941. }
  942. }
  943. }
  944.  
  945. // Checks the contents of eight points corresponding to the bbox around ent origin. Also does a trace from origin to each point. If anything goes wrong, report a hit.
  946. // TODO: high bounds should get higher, so that building in tight places not gets sentries stuck in roof... TraceCheckCollides
  947. bool:TraceCheckCollides(Float:origin[3], const Float:BOUNDS) {
  948. new Float:traceEnds[8][3], Float:traceHit[3], hitEnt
  949.  
  950. // x, z, y
  951. traceEnds[0][0] = origin[0] - BOUNDS
  952. traceEnds[0][1] = origin[1] - BOUNDS
  953. traceEnds[0][2] = origin[2] - BOUNDS
  954.  
  955. traceEnds[1][0] = origin[0] - BOUNDS
  956. traceEnds[1][1] = origin[1] - BOUNDS
  957. traceEnds[1][2] = origin[2] + BOUNDS
  958.  
  959. traceEnds[2][0] = origin[0] + BOUNDS
  960. traceEnds[2][1] = origin[1] - BOUNDS
  961. traceEnds[2][2] = origin[2] + BOUNDS
  962.  
  963. traceEnds[3][0] = origin[0] + BOUNDS
  964. traceEnds[3][1] = origin[1] - BOUNDS
  965. traceEnds[3][2] = origin[2] - BOUNDS
  966. //
  967. traceEnds[4][0] = origin[0] - BOUNDS
  968. traceEnds[4][1] = origin[1] + BOUNDS
  969. traceEnds[4][2] = origin[2] - BOUNDS
  970.  
  971. traceEnds[5][0] = origin[0] - BOUNDS
  972. traceEnds[5][1] = origin[1] + BOUNDS
  973. traceEnds[5][2] = origin[2] + BOUNDS
  974.  
  975. traceEnds[6][0] = origin[0] + BOUNDS
  976. traceEnds[6][1] = origin[1] + BOUNDS
  977. traceEnds[6][2] = origin[2] + BOUNDS
  978.  
  979. traceEnds[7][0] = origin[0] + BOUNDS
  980. traceEnds[7][1] = origin[1] + BOUNDS
  981. traceEnds[7][2] = origin[2] - BOUNDS
  982.  
  983. for (new i = 0; i < 8; i++) {
  984. if (point_contents(traceEnds[i]) != CONTENTS_EMPTY)
  985. return true
  986.  
  987. hitEnt = trace_line(0, origin, traceEnds[i], traceHit)
  988. if (hitEnt != 0)
  989. return true
  990. for (new j = 0; j < 3; j++) {
  991. if (traceEnds[i][j] != traceHit[j])
  992. return true
  993. }
  994. }
  995.  
  996. return false
  997. }
  998.  
  999.  
  1000. //#define TE_TRACER 6 // tracer effect from point to point
  1001. // coord, coord, coord (start)
  1002. // coord, coord, coord (end)
  1003.  
  1004. tracer(Float:start[3], Float:end[3]) {
  1005. //new start_[3]
  1006. new start_[3], end_[3]
  1007. FVecIVec(start, start_)
  1008. FVecIVec(end, end_)
  1009. message_begin(MSG_BROADCAST, SVC_TEMPENTITY) // MSG_PAS MSG_BROADCAST
  1010. write_byte(TE_TRACER)
  1011. write_coord(start_[0])
  1012. write_coord(start_[1])
  1013. write_coord(start_[2])
  1014. write_coord(end_[0])
  1015. write_coord(end_[1])
  1016. write_coord(end_[2])
  1017. message_end()
  1018. }
  1019.  
  1020. /*
  1021. #define TE_BREAKMODEL 108 // box of models or sprites
  1022. // coord, coord, coord (position)
  1023. // coord, coord, coord (size)
  1024. // coord, coord, coord (velocity)
  1025. // byte (random velocity in 10's)
  1026. // short (sprite or model index)
  1027. // byte (count)
  1028. // byte (life in 0.1 secs)
  1029. // byte (flags)
  1030. */
  1031.  
  1032. stock create_explosion(Float:origin_[3]) {
  1033. new origin[3]
  1034. FVecIVec(origin_, origin)
  1035. //client_print(0, print_chat, "Creating explosion at %d %d %d", origin[0], origin[1], origin[2])
  1036.  
  1037. message_begin(MSG_BROADCAST, SVC_TEMPENTITY, origin) // MSG_PAS not really good here
  1038. write_byte(TE_EXPLOSION)
  1039. write_coord(origin[0])
  1040. write_coord(origin[1])
  1041. write_coord(origin[2])
  1042. write_short(g_sModelIndexFireball)
  1043. write_byte(random_num(0, 20) + 50) // scale * 10 // random_num(0, 20) + 20
  1044. write_byte(12) // framerate
  1045. write_byte(TE_EXPLFLAG_NONE)
  1046. message_end()
  1047.  
  1048. // Blast stuff away
  1049. genericShock(origin_, SENTRYEXPLODERADIUS, "weaponbox", 32, SENTRYSHOCKPOWER, OBJECT_GENERIC)
  1050. genericShock(origin_, SENTRYEXPLODERADIUS, "armoury_entity", 32, SENTRYSHOCKPOWER, OBJECT_ARMOURY)
  1051. genericShock(origin_, SENTRYEXPLODERADIUS, "player", 32, SENTRYSHOCKPOWER, OBJECT_PLAYER)
  1052. genericShock(origin_, SENTRYEXPLODERADIUS, "grenade", 32, SENTRYSHOCKPOWER, OBJECT_GRENADE)
  1053. genericShock(origin_, SENTRYEXPLODERADIUS, "hostage_entity", 32, SENTRYSHOCKPOWER, OBJECT_GENERIC)
  1054.  
  1055. // Hurt ppl in vicinity
  1056.  
  1057. new Float:playerOrigin[3], Float:distance, Float:flDmgToDo, Float:dmgbase = DMG_EXPLOSION_TAKE + 0.0, newHealth
  1058. for (new i = 1; i <= g_MAXPLAYERS; i++) {
  1059. if (!is_user_alive(i) || get_user_godmode(i))
  1060. continue
  1061.  
  1062. entity_get_vector(i, EV_VEC_origin, playerOrigin)
  1063. distance = vector_distance(playerOrigin, origin_)
  1064. if (distance <= SENTRYEXPLODERADIUS) {
  1065. flDmgToDo = dmgbase - (dmgbase * (distance / SENTRYEXPLODERADIUS))
  1066. //client_print(i, print_chat, "flDmgToDo = %f, dmgbase = %f, distance = %f, SENTRYEXPLODERADIUS = %f", flDmgToDo, dmgbase, distance, SENTRYEXPLODERADIUS)
  1067. newHealth = get_user_health(i) - floatround(flDmgToDo)
  1068. if (newHealth <= 0) {
  1069. // Somehow if player is killed here server crashes out saying some message (Damage or Death) has not been sent yet when trying to send another message.
  1070. // By delaying death with 0.0 (huuh?) seconds this seems to be fixed.
  1071. set_task(0.0, "TicketToHell", i)
  1072. continue
  1073. }
  1074.  
  1075. set_user_health(i, newHealth)
  1076.  
  1077. message_begin(MSG_ONE_UNRELIABLE, g_msgDamage, {0,0,0}, i)
  1078. write_byte(floatround(flDmgToDo))
  1079. write_byte(floatround(flDmgToDo))
  1080. write_long(DMG_BLAST)
  1081. write_coord(origin[0])
  1082. write_coord(origin[1])
  1083. write_coord(origin[2])
  1084. message_end()
  1085. }
  1086. }
  1087. }
  1088.  
  1089. // Hacks, damn you!
  1090. public TicketToHell(player) {
  1091. if (!is_user_connected(player))
  1092. return
  1093. new frags = get_user_frags(player)
  1094. user_kill(player, 1) // don't decrease frags
  1095. new parms[4]
  1096. parms[0] = player
  1097. parms[1] = frags
  1098. parms[2] = cs_get_user_deaths(player)
  1099. parms[3] = int:cs_get_user_team(player)
  1100. set_task(0.0, "DelayedScoreInfoUpdate", 0, parms, 4)
  1101. }
  1102.  
  1103. public DelayedScoreInfoUpdate(parms[4]) {
  1104. scoreinfo_update(parms[0], parms[1], parms[2], parms[3])
  1105. }
  1106.  
  1107. stock genericShock(Float:hitPointOrigin[3], Float:radius, classString[], maxEntsToFind, Float:power, OBJECTTYPE:objecttype) { // bool:isthisplayer, bool:isthisarmouryentity, bool:isthisgrenade/*, Float:pullup*/) {
  1108. new entList[32]
  1109. if (maxEntsToFind > 32)
  1110. maxEntsToFind = 32
  1111.  
  1112. new entsFound = find_sphere_class(0, classString, radius, entList, maxEntsToFind, hitPointOrigin)
  1113.  
  1114. new Float:entOrigin[3]
  1115. new Float:velocity[3]
  1116. new Float:cOrigin[3]
  1117.  
  1118. for (new j = 0; j < entsFound; j++) {
  1119. switch (objecttype) {
  1120. case OBJECT_PLAYER: {
  1121. if (!is_user_alive(entList[j])) // Don't move dead players
  1122. continue
  1123. }
  1124. case OBJECT_GRENADE: {
  1125. new l_model[16]
  1126. entity_get_string(entList[j], EV_SZ_model, l_model, 15)
  1127. if (equal(l_model, "models/w_c4.mdl")) // don't move planted c4s :-P
  1128. continue
  1129. }
  1130. }
  1131.  
  1132. entity_get_vector(entList[j], EV_VEC_origin, entOrigin) // get_entity_origin(entList[j],entOrigin)
  1133.  
  1134. new Float:distanceNadePl = vector_distance(entOrigin, hitPointOrigin)
  1135.  
  1136. // Stuff on ground AND below explosion are "placed" a distance above explosion Y-wise ([2]), so that they fly off ground etc.
  1137. if (entity_is_on_ground(entList[j]) && entOrigin[2] < hitPointOrigin[2])
  1138. entOrigin[2] = hitPointOrigin[2] + distanceNadePl
  1139.  
  1140. entity_get_vector(entList[j], EV_VEC_velocity, velocity)
  1141.  
  1142. cOrigin[0] = (entOrigin[0] - hitPointOrigin[0]) * radius / distanceNadePl + hitPointOrigin[0]
  1143. cOrigin[1] = (entOrigin[1] - hitPointOrigin[1]) * radius / distanceNadePl + hitPointOrigin[1]
  1144. cOrigin[2] = (entOrigin[2] - hitPointOrigin[2]) * radius / distanceNadePl + hitPointOrigin[2]
  1145.  
  1146. velocity[0] += (cOrigin[0] - entOrigin[0]) * power
  1147. velocity[1] += (cOrigin[1] - entOrigin[1]) * power
  1148. velocity[2] += (cOrigin[2] - entOrigin[2]) * power
  1149.  
  1150. entity_set_vector(entList[j], EV_VEC_velocity, velocity)
  1151. }
  1152. }
  1153.  
  1154. stock entity_is_on_ground(entity) {
  1155. return entity_get_int(entity, EV_INT_flags) & FL_ONGROUND
  1156. }
  1157.  
  1158.  
  1159. public message_tempentity() {
  1160. if (get_msg_args() != 15 && get_msg_arg_int(1) != TE_BREAKMODEL)
  1161. return PLUGIN_CONTINUE
  1162.  
  1163. // Something broke, maybe it was one of our sentries. Loop through all sentries to see if any of them has health <=0.
  1164. for (new i = 0; i < g_sentriesNum; i++) {
  1165. if (entity_get_float(g_sentries[i], EV_FL_health) <= 0.0) {
  1166. //server_cmd("amx_box %d", g_sentries[i])
  1167. sentry_detonate(i, false, true)
  1168.  
  1169. //origin[0] = get_msg_arg_float(2)
  1170. //origin[1] = get_msg_arg_float(3)
  1171. //origin[2] = get_msg_arg_float(4)
  1172.  
  1173. // Rewind iteration loop; the last sentry may have been destroyed also
  1174. i--
  1175. }
  1176. }
  1177.  
  1178. return PLUGIN_CONTINUE
  1179. }
  1180.  
  1181. /*
  1182. public think_sentry(ent) {
  1183. // Hmm this place can be used to tell when a sentry breaks...
  1184.  
  1185. sentry_detonate(ent, false)
  1186. // All of these always give 0 values :-(
  1187. //client_print(0, print_chat, "%d thinks: inflictor: %d, EV_ENT_enemy: %d, EV_ENT_aiment: %d, EV_ENT_chain: %d, EV_ENT_owner: %d", ent, entity_get_edict(ent, EV_ENT_dmg_inflictor), entity_get_edict(ent, EV_ENT_enemy), entity_get_edict(ent, EV_ENT_aiment), entity_get_edict(ent, EV_ENT_chain), entity_get_edict(ent, EV_ENT_owner))
  1188.  
  1189. return PLUGIN_CONTINUE
  1190. }
  1191. */
  1192. public think_sentrybase(sentrybase) {
  1193. // Hmm this place can be used to tell when a sentrybase breaks...
  1194.  
  1195. sentrybase_broke(sentrybase)
  1196. //sentry_detonate(ent, false)
  1197.  
  1198. // All of these always give 0 values :-(
  1199. //client_print(0, print_chat, "%d thinks: inflictor: %d, EV_ENT_enemy: %d, EV_ENT_aiment: %d, EV_ENT_chain: %d, EV_ENT_owner: %d", ent, entity_get_edict(ent, EV_ENT_dmg_inflictor), entity_get_edict(ent, EV_ENT_enemy), entity_get_edict(ent, EV_ENT_aiment), entity_get_edict(ent, EV_ENT_chain), entity_get_edict(ent, EV_ENT_owner))
  1200.  
  1201. return PLUGIN_CONTINUE
  1202. }
  1203.  
  1204. sentrybase_broke(sentrybase) {
  1205. new sentry = entity_get_edict(sentrybase, BASE_ENT_SENTRY)
  1206. if (is_valid_ent(sentrybase))
  1207. remove_entity(sentrybase)
  1208.  
  1209. // Sentry could be 0 which should mean it has not been built yet. No need to do anything in that case.
  1210. if (sentry == 0)
  1211. return
  1212.  
  1213. entity_set_int(sentry, SENTRY_INT_FIRE, SENTRY_FIREMODE_NUTS)
  1214. // Set cannon tower straight, calculate tower tilt offset to angles later... entityviewhitpoint fn needs changing for this to use a custom angle vector
  1215. set_pev(sentry, PEV_SENTRY_TILT_TURRET, 127) //entity_set_byte(sentry, SENTRY_TILT_TURRET, 127)
  1216. }
  1217.  
  1218. sentry_detonate(sentry, bool:quiet, bool:isIndex) {
  1219. // Explode!
  1220. new i
  1221. if (isIndex) {
  1222. i = sentry
  1223. sentry = g_sentries[sentry]
  1224. if (!is_valid_ent(sentry))
  1225. return
  1226. }
  1227. else {
  1228. if (!is_valid_ent(sentry))
  1229. return
  1230. // Find index of this sentry
  1231. for (new j = 0; j < g_sentriesNum; j++) {
  1232. if (g_sentries[j] == sentry) {
  1233. i = j
  1234. break
  1235. }
  1236. }
  1237. }
  1238.  
  1239.  
  1240. // Kill tasks
  1241. remove_task(TASKID_THINK + sentry) // removes think
  1242. remove_task(TASKID_THINKPENDULUM + sentry) // removes think
  1243. remove_task(TASKID_SENTRYONRADAR + sentry) // in case someone's displaying this on radar
  1244.  
  1245. new owner = GetSentryPeople(sentry, OWNER)
  1246.  
  1247. // If sentry has a spycam, call the stuff to remove it now
  1248. if (entity_get_edict(sentry, SENTRY_ENT_SPYCAM) != 0) {
  1249. remove_task(TASKID_SPYCAM + owner) // remove the ongoing task...
  1250. // And call this now on our own...
  1251. new parms[3]
  1252. parms[0] = owner
  1253. parms[1] = entity_get_edict(sentry, SENTRY_ENT_SPYCAM)
  1254. parms[2] = sentry
  1255. DestroySpyCam(parms)
  1256. }
  1257.  
  1258. if (!quiet) {
  1259. #if defined EXPLODINGSENTRIES
  1260. new Float:origin[3]
  1261. entity_get_vector(sentry, EV_VEC_origin, origin)
  1262. create_explosion(origin)
  1263. #endif
  1264.  
  1265. // Report to owner that it broke
  1266. client_print(owner, print_center, "On fel robbantotta a sentry Gun!")
  1267. }
  1268. DecreaseSentryCount(owner, sentry)
  1269. //SetHasSentry(GetSentryPeople(sentry, OWNER), false)
  1270.  
  1271. // Remove base first
  1272. //server_cmd("amx_entinfo %d", ent)
  1273. if (entity_get_int(sentry, SENTRY_INT_FIRE) != SENTRY_FIREMODE_NUTS)
  1274. set_task(0.0, "delayedremovalofentity", entity_get_edict(sentry, SENTRY_ENT_BASE))
  1275. //remove_entity(entity_get_edict(g_sentries[i], SENTRY_ENT_BASE))
  1276. // Remove this entity
  1277. //server_cmd("amx_entinfo %d", ent)
  1278. set_task(0.0, "delayedremovalofentity", sentry)
  1279. //remove_entity(g_sentries[i])
  1280. // Put the last sentry in the deleted entity's place
  1281. g_sentries[i] = g_sentries[g_sentriesNum - 1]
  1282. // Lower nr of sentries
  1283. g_sentriesNum--
  1284. }
  1285.  
  1286. public delayedremovalofentity(entity) {
  1287. if (!is_valid_ent(entity)) {
  1288. //client_print(0, print_chat, "Was gonna remove %d, but it's not valid", entity)
  1289. return
  1290. }
  1291. //client_print(0, print_chat, "removing %d", entity)
  1292. remove_entity(entity)
  1293. }
  1294.  
  1295. sentry_detonate_by_owner(owner, bool:quiet = false) {
  1296.  
  1297. /*
  1298. for (new i = g_MAXPLAYERS + 1, classname[7]; i < g_MAXENTITIES; i++) {
  1299. if (!is_valid_ent(i))
  1300. continue
  1301. entity_get_string(i, EV_SZ_classname, classname, 6)
  1302. if (!equal(classname, "sentry"))
  1303. continue
  1304.  
  1305. if (entity_get_edict(i, SENTRY_ENT_OWNER) == owner) {
  1306. sentry_detonate(i, quiet)
  1307. return
  1308. }
  1309. }
  1310. */
  1311.  
  1312. for(new i = 0; i < g_sentriesNum; i++) {
  1313. if (GetSentryPeople(g_sentries[i], OWNER) == owner) {
  1314. sentry_detonate(i, quiet, true)
  1315. break
  1316. }
  1317. }
  1318. }
  1319.  
  1320. public client_disconnect(id) {
  1321. while (GetSentryCount(id) > 0)
  1322. sentry_detonate_by_owner(id)
  1323. }
  1324.  
  1325. public sentry_think(parm[1]) {
  1326. if (!is_valid_ent(parm[0])) {
  1327. client_print(0, print_chat, "%d nem ervenyes ent, veget ert!", parm[0])
  1328. return
  1329. }
  1330.  
  1331. new ent = parm[0]
  1332.  
  1333. new Float:sentryOrigin[3], Float:hitOrigin[3], hitent
  1334. entity_get_vector(ent, EV_VEC_origin, sentryOrigin)
  1335. sentryOrigin[2] += CANNONHEIGHTFROMFEET // Move up some, this should be the Y origin of the cannon
  1336.  
  1337. // If fire, do a trace and fire
  1338. new firemode = entity_get_int(ent, SENTRY_INT_FIRE)
  1339. new target = entity_get_edict(ent, SENTRY_ENT_TARGET)
  1340. if (firemode == SENTRY_FIREMODE_YES && is_valid_ent(target) && is_user_alive(target) && get_user_team(target) != entity_get_int(ent, SENTRY_INT_TEAM)) { // temp removed team check: && get_user_team(target) != entity_get_int(ent, SENTRY_INT_TEAM)
  1341. new sentryLevel = entity_get_int(ent, SENTRY_INT_LEVEL)
  1342.  
  1343. // Is target still visible?
  1344. new Float:targetOrigin[3]
  1345. entity_get_vector(target, EV_VEC_origin, targetOrigin)
  1346.  
  1347. // Adjust for ducking. This is still not 100%. :-(
  1348. if (entity_get_int(target, EV_INT_flags) & FL_DUCKING) {
  1349. //client_print(0, print_chat, "%d: Target %d is ducking, moving its origin up by %f", ent, target, TARGETUPMODIFIER)
  1350. targetOrigin[2] += TARGETUPMODIFIER
  1351. }
  1352.  
  1353. hitent = trace_line(ent, sentryOrigin, targetOrigin, hitOrigin)
  1354. if (hitent == entity_get_edict(ent, SENTRY_ENT_BASE)) {
  1355. // We traced into our base, do another trace from there
  1356. hitent = trace_line(hitent, hitOrigin, targetOrigin, hitOrigin)
  1357. //client_print(0, print_chat, "%d: I first hit my own base, and after doing another trace I hit %d, target: %d", ent, hitent, target)
  1358. }
  1359.  
  1360. if (hitent != target && is_user_alive(hitent) && entity_get_int(ent, SENTRY_INT_TEAM) != get_user_team(hitent)) {
  1361. // Another new enemy target got into scope, pick this new enemy as a new target...
  1362. target = hitent
  1363. entity_set_edict(ent, SENTRY_ENT_TARGET, hitent)
  1364. }
  1365. if (hitent == target) {
  1366. // Fire here
  1367. //client_print(0, print_chat, "%d: I see %d, will fire. Dist: %f, Hitorigin: %f %f %f", ent, hitent, dist, hitOrigin[0], hitOrigin[1], hitOrigin[2])
  1368. sentry_turntotarget(ent, sentryOrigin, target, targetOrigin)
  1369. // Firing sound
  1370. emit_sound(ent, CHAN_WEAPON, "weapons/m249-1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
  1371.  
  1372. new Float:hitRatio = random_float(0.0, 1.0) - g_HITRATIOS[sentryLevel] // ie 0.5 - 0.7 = -0.2, a hit and 0.8 - 0.7 = a miss by 0.1
  1373.  
  1374. if (!get_user_godmode(target) && hitRatio <= 0.0) {
  1375. // Do damage to player
  1376. sentry_damagetoplayer(ent, sentryLevel, sentryOrigin, target)
  1377. // Tracer effect to target
  1378. }
  1379. else {
  1380. // Tracer hitOrigin adjusted for miss...
  1381. /*
  1382. MAKE_VECTORS(pEnt->v.v_angle);
  1383. vVector = gpGlobals->v_forward * iVelocity;
  1384.  
  1385. vRet[0] = amx_ftoc(vVector.x);
  1386. vRet[1] = amx_ftoc(vVector.y);
  1387. vRet[2] = amx_ftoc(vVector.z);
  1388. */
  1389. new Float:sentryAngle[3] = {0.0, 0.0, 0.0}
  1390.  
  1391. new Float:x = hitOrigin[0] - sentryOrigin[0]
  1392. new Float:z = hitOrigin[1] - sentryOrigin[1]
  1393. new Float:radians = floatatan(z/x, radian)
  1394. sentryAngle[1] = radians * g_ONEEIGHTYTHROUGHPI
  1395. if (hitOrigin[0] < sentryOrigin[0])
  1396. sentryAngle[1] -= 180.0
  1397.  
  1398. new Float:h = hitOrigin[2] - sentryOrigin[2]
  1399. new Float:b = vector_distance(sentryOrigin, hitOrigin)
  1400. radians = floatatan(h/b, radian)
  1401. sentryAngle[0] = radians * g_ONEEIGHTYTHROUGHPI;
  1402.  
  1403. sentryAngle[0] += random_float(-10.0 * hitRatio, 10.0 * hitRatio) // aim is a little off here :-)
  1404. sentryAngle[1] += random_float(-10.0 * hitRatio, 10.0 * hitRatio) // aim is a little off here :-)
  1405. engfunc(EngFunc_MakeVectors, sentryAngle)
  1406. new Float:vector[3]
  1407. get_global_vector(GL_v_forward, vector)
  1408. for (new i = 0; i < 3; i++)
  1409. vector[i] *= 1000;
  1410.  
  1411. new Float:traceEnd[3]
  1412. for (new i = 0; i < 3; i++)
  1413. traceEnd[i] = vector[i] + sentryOrigin[i]
  1414.  
  1415. new hitEnt = ent
  1416. while((hitEnt = trace_line(hitEnt, hitOrigin, traceEnd, hitOrigin))) {
  1417. // continue tracing until hit nothing...
  1418. }
  1419.  
  1420. //for (new i = 0; i < 3; i++)
  1421. //hitOrigin[i] += random_float(-5.0, 5.0)
  1422. }
  1423. tracer(sentryOrigin, hitOrigin)
  1424.  
  1425. // Don't do any more here
  1426. //set_task(THINKFIREFREQUENCY, "sentry_think", ent)
  1427. set_task(THINKFIREFREQUENCY, "sentry_think", TASKID_THINK + parm[0], parm, 1)
  1428. return
  1429. }
  1430. else {
  1431. //client_print(target, print_chat, "%d: Lost track of you! Hit: %d", ent, target, hitent)
  1432. //client_print(0, print_chat, "%d: I can't see %d, i see %d... will not fire. Dist: %f, Hitorigin: %f %f %f", ent, entity_get_edict(ent, SENTRY_ENT_TARGET), hitent, dist, hitOrigin[0], hitOrigin[1], hitOrigin[2])
  1433. // Else target isn't still visible, unset fire state.
  1434. entity_set_int(ent, SENTRY_INT_FIRE, SENTRY_FIREMODE_NO)
  1435. // vvvv - Not really necessary but it's cleaner. Leave it out for now and be sure to set a fresh target each time SENTRY_INT_FIRE is set to 1!!!
  1436. // vvvv - Else this is breaking this think altogether! :-(
  1437. //entity_set_edict(ent, SENTRY_ENT_TARGET, 0)
  1438.  
  1439. // Don't return here, continue with searching for targets below...
  1440. }
  1441. }
  1442. else if (firemode == SENTRY_FIREMODE_NUTS) {
  1443. //client_print(0, print_chat, "Gone nuts firing... spin speed: %f", entity_get_float(ent, SENTRY_FL_SPINSPEED))
  1444. new hitEnt = entityviewhitpoint(ent, sentryOrigin, hitOrigin)
  1445. // Firing sound
  1446. emit_sound(ent, CHAN_WEAPON, "weapons/m249-1.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
  1447. // Tracer effect
  1448. tracer(sentryOrigin, hitOrigin)
  1449.  
  1450. if (is_user_connected(hitEnt) && is_user_alive(hitEnt) && !get_user_godmode(hitEnt)) {
  1451. // Do damage to player
  1452. sentry_damagetoplayer(ent, entity_get_int(ent, SENTRY_INT_LEVEL), sentryOrigin, hitEnt)
  1453. }
  1454.  
  1455. // Don't do any more here
  1456. set_task(THINKFIREFREQUENCY, "sentry_think", TASKID_THINK + parm[0], parm, 1)
  1457. return
  1458. }
  1459. else {
  1460. //client_print(0, print_chat, "My firemode: %d", firemode)
  1461. // Either wasn't meant to fire or target was not a valid entity or dead, set both to 0.
  1462. //client_print(target, print_chat, "%d: Fire: %d Target: %d (%s)", ent, entity_get_int(ent, SENTRY_INT_FIRE), target, is_valid_ent(target) ? (is_user_alive(target) ? "alive" : "dead") : "invalid")
  1463.  
  1464. //entity_set_int(ent, SENTRY_INT_FIRE, 0)
  1465. //entity_set_edict(ent, SENTRY_ENT_TARGET, 0)
  1466. }
  1467.  
  1468. // Tell what players you see
  1469. if (random_num(0, 99) < 10)
  1470. emit_sound(ent, CHAN_AUTO, "sentries/turridle.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
  1471.  
  1472. new closestTarget = 0, Float:closestDistance, Float:distance, Float:closestOrigin[3], Float:playerOrigin[3], sentryTeam = entity_get_int(ent, SENTRY_INT_TEAM)
  1473. for (new i = 1; i <= g_MAXPLAYERS; i++) {
  1474. if (!is_user_connected(i) || !is_user_alive(i) || get_user_team(i) == sentryTeam) // temporarily dont check team: || get_user_team(i) == sentryTeam
  1475. continue
  1476.  
  1477. entity_get_vector(i, EV_VEC_origin, playerOrigin)
  1478.  
  1479. // Adjust for ducking. This is still not 100%. :-(
  1480. if (entity_get_int(i, EV_INT_flags) & FL_DUCKING) {
  1481. //client_print(0, print_chat, "%d: Target %d is ducking, moving its origin up by %f", ent, target, TARGETUPMODIFIER)
  1482. playerOrigin[2] += TARGETUPMODIFIER
  1483. }
  1484.  
  1485. //playerOrigin[2] += TARGETUPMODIFIER
  1486.  
  1487. hitent = trace_line(ent, sentryOrigin, playerOrigin, hitOrigin)
  1488. if (hitent == entity_get_edict(ent, SENTRY_ENT_BASE)) {
  1489. // We traced into our base, do another trace from there
  1490. hitent = trace_line(hitent, hitOrigin, playerOrigin, hitOrigin)
  1491. //client_print(0, print_chat, "%d (scanning): I first hit my own base, and after doing another trace I hit %d, target: %d", ent, hitent, i)
  1492. }
  1493. //client_print(0, print_chat, "%d: t: %f %f %f - %f %f %f - %f %f %f, i: %d hitent: %d", ent, sentryOrigin[0], sentryOrigin[1], sentryOrigin[2], playerOrigin[0], playerOrigin[1], playerOrigin[2], hitOrigin[0], hitOrigin[1], hitOrigin[2], i, hitent)
  1494. if (hitent == i) {
  1495. //len += format(seethese[len], 63 - len, "%d,", hitent)
  1496.  
  1497. distance = vector_distance(sentryOrigin, playerOrigin)
  1498. closestOrigin = playerOrigin
  1499.  
  1500. if (distance < closestDistance || closestTarget == 0) {
  1501. closestTarget = i
  1502. closestDistance = distance
  1503. }
  1504. }
  1505. }
  1506.  
  1507. if (closestTarget) {
  1508. // We found a target, play sound and turn to target
  1509. emit_sound(ent, CHAN_AUTO, "sentries/turrspot.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
  1510. sentry_turntotarget(ent, sentryOrigin, closestTarget, closestOrigin)
  1511.  
  1512. // Set to fire mode and set target (always set also a new target when setting fire mode 1!!!)
  1513. entity_set_int(ent, SENTRY_INT_FIRE, SENTRY_FIREMODE_YES)
  1514. entity_set_edict(ent, SENTRY_ENT_TARGET, closestTarget)
  1515. // Set radar straight...
  1516. entity_set_float(ent, SENTRY_FL_RADARANGLE, 127.0)
  1517. set_pev(ent, PEV_SENTRY_TILT_RADAR, 127)
  1518. }
  1519. else
  1520. entity_set_int(ent, SENTRY_INT_FIRE, SENTRY_FIREMODE_NO)
  1521.  
  1522. //set_task(g_THINKFREQUENCIES[entity_get_int(ent, SENTRY_INT_LEVEL)], "sentry_think", ent)
  1523. //client_print(0, print_chat, "%d: my inflictor: %d, EV_ENT_enemy: %d, EV_ENT_aiment: %d, EV_ENT_chain: %d, EV_ENT_owner: %d", ent, entity_get_edict(ent, EV_ENT_dmg_inflictor), entity_get_edict(ent, EV_ENT_enemy), entity_get_edict(ent, EV_ENT_aiment), entity_get_edict(ent, EV_ENT_chain), entity_get_edict(ent, EV_ENT_owner))
  1524. set_task(g_THINKFREQUENCIES[entity_get_int(ent, SENTRY_INT_LEVEL)], "sentry_think", TASKID_THINK + parm[0], parm, 1)
  1525. }
  1526.  
  1527. stock sentry_damagetoplayer(sentry, sentryLevel, Float:sentryOrigin[3], target) {
  1528. new newHealth = get_user_health(target) - g_DMG[sentryLevel]
  1529.  
  1530. if (newHealth <= 0) {
  1531. new targetFrags = get_user_frags(target) + 1
  1532. new owner = GetSentryPeople(sentry, OWNER)
  1533. new ownerFrags = get_user_frags(owner) + 1
  1534. set_user_frags(target, targetFrags) // otherwise frags are subtracted from victim for dying (!!)
  1535. set_user_frags(owner, ownerFrags)
  1536. // Give money to player here
  1537. new contributors[3], moneyRewards[33] = {0, ...}
  1538. contributors[0] = owner
  1539. contributors[1] = GetSentryPeople(sentry, UPGRADER_1)
  1540. contributors[2] = GetSentryPeople(sentry, UPGRADER_2)
  1541. for (new i = SENTRY_LEVEL_1; i <= sentryLevel; i++) {
  1542. moneyRewards[contributors[i]] += g_SENTRYFRAGREWARDS[i]
  1543. }
  1544. for (new i = 1; i <= g_MAXPLAYERS; i++) {
  1545. if (moneyRewards[i] && is_user_connected(i))
  1546. cs_set_user_money(i, cs_get_user_money(i) + moneyRewards[i])
  1547. }
  1548.  
  1549. message_begin(MSG_ALL, g_msgDeathMsg, {0, 0, 0} ,0)
  1550. write_byte(owner)
  1551. write_byte(target)
  1552. write_byte(0)
  1553. write_string("sentry gun")
  1554. message_end()
  1555.  
  1556. scoreinfo_update(owner, ownerFrags, cs_get_user_deaths(owner), int:cs_get_user_team(owner))
  1557. //scoreinfo_update(target, targetFrags, targetDeaths, targetTeam) // dont need to update frags of victim, because it's done after set_user_health
  1558.  
  1559. set_msg_block(g_msgDeathMsg, BLOCK_ONCE)
  1560. }
  1561.  
  1562. set_user_health(target, newHealth)
  1563.  
  1564. message_begin(MSG_ONE_UNRELIABLE, g_msgDamage, {0,0,0}, target) //
  1565. write_byte(g_DMG[sentryLevel]) // write_byte(DMG_SAVE)
  1566. write_byte(g_DMG[sentryLevel])
  1567. write_long(DMG_BULLET)
  1568. write_coord(floatround(sentryOrigin[0]))
  1569. write_coord(floatround(sentryOrigin[1]))
  1570. write_coord(floatround(sentryOrigin[2]))
  1571. message_end()
  1572. }
  1573.  
  1574. scoreinfo_update(id, frags, deaths, team) {
  1575. // Send msg to update ppls scoreboards.
  1576. /*
  1577. MESSAGE_BEGIN( MSG_ALL, gmsgScoreInfo );
  1578. WRITE_BYTE( params[1] );
  1579. WRITE_SHORT( pPlayer->v.frags );
  1580. WRITE_SHORT( params[2] );
  1581. WRITE_SHORT( 0 );
  1582. WRITE_SHORT( g_pGameRules->GetTeamIndex( m_szTeamName ) + 1 );
  1583. MESSAGE_END();
  1584.  
  1585. */
  1586. message_begin(MSG_ALL, g_msgScoreInfo)
  1587. write_byte(id)
  1588. write_short(frags)
  1589. write_short(deaths)
  1590. write_short(0)
  1591. write_short(team)
  1592. message_end()
  1593. }
  1594.  
  1595. sentry_turntotarget(ent, Float:sentryOrigin[3], target, Float:closestOrigin[3]) {
  1596. if (target) {
  1597. new name[32]
  1598. get_user_name(target, name, 31)
  1599.  
  1600. // Alter ent's angle
  1601. new Float:newAngle[3]
  1602. entity_get_vector(ent, EV_VEC_angles, newAngle)
  1603. new Float:x = closestOrigin[0] - sentryOrigin[0]
  1604. new Float:z = closestOrigin[1] - sentryOrigin[1]
  1605. //new Float:y = closestOrigin[2] - sentryOrigin[2]
  1606. /*
  1607. //newAngle[0] = floatasin(x/floatsqroot(x*x+y*y), degrees)
  1608. newAngle[1] = floatasin(z/floatsqroot(x*x+z*z), degrees)
  1609. */
  1610.  
  1611. new Float:radians = floatatan(z/x, radian)
  1612. newAngle[1] = radians * g_ONEEIGHTYTHROUGHPI
  1613. if (closestOrigin[0] < sentryOrigin[0])
  1614.  
  1615. newAngle[1] -= 180.0
  1616.  
  1617. entity_set_float(ent, SENTRY_FL_ANGLE, newAngle[1])
  1618. // Tilt is handled thorugh the EV_BYTE_controller1 member. 0-255 are the values, 127ish should be horisontal aim, 255 is furthest down (about 50 degrees off)
  1619. // and 0 is also about 50 degrees up. Scope = ~100 degrees
  1620.  
  1621. // Set tilt
  1622. new Float:h = closestOrigin[2] - sentryOrigin[2]
  1623. new Float:b = vector_distance(sentryOrigin, closestOrigin)
  1624. radians = floatatan(h/b, radian)
  1625. new Float:degs = radians * g_ONEEIGHTYTHROUGHPI;
  1626. // Now adjust EV_BYTE_controller1
  1627. // Each degree corresponds to about 100/256 "bytes", = ~0,39 byte / degree (ok this is not entirely true, just tweaked for now with SENTRYTILTRADIUS)
  1628. new Float:RADIUS = SENTRYTILTRADIUS // get_cvar_float("sentry_tiltradius");
  1629. new Float:degreeByte = RADIUS/256.0; // tweak radius later
  1630. new Float:tilt = 127.0 - degreeByte * degs; // 127 is center of 256... well, almost
  1631. //client_print(GetSentryPeople(ent, OWNER), print_chat, "%d: Setting tilt to %d", ent, floatround(tilt))
  1632. set_pev(ent, PEV_SENTRY_TILT_TURRET, floatround(tilt)) //entity_set_byte(ent, SENTRY_TILT_TURRET, floatround(tilt))
  1633. entity_set_vector(ent, EV_VEC_angles, newAngle)
  1634. }
  1635. else {
  1636. //entity_set_int(ent, SENTRY_INT_FIRE, 0)
  1637. //entity_set_edict(ent, SENTRY_ENT_TARGET, 0)
  1638. //client_print(0, print_chat, "%d: I don't see anyone.", ent)
  1639. }
  1640. }
  1641.  
  1642. public menumain(id) {
  1643. if (!is_user_alive(id))
  1644. return PLUGIN_HANDLED
  1645.  
  1646. menumain_starter(id)
  1647.  
  1648. return PLUGIN_HANDLED
  1649. }
  1650.  
  1651. AimingAtSentry(id, bool:alwaysReturn = false) {
  1652. //new Float:hitOrigin[3]
  1653. //new hitEnt = userviewhitpoint(id, hitOrigin)
  1654. new hitEnt, bodyPart
  1655. //
  1656. if (get_user_aiming(id, hitEnt, bodyPart) == 0.0)
  1657. return 0
  1658.  
  1659. //if (get_user_aiming_func(id, hitEnt, bodyPart) == 0.0)
  1660. //return 0
  1661.  
  1662. new sentry = 0
  1663. while (hitEnt) {
  1664. new classname[32], l_sentry
  1665. entity_get_string(hitEnt, EV_SZ_classname, classname, 31)
  1666. if (equal(classname, "sentry_base"))
  1667. l_sentry = entity_get_edict(hitEnt, BASE_ENT_SENTRY)
  1668. else if (equal(classname, "sentry"))
  1669. l_sentry = hitEnt
  1670. else
  1671. break
  1672.  
  1673. if (alwaysReturn)
  1674. return l_sentry
  1675.  
  1676. new sentryLevel = entity_get_int(l_sentry, SENTRY_INT_LEVEL)
  1677. new owner = GetSentryPeople(l_sentry, OWNER)
  1678. if (cs_get_user_team(owner) == cs_get_user_team(id) && sentryLevel < 2) {
  1679. #if defined DISALLOW_OWN_UPGRADES
  1680. // Don't allow builder to upgrade his own sentry first time.
  1681. if (sentryLevel == SENTRY_LEVEL_1 && id == owner)
  1682. break
  1683. #endif
  1684. #if defined DISALLOW_TWO_UPGRADES
  1685. // Don't allow upgrader to upgrade again.
  1686. if (sentryLevel == SENTRY_LEVEL_2 && id == GetSentryPeople(l_sentry, UPGRADER_1))
  1687. break
  1688. #endif
  1689. sentry = l_sentry
  1690. }
  1691. break
  1692. }
  1693.  
  1694. return sentry
  1695. }
  1696.  
  1697. menumain_starter(id) {
  1698. if (g_inSpyCam[id - 1])
  1699. return
  1700.  
  1701. g_aimSentry[id - 1] = 0
  1702. new menuBuffer[256], len = 0, flags = MENUBUTTON0
  1703. len += format(menuBuffer[len], 255 - len, "\ySentry gun menu^n^n")
  1704.  
  1705. len += format(menuBuffer[len], 255 - len, "%s1. Sentry epites, $%d^n", GetSentryCount(id) < MAXPLAYERSENTRIES && cs_get_user_money(id) >= g_COST(0) ? "\w" : "\d", g_COST(0))
  1706.  
  1707. //if (GetSentryCount(id) == 1)
  1708. //g_selectedSentry[id - 1] = g_playerSentriesEdicts[id - 1][0]
  1709. //g_selectedSentry[id - 1] = GetClosestSentry(id)
  1710. if (GetSentryCount(id) > 0 && g_selectedSentry[id - 1] == -1)
  1711. g_selectedSentry[id - 1] = g_playerSentriesEdicts[id - 1][0]
  1712. // g_playerSentriesEdicts[id - 1]
  1713.  
  1714. if (g_selectedSentry[id - 1]) {
  1715. new parm[2]
  1716. parm[0] = id
  1717. parm[1] = g_selectedSentry[id - 1]
  1718. set_task(0.0, "SentryRadarBlink", TASKID_SENTRYONRADAR + g_selectedSentry[id - 1], parm, 2)
  1719. }
  1720.  
  1721. //len += format(menuBuffer[len], 255 - len, "%s2. Detonate %ssentry^n", GetSentryCount(id) > 0 ? "\w" : "\d", GetSentryCount(id) > 1 ? "closest " : "")
  1722. len += format(menuBuffer[len], 255 - len, "%s2. Felrobbantod radar alapjan^n", GetSentryCount(id) > 0 ? "\w" : "\d")
  1723.  
  1724. while (len) {
  1725. new sentry = AimingAtSentry(id)
  1726. if (!sentry)
  1727. break
  1728. new sentryLevel = entity_get_int(sentry, SENTRY_INT_LEVEL)
  1729.  
  1730. if (entity_range(sentry, id) <= MAXUPGRADERANGE) {
  1731. if (cs_get_user_money(id) >= g_COST(sentryLevel + 1)) {
  1732. len += format(menuBuffer[len], 255 - len, "\w3. Fejleszted a sentryt, $%d^n", g_COST(sentryLevel + 1))
  1733. flags |= MENUBUTTON3
  1734. g_aimSentry[id - 1] = sentry
  1735. }
  1736. else
  1737. len += format(menuBuffer[len], 255 - len, "\d3. Fejlodott a Sentry (needs $%d)^n", g_COST(sentryLevel + 1))
  1738. }
  1739. else
  1740. len += format(menuBuffer[len], 255 - len, "\d3. Fejlodott a Sentry , $%d (out of range)^n", g_COST(sentryLevel + 1))
  1741. //}
  1742.  
  1743. break
  1744. }
  1745. if (GetSentryCount(id) > 1) {
  1746. len += format(menuBuffer[len], 255 - len, "\w4. Felrobbant az osszes sentries^n")
  1747. len += format(menuBuffer[len], 255 - len, "^n\w5. Elozo kivalaszt sentry^n")
  1748. len += format(menuBuffer[len], 255 - len, "\w6. Kovetkezo kivalaszt sentry^n")
  1749. flags |= MENUBUTTON4 | MENUBUTTON5 | MENUBUTTON6
  1750. }
  1751.  
  1752. len += format(menuBuffer[len], 255 - len, "%s7. Megnezem a Sentry-ket radar^n -on", g_selectedSentry[id - 1] != -1 ? "\w" : "\d")
  1753. if (g_selectedSentry[id - 1] != -1)
  1754. flags |= MENUBUTTON7
  1755.  
  1756. //len += format(menuBuffer[len], 255 - len, "%s4. View from sentry^n", HasSentry(id) ? "\w" : "\d")
  1757.  
  1758. len += format(menuBuffer[len], 255 - len, "^n\w0. Kilepes")
  1759.  
  1760. if (GetSentryCount(id) > 0) {
  1761. flags |= MENUBUTTON2
  1762. }
  1763. if (GetSentryCount(id) < MAXPLAYERSENTRIES && cs_get_user_money(id) >= g_COST(SENTRY_LEVEL_1))
  1764. flags |= MENUBUTTON1
  1765.  
  1766. show_menu(id, flags, menuBuffer)
  1767. }
  1768.  
  1769. public SentryRadarBlink(parm[2]) {
  1770. // 0 = player
  1771. // 1 = sentry
  1772. if (!is_user_connected(parm[0]) || !is_valid_ent(parm[1]))
  1773. return
  1774.  
  1775. new Float:sentryOrigin[3]
  1776. entity_get_vector(parm[1], EV_VEC_origin, sentryOrigin)
  1777. //client_print(parm[0], print_chat, "Plotting closest sentry %d on radar: %f %f %f", parm[1], sentryOrigin[0], sentryOrigin[1], sentryOrigin[2])
  1778. message_begin(MSG_ONE, g_msgHostagePos, {0,0,0}, parm[0])
  1779. write_byte(parm[0])
  1780. write_byte(SENTRY_RADAR)
  1781. write_coord(floatround(sentryOrigin[0]))
  1782. write_coord(floatround(sentryOrigin[1]))
  1783. write_coord(floatround(sentryOrigin[2]))
  1784. message_end()
  1785.  
  1786. message_begin(MSG_ONE, g_msgHostageK, {0,0,0}, parm[0])
  1787. write_byte(SENTRY_RADAR)
  1788. message_end()
  1789.  
  1790. new usermenuid, keys
  1791. get_user_menu(parm[0], usermenuid, keys)
  1792. if (g_menuId == usermenuid)
  1793. set_task(1.5, "SentryRadarBlink", TASKID_SENTRYONRADAR + parm[1], parm, 2)
  1794. }
  1795.  
  1796. stock GetClosestSentry(id) {
  1797. // Find closest sentry
  1798. new sentry = 0, closestSentry = 0, Float:closestDistance, Float:distance
  1799. while ((sentry = find_ent_by_class(sentry, "sentry"))) {
  1800. if (GetSentryPeople(sentry, OWNER) != id)
  1801. continue
  1802.  
  1803. distance = entity_range(id, sentry)
  1804. if (distance < closestDistance || closestSentry == 0) {
  1805. closestSentry = sentry
  1806. closestDistance = distance
  1807. }
  1808. }
  1809.  
  1810. return closestSentry
  1811. }
  1812.  
  1813. public menumain_handle(id, key) {
  1814. new bool:stayInMenu = false
  1815. switch (key) {
  1816. case MENUSELECT1: {
  1817. // Build if still not has
  1818. if (GetSentryCount(id) < MAXPLAYERSENTRIES) {
  1819. sentry_build(id)
  1820. }
  1821. /*
  1822. else {
  1823. new numwords[128]
  1824. getnumbers(MAXPLAYERSENTRIES, numwords, MAXPLAYERSENTRIES)
  1825. client_print(id, print_center, "You can only build %s sentry gun!", numwords)
  1826. }
  1827. */
  1828. }
  1829. case MENUSELECT2: {
  1830. // Detonate if still has
  1831. new sentryCount = GetSentryCount(id)
  1832.  
  1833. if (sentryCount == 1)
  1834. sentry_detonate_by_owner(id)
  1835. else if (sentryCount > 1) {
  1836. sentry_detonate(g_selectedSentry[id - 1], false, false)
  1837. }
  1838. }
  1839. case MENUSELECT3: {
  1840. // Upgrade sentry
  1841. new sentry = g_aimSentry[id - 1]
  1842. if (is_valid_ent(sentry) && entity_range(sentry, id) <= MAXUPGRADERANGE) {
  1843. sentry_upgrade(id, sentry)
  1844. }
  1845. }
  1846. case MENUSELECT4: {
  1847. while(GetSentryCount(id) > 0)
  1848. sentry_detonate_by_owner(id, true)
  1849. }
  1850. case MENUSELECT5: {
  1851. // one back
  1852. CycleSelectedSentry(id, -1)
  1853. stayInMenu = true
  1854. }
  1855. case MENUSELECT6: {
  1856. // one forward
  1857. CycleSelectedSentry(id, 1)
  1858. stayInMenu = true
  1859. }
  1860. case MENUSELECT7: {
  1861. if (g_selectedSentry[id - 1] != -1) {
  1862. new spycam = CreateSpyCam(id, g_selectedSentry[id - 1])
  1863. if (!spycam)
  1864. return PLUGIN_HANDLED
  1865.  
  1866. new parms[3]
  1867. parms[0] = id
  1868. parms[1] = spycam
  1869. parms[2] = g_selectedSentry[id - 1]
  1870. set_task(SPYCAMTIME, "DestroySpyCam", TASKID_SPYCAM + id, parms, 3)
  1871. }
  1872. }
  1873. case MENUSELECT0: {
  1874. // nothing
  1875. //stayInMenu = false
  1876. }
  1877. }
  1878.  
  1879. if (stayInMenu)
  1880. menumain_starter(id)
  1881.  
  1882. return PLUGIN_HANDLED
  1883. }
  1884.  
  1885. CreateSpyCam(id, sentry) {
  1886. new spycam = create_entity("info_target")
  1887. if (!spycam)
  1888. return 0
  1889.  
  1890. // Set connection from sentry to this spycam
  1891. entity_set_edict(sentry, SENTRY_ENT_SPYCAM, spycam)
  1892.  
  1893. // Set classname
  1894. entity_set_string(spycam, EV_SZ_classname, "spycam")
  1895.  
  1896. // Set origin, pull up some
  1897. new Float:origin[3]
  1898. entity_get_vector(sentry, EV_VEC_origin, origin)
  1899. origin[2] += g_spyCamOffset[entity_get_int(sentry, SENTRY_INT_LEVEL)]
  1900. entity_set_vector(spycam, EV_VEC_origin, origin)
  1901.  
  1902. // Set model, has to have one... but make it invisible with the stuff below
  1903. entity_set_model(spycam, "models/sentries/base.mdl")
  1904. entity_set_int(spycam, EV_INT_rendermode, kRenderTransColor)
  1905. entity_set_float(spycam, EV_FL_renderamt, 0.0)
  1906. entity_set_int(spycam, EV_INT_renderfx, kRenderFxNone)
  1907.  
  1908. // Set initial angle, this is also done in server_frame
  1909. new Float:angles[3]
  1910. entity_get_vector(sentry, EV_VEC_angles, angles)
  1911. entity_set_vector(spycam, EV_VEC_angles, angles)
  1912.  
  1913. // Set view of player
  1914. engfunc(EngFunc_SetView, id, spycam)
  1915. g_inSpyCam[id - 1] = true
  1916.  
  1917. return spycam
  1918. }
  1919.  
  1920. public DestroySpyCam(parms[3]) {
  1921. new id = parms[0]
  1922. new spycam = parms[1]
  1923. new sentry = parms[2]
  1924. g_inSpyCam[id - 1] = false
  1925.  
  1926. // If user is still around, set his view back
  1927. if (is_user_connected(id))
  1928. engfunc(EngFunc_SetView, id, id)
  1929.  
  1930. // Remove connection from sentry (this sentry could've been removed because of a newround, or it was destroyed...)
  1931. if (is_valid_ent(sentry) && entity_get_edict(sentry, SENTRY_ENT_SPYCAM) == spycam)
  1932. entity_set_edict(sentry, SENTRY_ENT_SPYCAM, 0)
  1933.  
  1934. remove_entity(spycam)
  1935. }
  1936.  
  1937. CycleSelectedSentry(id, steps) {
  1938. // Find current index
  1939. new index = -1
  1940. for (new i = 0; i < g_playerSentries[id - 1]; i++) {
  1941. if (g_playerSentriesEdicts[id - 1][i] == g_selectedSentry[id - 1]) {
  1942. index = i
  1943. break
  1944. }
  1945. }
  1946. if (index == -1)
  1947. return // error :-P
  1948.  
  1949. remove_task(TASKID_SENTRYONRADAR + g_selectedSentry[id - 1])
  1950.  
  1951. if (steps > 0) {
  1952. do {
  1953. index++
  1954. steps--
  1955. if (index == g_playerSentries[id - 1])
  1956. index = 0
  1957. }
  1958. while(steps > 0)
  1959. }
  1960. else if (steps < 0) {
  1961. do {
  1962. index--
  1963. steps++
  1964. if (index == -1)
  1965. index = g_playerSentries[id - 1] - 1
  1966. }
  1967. while(steps < 0)
  1968. }
  1969.  
  1970. g_selectedSentry[id - 1] = g_playerSentriesEdicts[id - 1][index]
  1971. }
  1972.  
  1973. sentry_upgrade(id, sentry) {
  1974. new sentryLevel = entity_get_int(sentry, SENTRY_INT_LEVEL)
  1975. if (entity_get_int(sentry, SENTRY_INT_FIRE) == SENTRY_FIREMODE_NUTS) {
  1976. client_print(id, print_center, "Ezt a Sentry-t nem lehet fejleszteni!")
  1977. return
  1978. }
  1979. else if (get_user_team(id) != entity_get_int(sentry, SENTRY_INT_TEAM)) {
  1980. client_print(id, print_center, "Te fejlesztetted a csapat tarsad Sentry-et!")
  1981. return
  1982. }
  1983. #if defined DISALLOW_OWN_UPGRADES
  1984. else if (sentryLevel == SENTRY_LEVEL_1 && GetSentryPeople(sentry, OWNER) == id) {
  1985. // Don't print anything here, it could get spammy
  1986. //client_print(id, print_center, "")
  1987. return
  1988. }
  1989. #endif
  1990. #if defined DISALLOW_TWO_UPGRADES
  1991. else if (sentryLevel == SENTRY_LEVEL_2 && GetSentryPeople(sentry, UPGRADER_1) == id) {
  1992. // Don't print anything here, it could get spammy
  1993. //client_print(id, print_center, "")
  1994. return
  1995. }
  1996. #endif
  1997. sentryLevel++
  1998. new bool:newLevelIsOK = true, upgraderField
  1999. switch (sentryLevel) {
  2000. case SENTRY_LEVEL_2: {
  2001. entity_set_model(sentry, "models/sentries/sentry2.mdl")
  2002. upgraderField = UPGRADER_1
  2003. }
  2004. case SENTRY_LEVEL_3: {
  2005. entity_set_model(sentry, "models/sentries/sentry3.mdl")
  2006. upgraderField = UPGRADER_2
  2007. }
  2008. default: {
  2009. // Error... can only upgrade to level 2 and 3... so far! ;-)
  2010. newLevelIsOK = false
  2011. }
  2012. }
  2013.  
  2014. if (newLevelIsOK) {
  2015. if (cs_get_user_money(id) - g_COST(sentryLevel) < 0) {
  2016. client_print(id, print_center, "Onek nincs eleg penze, hogy frissitse az orszem fegyvert! (needed $%d)", g_COST(sentryLevel))
  2017. return
  2018. }
  2019.  
  2020. cs_set_user_money(id, cs_get_user_money(id) - g_COST(sentryLevel))
  2021.  
  2022. new Float:mins[3], Float:maxs[3]
  2023. mins[0] = -16.0
  2024. mins[1] = -16.0
  2025. mins[2] = 0.0
  2026. maxs[0] = 16.0
  2027. maxs[1] = 16.0
  2028. maxs[2] = 48.0 // 4.0
  2029. entity_set_size(sentry, mins, maxs)
  2030. emit_sound(sentry, CHAN_AUTO, "sentries/turrset.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
  2031. entity_set_int(sentry, SENTRY_INT_LEVEL, sentryLevel)
  2032. entity_set_float(sentry, EV_FL_health, g_HEALTHS[sentryLevel])
  2033. entity_set_float(entity_get_edict(sentry, SENTRY_ENT_BASE), EV_FL_health, g_HEALTHS[0])
  2034. SetSentryPeople(sentry, upgraderField, id)
  2035.  
  2036. if (id != GetSentryPeople(sentry, OWNER)) {
  2037. new upgraderName[32]
  2038. get_user_name(id, upgraderName, 31)
  2039. client_print(GetSentryPeople(sentry, OWNER), print_center, "%s fejlesztetted a fegyvert, a kovetkezo szintre: %d", upgraderName, sentryLevel + 1)
  2040. }
  2041. }
  2042. }
  2043.  
  2044. stock userviewhitpoint(index, Float:hitorigin[3]) {
  2045. if (!is_user_connected(index)) {
  2046. // Error
  2047. log_amx("ERROR in plugin - %d is not a valid player index", index)
  2048. return 0
  2049. }
  2050. new Float:origin[3], Float:pos[3], Float:v_angle[3], Float:vec[3], Float:f_dest[3]
  2051.  
  2052. entity_get_vector(index, EV_VEC_origin, origin)
  2053. entity_get_vector(index, EV_VEC_view_ofs, pos)
  2054.  
  2055. pos[0] += origin[0]
  2056. pos[1] += origin[1]
  2057. pos[2] += origin[2]
  2058.  
  2059. entity_get_vector(index, EV_VEC_v_angle, v_angle)
  2060.  
  2061. engfunc(EngFunc_AngleVectors, v_angle, vec, 0, 0)
  2062.  
  2063. f_dest[0] = pos[0] + vec[0] * 9999
  2064. f_dest[1] = pos[1] + vec[1] * 9999
  2065. f_dest[2] = pos[2] + vec[2] * 9999
  2066.  
  2067. return trace_line(index, pos, f_dest, hitorigin)
  2068. }
  2069. stock entityviewhitpoint(index, Float:origin[3], Float:hitorigin[3]) {
  2070. if (!is_valid_ent(index)) {
  2071. // Error
  2072. log_amx("ERROR in plugin - %d is not a valid entity index", index)
  2073. return 0
  2074. }
  2075. new Float:angle[3], Float:vec[3], Float:f_dest[3]
  2076.  
  2077. //entity_get_vector(index, EV_VEC_origin, origin)
  2078. /*
  2079. entity_get_vector(index, EV_VEC_view_ofs, pos)
  2080.  
  2081. pos[0] += origin[0]
  2082. pos[1] += origin[1]
  2083. pos[2] += origin[2]
  2084. */
  2085.  
  2086. entity_get_vector(index, EV_VEC_angles, angle)
  2087.  
  2088. engfunc(EngFunc_AngleVectors, angle, vec, 0, 0)
  2089.  
  2090. f_dest[0] = origin[0] + vec[0] * 9999
  2091. f_dest[1] = origin[1] + vec[1] * 9999
  2092. f_dest[2] = origin[2] + vec[2] * 9999
  2093.  
  2094. return trace_line(index, origin, f_dest, hitorigin)
  2095. }
  2096.  
  2097. public newround_event(id) {
  2098. //Shaman: Disallow building and enable it after some time
  2099. g_allowBuild= false
  2100. set_task(get_pcvar_float(sentry_wait), "enablesentrybur")
  2101.  
  2102. g_inBuilding[id - 1] = false
  2103.  
  2104. #if !defined SENTRIES_SURVIVE_ROUNDS
  2105. while(GetSentryCount(id) > 0)
  2106. sentry_detonate_by_owner(id, true)
  2107. #endif
  2108.  
  2109. if (!g_resetArmouryThisRound && g_hasArmouries) {
  2110. ResetArmoury()
  2111. g_resetArmouryThisRound = true
  2112. }
  2113.  
  2114. return PLUGIN_CONTINUE
  2115. }
  2116.  
  2117. public endround_event() {
  2118. if (!g_hasArmouries)
  2119. return PLUGIN_CONTINUE
  2120.  
  2121. set_task(4.0, "ResetArmouryFalse")
  2122.  
  2123. return PLUGIN_CONTINUE
  2124. }
  2125.  
  2126. public ResetArmouryFalse() {
  2127. //client_print(0, print_chat, "Setting g_resetArmouryThisRound to false!")
  2128. g_resetArmouryThisRound = false
  2129. }
  2130.  
  2131. public client_putinserver(id) {
  2132. if (is_user_bot(id)) {
  2133. new parm[1]
  2134. parm[0] = id
  2135. botbuildsrandomly(parm)
  2136.  
  2137. }
  2138. else
  2139. set_task(15.0, "dispInfo", id)
  2140.  
  2141. return PLUGIN_CONTINUE
  2142. }
  2143.  
  2144. public dispInfo(id)
  2145. {
  2146. client_print(id, print_chat, "Ha szeretnel orszemet epiteni akkor irbe consolba sentry_menu!")
  2147. }
  2148.  
  2149. public check_say(id){
  2150. new said[32]
  2151. read_args(said, 31)
  2152.  
  2153. if (equali(said, "^"sentryhelp^"") || equali(said, "^"/sentryhelp^"")) {
  2154. const SIZE = MAXHTMLSIZE
  2155. new msg[SIZE + 1], len = 0
  2156.  
  2157. len += format(msg[len], SIZE - len, "<html><body>")
  2158. len += format(msg[len], SIZE - len, "<p>Itt találod Sentry Gun épitéséhez valo segitseget!<br/>")
  2159. len += format(msg[len], SIZE - len, "A Sentry Gun telepiteni kell, es ugy megöli a közeledõ ellenfelet!<br/>")
  2160. len += format(msg[len], SIZE - len, "A Sentry Gun ketszer is lehet frissiteni, 2 szint van minnel nagyon annál erõsseb és jobb! úgy lehet fejleszteni hogy neki kell menni, de te kétszer nem mehet neki ezért csapat társaidnak kell!.</p>")
  2161. len += format(msg[len], SIZE - len, "<p>Nyisd meg a consolt ^"bind j sentry_menu^" ezután meg nyomod a J betüt és bejön a menü!. Lehet bindelni az építést is bind ^"sentry_build^".<br/>")
  2162. len += format(msg[len], SIZE - len, "Ne feledd hogy ajánlott be bindelni a prancsot a gyors epiteshez! ^"bind j sentry_build^".</p>")
  2163. #if defined DISALLOW_OWN_UPGRADES
  2164. len += format(msg[len], SIZE - len, "<p>Ön<b>cannot</b> Nem fejlesztheti a saját Õrszemét ezt a csapat másik tagjának kell megtenni-e!</p>")
  2165. #else
  2166. len += format(msg[len], SIZE - len, "<p>Ön <b>can</b> Egy játékos egy Õrszemet csak egyszer frissíthet!.</p>")
  2167. #endif
  2168. #if defined DISALLOW_TWO_UPGRADES
  2169. len += format(msg[len], SIZE - len, "<p></p>")
  2170. #else
  2171. len += format(msg[len], SIZE - len, "<p></p>")
  2172. #endif
  2173. len += format(msg[len], SIZE - len, "<center>")
  2174. len += format(msg[len], SIZE - len, "<table width=^"50%^" border=^"1^">")
  2175. len += format(msg[len], SIZE - len, "<tr><td><b>Parancs</b></td><td><b>Jelentes</b></td>")
  2176. len += format(msg[len], SIZE - len, "<tr><td>sentry_menu</td><td>Be hozza Sentry Gun építés és egyéb menüt!</td>")
  2177. len += format(msg[len], SIZE - len, "<tr><td>sentry_build</td><td>Meg építi éppen azon a helyen a Sentry Gun ahol vagy!</td>")
  2178. len += format(msg[len], SIZE - len, "</table>")
  2179. len += format(msg[len], SIZE - len, "<table width=^"50%^" border=^"1^">")
  2180. len += format(msg[len], SIZE - len, "<tr><td><b>Sentry gun szint</b></td><td><b>Pénz, építés/fejlesztés to</b></td>")
  2181. len += format(msg[len], SIZE - len, "<tr><td>1</td><td>%d</td>", g_COST(0))
  2182. len += format(msg[len], SIZE - len, "<tr><td>2</td><td>%d</td>", g_COST(1))
  2183. len += format(msg[len], SIZE - len, "<tr><td>3</td><td>%d</td>", g_COST(2))
  2184. len += format(msg[len], SIZE - len, "Magyarositotta:*Cyber*")
  2185. len += format(msg[len], SIZE - len, "</table>")
  2186. len += format(msg[len], SIZE - len, "</center>")
  2187. len += format(msg[len], SIZE - len, "</body></html>")
  2188. show_motd(id, msg, "Sentry guns help")
  2189. }
  2190. else if (containi(said, "sentr") != -1) {
  2191. dispInfo(id)
  2192. }
  2193.  
  2194. return PLUGIN_CONTINUE
  2195. }
  2196.  
  2197. public plugin_modules() {
  2198. require_module("engine")
  2199. require_module("fun")
  2200. require_module("cstrike")
  2201. require_module("fakemeta")
  2202. }
  2203.  
  2204. public plugin_precache() {
  2205. // Sentries below
  2206. precache_model("models/sentries/base.mdl")
  2207. precache_model("models/sentries/sentry1.mdl")
  2208. precache_model("models/sentries/sentry2.mdl")
  2209. precache_model("models/sentries/sentry3.mdl")
  2210.  
  2211. g_sModelIndexFireball = precache_model("sprites/zerogxplode.spr") // explosion
  2212.  
  2213. precache_sound("debris/bustmetal1.wav") // metal, computer breaking
  2214. precache_sound("debris/bustmetal2.wav") // metal, computer breaking
  2215. precache_sound("debris/metal1.wav") // metal breaking (needed for comp also?!)
  2216. // precache_sound("debris/metal2.wav") // metal breaking
  2217. precache_sound("debris/metal3.wav") // metal breaking (needed for comp also?!)
  2218. // precache_model("models/metalplategibs.mdl") // metal breaking
  2219. precache_model("models/computergibs.mdl") // computer breaking
  2220.  
  2221. precache_sound("sentries/asscan1.wav")
  2222. precache_sound("sentries/asscan2.wav")
  2223. precache_sound("sentries/asscan3.wav")
  2224. precache_sound("sentries/asscan4.wav")
  2225. precache_sound("sentries/turridle.wav")
  2226. precache_sound("sentries/turrset.wav")
  2227. precache_sound("sentries/turrspot.wav")
  2228. precache_sound("sentries/building.wav")
  2229.  
  2230. precache_sound("weapons/m249-1.wav")
  2231. }
  2232.  
  2233.  
  2234. stock spambits(to, bits) {
  2235. new buffer[512], len = 0
  2236. for (new i = 31; i >= 0; i--) {
  2237. len += format(buffer[len], 511 - len, "%d", bits & (1<<i) ? 1 : 0)
  2238. }
  2239. client_print(to, print_chat, buffer)
  2240. server_print(buffer)
  2241. }
  2242.  
  2243. public forward_traceline_post(Float:start[3], Float:end[3], noMonsters, player) {
  2244. if (is_user_bot(player) || player < 1 || player > g_MAXPLAYERS)
  2245. return FMRES_IGNORED
  2246.  
  2247. if (!is_user_alive(player))
  2248. return FMRES_IGNORED
  2249.  
  2250. SetStatusTrigger(player, false)
  2251.  
  2252. new hitEnt = get_tr(TR_pHit)
  2253. if (hitEnt <= g_MAXPLAYERS)
  2254. return FMRES_IGNORED
  2255.  
  2256. new classname[11], sentry = 0, base = 0
  2257. entity_get_string(hitEnt, EV_SZ_classname, classname, 10)
  2258. if (equal(classname, "sentrybase")) {
  2259. base = hitEnt
  2260. sentry = entity_get_edict(hitEnt, BASE_ENT_SENTRY)
  2261. }
  2262. else if (equal(classname, "sentry")) {
  2263. sentry = hitEnt
  2264. base = entity_get_edict(sentry, SENTRY_ENT_BASE)
  2265. }
  2266. if (!sentry || !base || entity_get_int(sentry, SENTRY_INT_FIRE) == SENTRY_FIREMODE_NUTS)
  2267. return FMRES_IGNORED
  2268. new Float:health = entity_get_float(sentry, EV_FL_health)
  2269. if (health <= 0)
  2270. return FMRES_IGNORED
  2271. new Float:basehealth = entity_get_float(base, EV_FL_health)
  2272. if (basehealth <= 0)
  2273. return FMRES_IGNORED
  2274. new team = entity_get_int(sentry, SENTRY_INT_TEAM)
  2275. if (team != get_user_team(player))
  2276. return FMRES_IGNORED
  2277.  
  2278. // Display health
  2279. new level = entity_get_int(sentry, SENTRY_INT_LEVEL)
  2280. new upgradeInfo[128]
  2281. if (PlayerCanUpgradeSentry(player, sentry))
  2282. format(upgradeInfo, 127, "^n(Run into me to upgrade me to level %d for $%d)", level + 2, g_COST(level + 1))
  2283. else if (level < SENTRY_LEVEL_3)
  2284. format(upgradeInfo, 127, "^n(Upgrade cost: $%d)", g_COST(level + 1))
  2285. else
  2286. upgradeInfo = ""
  2287.  
  2288. new tempStatusBuffer[256]
  2289.  
  2290. format(tempStatusBuffer, 255, "Elet: %d/%d^nBazis Elet: %d/%d^nSzint: %d%s", floatround(health), floatround(g_HEALTHS[level]), floatround(basehealth), floatround(g_HEALTHS[0]), level + 1, upgradeInfo)
  2291. SetStatusTrigger(player, true)
  2292. if (!task_exists(TASKID_SENTRYSTATUS + player) || !equal(tempStatusBuffer, g_sentryStatusBuffer[player - 1])) {
  2293. // may still exist if !equal was true, so we remove previous task. This happens when sentry is being fired upon, player gets enough money to upgrade or sentry
  2294. // suddenly is upgradeable because another teammate upgraded it or something. This should make for instant updates to message without risking sending a lot of messages
  2295. // just in case data updated, now we only send more often if data changed often enough.
  2296. //client_print(player, print_chat, "Starting to send: %s", tempStatusBuffer)
  2297. remove_task(TASKID_SENTRYSTATUS + player)
  2298.  
  2299. g_sentryStatusBuffer[player - 1] = tempStatusBuffer
  2300. new parms[2]
  2301. parms[0] = player
  2302. parms[1] = team
  2303. set_task(0.0, "displaysentrystatus", TASKID_SENTRYSTATUS + player, parms, 2)
  2304. }
  2305.  
  2306. return FMRES_IGNORED
  2307. }
  2308.  
  2309. // Counting level, team, money and DEFINES
  2310. bool:PlayerCanUpgradeSentry(player, sentry) {
  2311. new level = entity_get_int(sentry, SENTRY_INT_LEVEL)
  2312. switch(level) {
  2313. case SENTRY_LEVEL_1: {
  2314. #if defined DISALLOW_OWN_UPGRADES
  2315. if (player == GetSentryPeople(sentry, OWNER))
  2316. return false
  2317. #endif
  2318. return get_user_team(player) == entity_get_int(sentry, SENTRY_INT_TEAM) && cs_get_user_money(player) >= g_COST(level + 1)
  2319. }
  2320. case SENTRY_LEVEL_2: {
  2321. #if defined DISALLOW_TWO_UPGRADES
  2322. if (player == GetSentryPeople(sentry, UPGRADER_1))
  2323. return false
  2324. #endif
  2325. return get_user_team(player) == entity_get_int(sentry, SENTRY_INT_TEAM) && cs_get_user_money(player) >= g_COST(level + 1)
  2326. }
  2327. }
  2328.  
  2329. return false
  2330. }
  2331.  
  2332. public displaysentrystatus(parms[2]) {
  2333. // parm 0 = player
  2334. // parm 1 = team
  2335. if (!GetStatusTrigger(parms[0]))
  2336. return
  2337.  
  2338. set_hudmessage(parms[1] == 1 ? 150 : 0, 0, parms[1] == 2 ? 150 : 0, -1.0, 0.35, 0, 0.0, STATUSINFOTIME + 0.1, 0.0, 0.0, 2) // STATUSINFOTIME + 0.1 = overlapping a little..
  2339. show_hudmessage(parms[0], g_sentryStatusBuffer[parms[0] - 1])
  2340.  
  2341. set_task(STATUSINFOTIME, "displaysentrystatus", TASKID_SENTRYSTATUS + parms[0], parms, 2)
  2342. }
  2343.  
  2344.  
  2345. ResetArmoury() {
  2346. // Find all armoury_entity:s, restore their initial origins
  2347. new entity = 0, Float:NULLVELOCITY[3] = {0.0, 0.0, 0.0}, Float:origin[3]
  2348. while ((entity = find_ent_by_class(entity, "armoury_entity"))) {
  2349. // Reset speed in case it's flying around...
  2350. entity_set_vector(entity, EV_VEC_velocity, NULLVELOCITY)
  2351.  
  2352. // Get origin and set it.
  2353. entity_get_vector(entity, EV_VEC_vuser1, origin)
  2354. entity_set_origin(entity, origin)
  2355. }
  2356. }
  2357.  
  2358. public InitArmoury() {
  2359. // Find all armoury_entity:s, store their initial origins
  2360. new entity = 0, Float:origin[3], counter = 0
  2361. while ((entity = find_ent_by_class(entity, "armoury_entity"))) {
  2362. entity_get_vector(entity, EV_VEC_origin, origin)
  2363. entity_set_vector(entity, EV_VEC_vuser1, origin)
  2364. counter++
  2365. }
  2366. if (counter > 0)
  2367. g_hasArmouries = true
  2368. }
  2369.  
  2370. stock getnumbers(number, wordnumbers[], length) {
  2371. if (number < 0) {
  2372. format(wordnumbers, length, "error")
  2373. return
  2374. }
  2375.  
  2376. new numberstr[20]
  2377. num_to_str(number, numberstr, 19)
  2378. new stlen = strlen(numberstr), bool:getzero = false, bool:jumpnext = false
  2379. if (stlen == 1)
  2380. getzero = true
  2381.  
  2382. do {
  2383. if (jumpnext)
  2384. jumpnext = false
  2385. else if (numberstr[0] != '0') {
  2386. switch (stlen) {
  2387. case 9: {
  2388. if (getsingledigit(numberstr[0], wordnumbers, length))
  2389. format(wordnumbers, length, "%s hundred%s", wordnumbers, numberstr[1] == '0' && numberstr[2] == '0' ? " million" : "")
  2390. }
  2391. case 8: {
  2392. jumpnext = gettens(wordnumbers, length, numberstr)
  2393. if (jumpnext)
  2394. format(wordnumbers, length, "%s million", wordnumbers)
  2395. }
  2396. case 7: {
  2397. getsingledigit(numberstr[0], wordnumbers, length)
  2398. format(wordnumbers, length, "%s million", wordnumbers)
  2399. }
  2400. case 6: {
  2401. if (getsingledigit(numberstr[0], wordnumbers, length))
  2402. format(wordnumbers, length, "%s hundred%s", wordnumbers, numberstr[1] == '0' && numberstr[2] == '0' ? " thousand" : "")
  2403. }
  2404. case 5: {
  2405. jumpnext = gettens(wordnumbers, length, numberstr)
  2406. if (numberstr[0] == '1' || numberstr[1] == '0')
  2407. format(wordnumbers, length, "%s thousand", wordnumbers)
  2408. }
  2409. case 4: {
  2410. getsingledigit(numberstr[0], wordnumbers, length)
  2411. format(wordnumbers, length, "%s thousand", wordnumbers)
  2412. }
  2413. case 3: {
  2414. getsingledigit(numberstr[0], wordnumbers, length)
  2415. format(wordnumbers, length, "%s hundred", wordnumbers)
  2416. }
  2417. case 2: jumpnext = gettens(wordnumbers, length, numberstr)
  2418. case 1: {
  2419. getsingledigit(numberstr[0], wordnumbers, length, getzero)
  2420. break // could've trimmed, but of no use here
  2421. }
  2422. default: {
  2423. format(wordnumbers, length, "%s TOO LONG", wordnumbers)
  2424. break
  2425. }
  2426. }
  2427. }
  2428.  
  2429. jghg_trim(numberstr, length, 1)
  2430. stlen = strlen(numberstr)
  2431. }
  2432. while (stlen > 0)
  2433.  
  2434. // Trim a char from left if first char is a space (very likely)
  2435. if (wordnumbers[0] == ' ')
  2436. jghg_trim(wordnumbers, length, 1)
  2437. }
  2438.  
  2439. // Returns true if next char should be jumped
  2440. stock bool:gettens(wordnumbers[], length, numberstr[]) {
  2441. new digitstr[11], bool:dont = false, bool:jumpnext = false
  2442. switch (numberstr[0]) {
  2443. case '1': {
  2444. jumpnext = true
  2445. switch (numberstr[1]) {
  2446. case '0': digitstr = "ten"
  2447. case '1': digitstr = "eleven"
  2448. case '2': digitstr = "twelve"
  2449. case '3': digitstr = "thirteen"
  2450. case '4': digitstr = "fourteen"
  2451. case '5': digitstr = "fifteen"
  2452. case '6': digitstr = "sixteen"
  2453. case '7': digitstr = "seventeen"
  2454. case '8': digitstr = "eighteen"
  2455. case '9': digitstr = "nineteen"
  2456. default: digitstr = "TEENSERROR"
  2457. }
  2458. }
  2459. case '2': digitstr = "twenty"
  2460. case '3': digitstr = "thirty"
  2461. case '4': digitstr = "fourty"
  2462. case '5': digitstr = "fifty"
  2463. case '6': digitstr = "sixty"
  2464. case '7': digitstr = "seventy"
  2465. case '8': digitstr = "eighty"
  2466. case '9': digitstr = "ninety"
  2467. case '0': dont = true // do nothing
  2468. default : digitstr = "TENSERROR"
  2469. }
  2470. if (!dont)
  2471. format(wordnumbers, length, "%s %s", wordnumbers, digitstr)
  2472.  
  2473. return jumpnext
  2474. }
  2475.  
  2476. // Returns true when sets, else false
  2477. stock getsingledigit(digit[], numbers[], length, bool:getzero = false) {
  2478. new digitstr[11]
  2479. switch (digit[0]) {
  2480. case '1': digitstr = "one"
  2481. case '2': digitstr = "two"
  2482. case '3': digitstr = "three"
  2483. case '4': digitstr = "four"
  2484. case '5': digitstr = "five"
  2485. case '6': digitstr = "six"
  2486. case '7': digitstr = "seven"
  2487. case '8': digitstr = "eight"
  2488. case '9': digitstr = "nine"
  2489. case '0': {
  2490. if (getzero)
  2491. digitstr = "zero"
  2492. else
  2493. return false
  2494. }
  2495. default : digitstr = "digiterror"
  2496. }
  2497. format(numbers, length, "%s %s", numbers, digitstr)
  2498.  
  2499. return true
  2500. }
  2501.  
  2502. stock jghg_trim(stringtotrim[], len, charstotrim, bool:fromleft = true) {
  2503. if (charstotrim <= 0)
  2504. return
  2505.  
  2506. if (fromleft) {
  2507. new maxlen = strlen(stringtotrim)
  2508. if (charstotrim > maxlen)
  2509. charstotrim = maxlen
  2510.  
  2511. format(stringtotrim, len, "%s", stringtotrim[charstotrim])
  2512. }
  2513. else {
  2514. new maxlen = strlen(stringtotrim) - charstotrim
  2515. if (maxlen < 0)
  2516. maxlen = 0
  2517.  
  2518. format(stringtotrim, maxlen, "%s", stringtotrim)
  2519. }
  2520. }
  2521.  
  2522.  
  2523. BotBuild(bot, Float:closestTime = 0.1, Float:longestTime = 5.0) {
  2524. // This function should only be used to build sentries at objective related targets.
  2525. // So as to not try to build all the time if recently started a build task when touched a objective related target
  2526. if (task_exists(bot))
  2527. return
  2528.  
  2529. new teamSentriesNear = GetStuffInVicinity(bot, BOT_MAXSENTRIESDISTANCE, true, "sentry") + GetStuffInVicinity(bot, BOT_MAXSENTRIESDISTANCE, true, "sentrybase")
  2530. if (teamSentriesNear >= BOT_MAXSENTRIESNEAR) {
  2531. new name[32]
  2532. get_user_name(bot, name, 31)
  2533. //client_print(0, print_chat, "There are already %d sentries near me, I won't build here, %s says. (objective)", teamSentriesNear, name)
  2534. return
  2535. }
  2536.  
  2537. new Float:ltime = random_float(closestTime, longestTime)
  2538. set_task(ltime, "sentry_build", bot)
  2539. //server_print("Bot task %d set to %f seconds", bot, ltime)
  2540.  
  2541. /*new tempname[32]
  2542. get_user_name(bot, tempname, 31)
  2543. client_print(0, print_chat, "Bot %s will build a sentry in %f seconds...", tempname, ltime)*/
  2544. }
  2545. public sentry_build_randomlybybot(taskid_and_id) {
  2546. //Shaman: Check if the player is allowed to build
  2547. if(!g_allowBuild)
  2548. return
  2549.  
  2550. if (!is_user_alive(taskid_and_id - TASKID_BOTBUILDRANDOMLY))
  2551. return
  2552.  
  2553. // Now finally do a short check if there already are enough (2-3 sentries) in this vicinity... then don't build.
  2554. new teamSentriesNear = GetStuffInVicinity(taskid_and_id - TASKID_BOTBUILDRANDOMLY, BOT_MAXSENTRIESDISTANCE, true, "sentry") + GetStuffInVicinity(taskid_and_id - TASKID_BOTBUILDRANDOMLY, BOT_MAXSENTRIESDISTANCE, true, "sentrybase")
  2555. if (teamSentriesNear >= BOT_MAXSENTRIESNEAR) {
  2556. //new name[32]
  2557. //get_user_name(taskid_and_id - TASKID_BOTBUILDRANDOMLY, name, 31)
  2558. //client_print(0, print_chat, "There are already %d sentries near me, I won't build here, %s says. (random)", teamSentriesNear, name)
  2559. return
  2560. }
  2561.  
  2562. sentry_build(taskid_and_id - TASKID_BOTBUILDRANDOMLY)
  2563. }
  2564.  
  2565. GetStuffInVicinity(entity, const Float:RADIUS, bool:followTeam, STUFF[]) {
  2566. new classname[32], sentryTeam, nrOfStuffNear = 0
  2567. entity_get_string(entity, EV_SZ_classname, classname, 31)
  2568. if (followTeam) {
  2569. if (equal(classname, "player"))
  2570. sentryTeam = get_user_team(entity)
  2571. else if (equal(classname, "sentry"))
  2572. sentryTeam = entity_get_int(entity, SENTRY_INT_TEAM)
  2573. }
  2574.  
  2575. if (followTeam) {
  2576. if (equal(STUFF, "sentry")) {
  2577. for (new i = 0; i < g_sentriesNum; i++) {
  2578. if (g_sentries[i] == entity || (followTeam && entity_get_int(g_sentries[i], SENTRY_INT_TEAM) != sentryTeam) || entity_range(g_sentries[i], entity) > RADIUS)
  2579. continue
  2580.  
  2581. nrOfStuffNear++
  2582. }
  2583. }
  2584. else if (equal(STUFF, "sentrybase")) {
  2585. new ent = 0
  2586. while ((ent = find_ent_by_class(ent, STUFF))) {
  2587. // Don't count if:
  2588. // If follow team then if team is not same
  2589. // If ent is the same as what we're searching from, which is entity
  2590. // Don't count a base if it has a head, we consider sentry+base only as one item (a sentry)
  2591. // Or if out of range
  2592. if ((followTeam && entity_get_int(ent, BASE_INT_TEAM) != sentryTeam)
  2593. || ent == entity
  2594. || entity_get_edict(ent, BASE_ENT_SENTRY) != 0
  2595. || entity_range(ent, entity) > RADIUS)
  2596. continue
  2597.  
  2598. nrOfStuffNear++
  2599. }
  2600. }
  2601. }
  2602.  
  2603. //client_print(0, print_chat, "Found %d sentries within %f distance of entity %d...", nrOfSentriesNear, RADIUS, entity)
  2604. return nrOfStuffNear
  2605. }
  2606.  
  2607. BotBuildRandomly(bot, Float:closestTime = 0.1, Float:longestTime = 5.0) {
  2608. // This function is used to stark tasks that will build sentries randomly regardless of map objectives and its targets.
  2609. new Float:ltime = random_float(closestTime, longestTime)
  2610. set_task(ltime, "sentry_build_randomlybybot", TASKID_BOTBUILDRANDOMLY + bot)
  2611.  
  2612. new tempname[32]
  2613. get_user_name(bot, tempname, 31)
  2614. //client_print(0, print_chat, "Bot %s will build a random sentry in %f seconds...", tempname, ltime)
  2615. //server_print("Bot %s will build a random sentry in %f seconds...", tempname, ltime)
  2616. }
  2617.  
  2618. public playerreachedtarget(target, bot) {
  2619. if (!is_user_bot(bot) || GetSentryCount(bot) >= MAXPLAYERSENTRIES || entity_get_int(bot, EV_INT_bInDuck) || cs_get_user_vip(bot) || get_systime() < g_lastObjectiveBuild[bot - 1] + BOT_OBJECTIVEWAIT)
  2620. return PLUGIN_CONTINUE
  2621.  
  2622. //client_print(bot, print_chat, "You touched bombtarget %d!", bombtarget)
  2623. BotBuild(bot)
  2624. g_lastObjectiveBuild[bot - 1] = get_systime()
  2625.  
  2626. return PLUGIN_CONTINUE
  2627. }
  2628.  
  2629. public playertouchedweaponbox(weaponbox, bot) {
  2630. if (!is_user_bot(bot) || GetSentryCount(bot) >= MAXPLAYERSENTRIES || cs_get_user_team(bot) != CS_TEAM_CT)
  2631. return PLUGIN_CONTINUE
  2632.  
  2633. new model[22]
  2634. entity_get_string(weaponbox, EV_SZ_model, model, 21)
  2635. if (!equal(model, "models/w_backpack.mdl"))
  2636. return PLUGIN_CONTINUE
  2637.  
  2638. // A ct will build near a dropped bomb
  2639. BotBuild(bot, 0.0, 2.0)
  2640.  
  2641. return PLUGIN_CONTINUE
  2642. }
  2643.  
  2644. public playerreachedhostagerescue(target, bot) {
  2645. if (!is_user_bot(bot) || GetSentryCount(bot) >= MAXPLAYERSENTRIES) // || cs_get_user_team(bot) != CS_TEAM_T
  2646. return PLUGIN_CONTINUE
  2647.  
  2648. // ~5% chance that a ct will build a sentry here, a t always builds
  2649. if (cs_get_user_team(bot) == CS_TEAM_CT) {
  2650. if (random_num(0, 99) < 95)
  2651. return PLUGIN_CONTINUE
  2652. }
  2653.  
  2654. BotBuild(bot)
  2655.  
  2656. //client_print(bot, print_chat, "You touched bombtarget %d!", bombtarget)
  2657.  
  2658. return PLUGIN_CONTINUE
  2659. }
  2660.  
  2661. public playertouchedhostage(hostage, bot) {
  2662. if (!is_user_bot(bot) || GetSentryCount(bot) >= MAXPLAYERSENTRIES || cs_get_user_team(bot) != CS_TEAM_T)
  2663. return PLUGIN_CONTINUE
  2664.  
  2665. // Build a sentry close to a hostage
  2666. BotBuild(bot)
  2667.  
  2668. //client_print(bot, print_chat, "You touched bombtarget %d!", bombtarget)
  2669.  
  2670. return PLUGIN_CONTINUE
  2671. }
  2672.  
  2673.  
  2674. public playertouchedsentry(sentry, player) {
  2675. if (PlayerCanUpgradeSentry(player, sentry))
  2676. sentry_upgrade(player, sentry)
  2677.  
  2678. //client_print(bot, print_chat, "You touched a sentry %d!", sentry)
  2679.  
  2680. return PLUGIN_CONTINUE
  2681. }
  2682.  
  2683. public botbuildsrandomly(parm[1]) {
  2684. if (!is_user_connected(parm[0])) {
  2685. //server_print("********* %d is no longer in server!", parm[0])
  2686. return
  2687. }
  2688.  
  2689. new Float:ltime = random_float(BOT_WAITTIME_MIN, BOT_WAITTIME_MAX)
  2690. new Float:ltime2 = ltime + random_float(BOT_NEXT_MIN, BOT_NEXT_MAX)
  2691. BotBuildRandomly(parm[0], ltime, ltime2)
  2692.  
  2693. set_task(ltime2, "botbuildsrandomly", 0, parm, 1)
  2694. }
  2695.  
  2696. #if defined DEBUG
  2697. public botbuild_fn(id, level, cid) {
  2698. if (!cmd_access(id, level, cid, 1))
  2699. return PLUGIN_HANDLED
  2700.  
  2701. new asked = 0
  2702. for(new i = 1; i <= g_MAXPLAYERS; i++) {
  2703. if (!is_user_connected(i) || !is_user_bot(i) || !is_user_alive(i))
  2704. continue
  2705.  
  2706. sentry_build(i)
  2707. asked++
  2708. }
  2709. console_print(id, "Ismetelt %d bots Sentry -ket epiteni (Nem szamolja a penzt!)", asked)
  2710.  
  2711. return PLUGIN_HANDLED
  2712. }
  2713. #endif
  2714.  
  2715. g_COST(i)
  2716. {
  2717. switch(i)
  2718. {
  2719. case 0: return COST_INIT
  2720. case 1: return COST_UP
  2721. case 2: return COST_UPTWO
  2722. }
  2723. return 0;
  2724. }
  2725.  
  2726. public plugin_init() {
  2727. register_plugin(PLUGINNAME, VERSION, AUTHOR)
  2728.  
  2729. register_clcmd("sentry_build", "createsentryhere", 0, "- build a sentry gun where you are")
  2730. register_clcmd("sentry_menu", "menumain", 0, "- displays Sentry gun menu")
  2731. register_clcmd("say", "check_say")
  2732. register_clcmd("say_team", "check_say")
  2733.  
  2734. #if defined DEBUG
  2735. register_concmd("0botbuild", "botbuild_fn", ADMIN_CFG, "- force bots to build right where they are (debug)")
  2736. #endif
  2737.  
  2738. //register_cvar("pend_inc", "30")
  2739. //register_cvar("radar_increment", "4.56")
  2740. //register_cvar("spycamoffset", "24.0")
  2741.  
  2742. sentry_max = register_cvar("sentry_max", "2");
  2743. sentry_cost1 = register_cvar("sentry_cost1", "1000");
  2744. sentry_cost2 = register_cvar("sentry_cost2", "500");
  2745. sentry_cost3 = register_cvar("sentry_cost3", "250");
  2746. sentry_team = register_cvar("sentry_team", "0");
  2747. sentry_wait = register_cvar("sentry_wait", "10");
  2748.  
  2749. register_event("ResetHUD", "newround_event", "b")
  2750. register_event("SendAudio", "endround_event", "a", "2&%!MRAD_terwin", "2&%!MRAD_ctwin", "2&%!MRAD_rounddraw")
  2751. register_event("TextMsg", "endround_event", "a", "2&#Game_C", "2&#Game_w")
  2752. register_event("TextMsg", "endround_event", "a", "2&#Game_will_restart_in")
  2753. //Shaman: For destroying sentries after team change
  2754. register_event( "TeamInfo", "jointeam_event", "a")
  2755.  
  2756. register_forward(FM_TraceLine, "forward_traceline_post", 1)
  2757.  
  2758. //new bool:foundSomething = false
  2759. if (find_ent_by_class(0, "func_bomb_target")) {
  2760. register_touch("func_bomb_target", "player", "playerreachedtarget")
  2761. register_touch("weaponbox", "player", "playertouchedweaponbox")
  2762. //foundSomething = true
  2763. }
  2764. if (find_ent_by_class(0, "func_hostage_rescue")) {
  2765. register_touch("func_hostage_rescue", "player", "playerreachedhostagerescue")
  2766. //foundSomething = true
  2767. }
  2768. if (find_ent_by_class(0, "func_vip_safetyzone")) {
  2769. register_touch("func_vip_safetyzone", "player", "playerreachedtarget")
  2770. //foundSomething = true
  2771. }
  2772. if (find_ent_by_class(0, "hostage_entity")) {
  2773. register_touch("hostage_entity", "player", "playertouchedhostage")
  2774. //foundSomething = true
  2775. }
  2776.  
  2777. register_touch("sentry", "player", "playertouchedsentry")
  2778.  
  2779. g_menuId = register_menuid("\ySentry gun menu")
  2780. register_menucmd(g_menuId, 1023, "menumain_handle")
  2781.  
  2782. register_message(23, "message_tempentity") // <-- works for 0.16 as well
  2783. //register_think("sentry", "think_sentry") // <-- only 0.20+ can do this
  2784. register_think("sentrybase", "think_sentrybase")
  2785.  
  2786. g_msgDamage = get_user_msgid("Damage")
  2787. g_msgDeathMsg = get_user_msgid("DeathMsg")
  2788. g_msgScoreInfo = get_user_msgid("ScoreInfo")
  2789. g_msgHostagePos = get_user_msgid("HostagePos")
  2790. g_msgHostageK = get_user_msgid("HostageK")
  2791.  
  2792. g_MAXPLAYERS = get_global_int(GL_maxClients)
  2793. //g_MAXENTITIES = get_global_int(GL_maxEntities)
  2794. g_ONEEIGHTYTHROUGHPI = 180.0 / PI
  2795.  
  2796. // Add menu item to menufront
  2797. #if defined AddMenuItem
  2798. AddMenuItem("Sentry guns", "sentry_menu", ADMIN_CFG, PLUGINNAME)
  2799. #endif
  2800.  
  2801. // InitArmoury saves the location of all onground weapons. Later we restore them to these origins when a newround begin.
  2802. set_task(5.0, "InitArmoury")
  2803. }
  2804.