· 9 years ago · Jan 17, 2017, 02:52 PM
1
2#include <amxmodx>
3#include <amxmisc>
4
5#include <cstrike>
6
7#include <engine>
8#include <hamsandwich>
9#include <fakemeta>
10#include <fakemeta_util>
11
12#include <xs>
13
14#include <sqlx>
15
16#define CanUseAlive 0
17
18new const Plugin[] = "Peeping Tom - Jack86"
19new const Author[] = "joaquimandrade"
20new const Version[] = "2.0"
21
22new SpritesPath[CsTeams][] = {"","sprites/peeping_tom/t.spr","sprites/peeping_tom/ct.spr",""}
23new SpritesCachedIDs[CsTeams]
24
25const MaxSlots = 32
26
27new bool:OnFirstPersonView[MaxSlots+1]
28new HasPermissions[MaxSlots+1]
29
30new SpectatingUser[MaxSlots+1]
31
32const PermissionFlag = ADMIN_BAN
33
34enum _:Vector
35{
36 X,
37 Y,
38 Z
39}
40
41enum Individual
42{
43 Spectated,
44 Viewed
45}
46
47enum OriginOffset
48{
49 FrameSide,
50 FrameTop,
51 FrameBottom,
52}
53
54enum FramePoint
55{
56 TopLeft,
57 TopRight,
58 BottomLeft,
59 BottomRight
60}
61
62new Float:OriginOffsets[OriginOffset] = {_:13.0,_:25.0,_:36.0}
63
64new Float:ScaleMultiplier = 0.013;
65new Float:ScaleLower = 0.005
66
67new Float:SomeNonZeroValue = 1.0
68
69new EntitiesOwner
70
71new MaxPlayers
72
73enum StateViewOption
74{
75 StateViewSpec,
76#if CanUseAlive
77 StateViewAlways,
78#endif
79 StateViewDisabled
80
81}
82
83enum TeamViewOption
84{
85 TeamViewEnemies,
86 TeamViewEverybody
87}
88
89enum _:Option
90{
91 OptionStateView,
92 OptionTeamView
93}
94
95new OptionValuesLabels[Option][][] =
96{
97 {
98 "Spectator",
99#if CanUseAlive
100 "Always",
101#endif
102 "Disabled"
103 },
104
105 {
106 "Enemies",
107 "Everybody"
108#if CanUseAlive
109 ,""
110#endif
111 }
112}
113
114new OptionLabels[][] =
115{
116 "State",
117 "View"
118}
119
120new OptionsLen[Option] = {_:StateViewOption,_:TeamViewOption}
121
122new UserOptions[MaxSlots+1][Option]
123
124new DatabaseName[] = "peepingTom"
125new TableName[] = "users"
126
127new TableCreateQuery[] =
128{
129 " \
130 CREATE TABLE `%s` \
131 ( \
132 `SteamID` VARCHAR(34) NOT NULL, \
133 `StateViewOption` INTEGER NOT NULL, \
134 `TeamViewOption` INTEGER NOT NULL, \
135 PRIMARY KEY(`SteamID`) \
136 ) \
137 "
138}
139
140new DatabaseError[511]
141
142new Handle:DatabaseInfoTuple
143
144new Array:SteamIDsList
145new Trie:SteamIDToListID
146new Array:PlayerOptionsOriginal
147new Array:PlayerOptionsFinal
148
149new PlayerListID[MaxSlots+1]
150
151#if !CanUseAlive
152new ForwardAddToFullPack
153new OnFirstPersonViewN
154#endif
155
156public plugin_precache()
157{
158 for(new CsTeams:i=CS_TEAM_T;i<=CS_TEAM_CT;i++)
159 SpritesCachedIDs[i] = precache_model(SpritesPath[i])
160}
161
162public plugin_init()
163{
164 register_plugin(Plugin,Version,Author)
165
166 register_event("TextMsg","specMode","b","2&#Spec_Mode")
167 register_event("StatusValue","specTarget","bd","1=2")
168 register_event("SpecHealth2","specTarget","bd")
169
170 RegisterHam(Ham_Spawn,"player","playerSpawn",1)
171
172 register_clcmd("peepingTom","peepingTom",PermissionFlag)
173
174 register_cvar("peepingTom_version",Version,FCVAR_SERVER|FCVAR_SPONLY);
175
176 #if CanUseAlive
177 register_forward(FM_AddToFullPack,"addToFullPackPost",1)
178 #endif
179}
180
181public plugin_cfg()
182{
183 EntitiesOwner = create_entity("info_target")
184
185 MaxPlayers = get_maxplayers()
186
187 for(new id=1;id<=MaxPlayers;id++)
188 createSprite(id,EntitiesOwner)
189
190 initializeDatabase()
191
192 if(!databaseLayoutCreated())
193 {
194 createDatabaseLayout()
195 }
196
197 SteamIDsList = ArrayCreate(34)
198 SteamIDToListID = TrieCreate()
199 PlayerOptionsOriginal = ArrayCreate(_:Option)
200 PlayerOptionsFinal = ArrayCreate(_:Option)
201}
202
203initializeDatabase()
204{
205 SQL_SetAffinity("sqlite")
206 DatabaseInfoTuple = SQL_MakeDbTuple("","","",DatabaseName)
207}
208
209databaseLayoutCreated()
210{
211 new Handle:connection = getDatabaseConnection()
212
213 new bool:exists = sqlite_TableExists(connection,TableName)
214
215 SQL_FreeHandle(connection)
216
217 return exists
218}
219
220createDatabaseLayout()
221{
222 new Handle:connection = getDatabaseConnection()
223
224 new Handle:query = SQL_PrepareQuery(connection,TableCreateQuery,TableName)
225
226 if(!SQL_Execute(query))
227 {
228 SQL_QueryError(query,DatabaseError,charsmax(DatabaseError))
229 set_fail_state(DatabaseError)
230 }
231
232 SQL_FreeHandle(query)
233
234 SQL_FreeHandle(connection)
235}
236
237Handle:getDatabaseConnection()
238{
239 static errorCode
240 static Handle:connection
241
242 connection = SQL_Connect(DatabaseInfoTuple,errorCode,DatabaseError,charsmax(DatabaseError))
243
244 if(connection == Empty_Handle)
245 {
246 set_fail_state(DatabaseError)
247 }
248
249 return connection
250}
251
252public createSprite(aiment,owner)
253{
254 new sprite = create_entity("info_target")
255
256 assert is_valid_ent(sprite);
257
258 entity_set_edict(sprite,EV_ENT_aiment,aiment)
259 set_pev(sprite,pev_movetype,MOVETYPE_FOLLOW)
260
261 entity_set_model(sprite,SpritesPath[CS_TEAM_T])
262
263 set_pev(sprite,pev_owner,owner)
264
265 set_pev(sprite,pev_solid,SOLID_NOT)
266
267 fm_set_rendering(sprite,.render=kRenderTransAlpha,.amount=0)
268}
269
270public addToFullPackPost(es, e, ent, host, hostflags, player, pSet)
271{
272 #if CanUseAlive
273 if((1<=host<=MaxPlayers) && ((UserOptions[host][OptionStateView] == _:StateViewSpec) && ((OnFirstPersonView[host] && SpectatingUser[host])) || (UserOptions[host][OptionStateView] == _:StateViewAlways)) && is_valid_ent(ent))
274 #else
275 if((1<=host<=MaxPlayers) && (UserOptions[host][OptionStateView] == _:StateViewSpec) && ((OnFirstPersonView[host] && SpectatingUser[host])) && is_valid_ent(ent))
276 #endif
277 {
278 if(pev(ent,pev_owner) == EntitiesOwner)
279 {
280 if(engfunc(EngFunc_CheckVisibility,ent,pSet))
281 {
282 new spectated = OnFirstPersonView[host] ? SpectatingUser[host] : host
283
284 new aiment = pev(ent,pev_aiment)
285
286 static CsTeams:team
287
288 if((spectated != aiment) && is_user_alive(aiment) && ((cs_get_user_team(spectated) != (team=cs_get_user_team(aiment))) || (UserOptions[host][OptionTeamView] == _:TeamViewEverybody)))
289 {
290 static ID[Individual]
291
292 ID[Spectated] = spectated
293 ID[Viewed] = ent
294
295 static Float:origin[Individual][Vector]
296
297 entity_get_vector(ID[Spectated],EV_VEC_origin,origin[Spectated])
298 get_es(es,ES_Origin,origin[Viewed])
299
300 static Float:diff[Vector]
301 static Float:diffAngles[Vector]
302
303 xs_vec_sub(origin[Viewed],origin[Spectated],diff)
304 xs_vec_normalize(diff,diff)
305
306 vector_to_angle(diff,diffAngles)
307
308 diffAngles[0] = -diffAngles[0];
309
310 static Float:framePoints[FramePoint][Vector]
311
312 calculateFramePoints(origin[Viewed],framePoints,diffAngles)
313
314 static Float:eyes[Vector]
315
316 xs_vec_copy(origin[Spectated],eyes)
317
318 static Float:viewOfs[Vector]
319 entity_get_vector(ID[Spectated],EV_VEC_view_ofs,viewOfs);
320 xs_vec_add(eyes,viewOfs,eyes);
321
322 static Float:framePointsTraced[FramePoint][Vector]
323
324 static FramePoint:closerFramePoint
325
326 if(traceEyesFrame(ID[Spectated],eyes,framePoints,framePointsTraced,closerFramePoint))
327 {
328 static Float:otherPointInThePlane[Vector]
329 static Float:anotherPointInThePlane[Vector]
330
331 static Float:sideVector[Vector]
332 static Float:topBottomVector[Vector]
333
334 angle_vector(diffAngles,ANGLEVECTOR_UP,topBottomVector)
335 angle_vector(diffAngles,ANGLEVECTOR_RIGHT,sideVector)
336
337 xs_vec_mul_scalar(sideVector,SomeNonZeroValue,otherPointInThePlane)
338 xs_vec_mul_scalar(topBottomVector,SomeNonZeroValue,anotherPointInThePlane)
339
340 xs_vec_add(otherPointInThePlane,framePointsTraced[closerFramePoint],otherPointInThePlane)
341 xs_vec_add(anotherPointInThePlane,framePointsTraced[closerFramePoint],anotherPointInThePlane)
342
343 static Float:plane[4]
344 xs_plane_3p(plane,framePointsTraced[closerFramePoint],otherPointInThePlane,anotherPointInThePlane)
345
346 moveToPlane(plane,eyes,framePointsTraced,closerFramePoint);
347
348 static Float:middle[Vector]
349
350 static Float:half = 2.0
351
352 xs_vec_add(framePointsTraced[TopLeft],framePointsTraced[BottomRight],middle)
353 xs_vec_div_scalar(middle,half,middle)
354
355 new Float:scale = ScaleMultiplier * vector_distance(framePointsTraced[TopLeft],framePointsTraced[TopRight])
356
357 if(scale < ScaleLower)
358 scale = ScaleLower;
359
360 set_es(es,ES_AimEnt,0)
361 set_es(es,ES_MoveType,MOVETYPE_NONE)
362 set_es(es,ES_ModelIndex,SpritesCachedIDs[team])
363 set_es(es,ES_Scale,scale)
364 set_es(es,ES_Angles,diffAngles)
365 set_es(es,ES_Origin,middle)
366 set_es(es,ES_RenderMode,kRenderNormal)
367 }
368 }
369 }
370 }
371 }
372}
373
374calculateFramePoints(Float:origin[Vector],Float:framePoints[FramePoint][Vector],Float:perpendicularAngles[Vector])
375{
376 new Float:sideVector[Vector]
377 new Float:topBottomVector[Vector]
378
379 angle_vector(perpendicularAngles,ANGLEVECTOR_UP,topBottomVector)
380 angle_vector(perpendicularAngles,ANGLEVECTOR_RIGHT,sideVector)
381
382 new Float:sideDislocation[Vector]
383 new Float:bottomDislocation[Vector]
384 new Float:topDislocation[Vector]
385
386 xs_vec_mul_scalar(sideVector,Float:OriginOffsets[FrameSide],sideDislocation)
387 xs_vec_mul_scalar(topBottomVector,Float:OriginOffsets[FrameTop],topDislocation)
388 xs_vec_mul_scalar(topBottomVector,Float:OriginOffsets[FrameBottom],bottomDislocation)
389
390 xs_vec_copy(topDislocation,framePoints[TopLeft])
391
392 xs_vec_add(framePoints[TopLeft],sideDislocation,framePoints[TopRight])
393 xs_vec_sub(framePoints[TopLeft],sideDislocation,framePoints[TopLeft])
394
395 xs_vec_neg(bottomDislocation,framePoints[BottomLeft])
396
397 xs_vec_add(framePoints[BottomLeft],sideDislocation,framePoints[BottomRight])
398 xs_vec_sub(framePoints[BottomLeft],sideDislocation,framePoints[BottomLeft])
399
400 for(new FramePoint:i = TopLeft; i <= BottomRight; i++)
401 xs_vec_add(origin,framePoints[i],framePoints[i])
402
403}
404
405traceEyesFrame(id,Float:eyes[Vector],Float:framePoints[FramePoint][Vector],Float:framePointsTraced[FramePoint][Vector],&FramePoint:closerFramePoint)
406{
407 new Float:smallFraction = 1.0
408
409 for(new FramePoint:i = TopLeft; i <= BottomRight; i++)
410 {
411 new trace;
412 engfunc(EngFunc_TraceLine,eyes,framePoints[i],IGNORE_GLASS,id,trace)
413
414 new Float:fraction
415 get_tr2(trace, TR_flFraction,fraction);
416
417 if(fraction == 1.0)
418 {
419 return false;
420 }
421 else
422 {
423 if(fraction < smallFraction)
424 {
425 smallFraction = fraction
426 closerFramePoint = i;
427 }
428
429 get_tr2(trace,TR_EndPos,framePointsTraced[i]);
430 }
431 }
432
433 return true;
434}
435
436moveToPlane(Float:plane[4],Float:eyes[Vector],Float:framePointsTraced[FramePoint][Vector],FramePoint:alreadyInPlane)
437{
438 new Float:direction[Vector]
439
440 for(new FramePoint:i=TopLeft;i<alreadyInPlane;i++)
441 {
442 xs_vec_sub(eyes,framePointsTraced[i],direction)
443 xs_plane_rayintersect(plane,framePointsTraced[i],direction,framePointsTraced[i])
444 }
445
446 for(new FramePoint:i=alreadyInPlane+FramePoint:1;i<=BottomRight;i++)
447 {
448 xs_vec_sub(eyes,framePointsTraced[i],direction)
449 xs_plane_rayintersect(plane,framePointsTraced[i],direction,framePointsTraced[i])
450 }
451}
452
453handleJoiningFirstPersonView(id)
454{
455 OnFirstPersonView[id] = true
456
457 #if !CanUseAlive
458 if(!OnFirstPersonViewN++)
459 {
460 ForwardAddToFullPack = register_forward(FM_AddToFullPack,"addToFullPackPost",1)
461 }
462 #endif
463}
464
465handleQuitingFirstPersonView(id)
466{
467 OnFirstPersonView[id] = false
468 SpectatingUser[id] = 0
469
470 #if !CanUseAlive
471 if(!--OnFirstPersonViewN)
472 {
473 unregister_forward(FM_AddToFullPack,ForwardAddToFullPack,1)
474 }
475 #endif
476}
477
478public playerSpawn(id)
479{
480 if(HasPermissions[id])
481 {
482 if(OnFirstPersonView[id] && is_user_alive(id))
483 {
484 handleQuitingFirstPersonView(id)
485 }
486 }
487}
488
489public client_authorized(id)
490{
491 if(get_user_flags(id) & PermissionFlag)
492 {
493 HasPermissions[id] = true
494
495 static steamID[34]
496 get_user_authid(id,steamID,charsmax(steamID))
497
498 new listID
499
500 UserOptions[id][OptionStateView] = UserOptions[id][OptionTeamView] = 0
501
502 if(!TrieGetCell(SteamIDToListID,steamID,listID))
503 {
504 static queryString[] = "SELECT StateViewOption,TeamViewOption FROM `%s` WHERE `SteamID`= '%s'"
505
506 new Handle:connection = getDatabaseConnection()
507
508 new Handle:query = SQL_PrepareQuery(connection,queryString,TableName,steamID)
509
510 if(!SQL_Execute(query))
511 {
512 SQL_QueryError(query,DatabaseError,charsmax(DatabaseError))
513 set_fail_state(DatabaseError)
514 }
515 else
516 {
517 if(SQL_MoreResults(query))
518 {
519 UserOptions[id][OptionStateView] = clamp(SQL_ReadResult(query,0),0,OptionsLen[OptionStateView]-1)
520 UserOptions[id][OptionTeamView] = clamp(SQL_ReadResult(query,1),0,OptionsLen[OptionTeamView]-1)
521 }
522 }
523
524 TrieSetCell(SteamIDToListID,steamID,ArraySize(SteamIDsList))
525
526 ArrayPushString(SteamIDsList,steamID)
527
528 ArrayPushArray(PlayerOptionsOriginal,UserOptions[id])
529 ArrayPushArray(PlayerOptionsFinal,UserOptions[id])
530
531 SQL_FreeHandle(query)
532 SQL_FreeHandle(connection)
533 }
534 else
535 {
536 ArrayGetArray(PlayerOptionsFinal,listID,UserOptions[id])
537 }
538
539 PlayerListID[id] = listID
540 }
541 else
542 {
543 HasPermissions[id] = false
544 }
545}
546
547public client_disconnect(id)
548{
549 if(HasPermissions[id])
550 {
551 if(OnFirstPersonView[id])
552 {
553 handleQuitingFirstPersonView(id)
554 }
555
556 ArraySetArray(PlayerOptionsFinal,PlayerListID[id],UserOptions[id])
557
558 HasPermissions[id] = false
559 }
560}
561
562public specMode(id)
563{
564 if(HasPermissions[id])
565 {
566 new specMode[12]
567 read_data(2,specMode,11)
568
569 if(specMode[10] == '4')
570 {
571 handleJoiningFirstPersonView(id)
572 }
573 else if(OnFirstPersonView[id])
574 {
575 handleQuitingFirstPersonView(id)
576 }
577 }
578}
579
580public specTarget(id)
581{
582 new spectated = read_data(2);
583
584 if(spectated)
585 {
586 if(OnFirstPersonView[id])
587 {
588 if(spectated != SpectatingUser[id])
589 {
590 handleQuitingFirstPersonView(id)
591 SpectatingUser[id] = spectated;
592 handleJoiningFirstPersonView(id)
593 }
594 }
595 else
596 {
597 SpectatingUser[id] = spectated;
598 }
599 }
600}
601
602public plugin_end()
603{
604 for(new i=0;i<ArraySize(SteamIDsList);i++)
605 {
606 new optionsOriginal[Option]
607 new optionsFinal[Option]
608
609 ArrayGetArray(PlayerOptionsOriginal,i,optionsOriginal)
610 ArrayGetArray(PlayerOptionsFinal,i,optionsFinal)
611
612 new bool:differs
613 new sumOriginal
614 new sumFinal
615
616 for(new j=0;j<Option;j++)
617 {
618 if(optionsOriginal[j] != optionsFinal[j])
619 {
620 differs = true
621 }
622
623 sumOriginal += optionsOriginal[j]
624 sumFinal += optionsFinal[j]
625 }
626
627 if(differs)
628 {
629 static Handle:query
630
631 static steamID[34]
632
633 ArrayGetString(SteamIDsList,i,steamID,charsmax(steamID))
634
635 new Handle:connection = getDatabaseConnection()
636
637 if(!sumOriginal)
638 {
639 static queryString[] = "INSERT INTO `%s` (SteamID,StateViewOption,TeamViewOption) VALUES ('%s','%d','%d')"
640
641 query = SQL_PrepareQuery(connection,queryString,TableName,steamID,optionsFinal[_:OptionStateView],optionsFinal[_:OptionTeamView])
642 }
643 else if(!sumFinal)
644 {
645 static queryString[] = "DELETE FROM `%s` WHERE SteamID ='%s'"
646
647 query = SQL_PrepareQuery(connection,queryString,TableName,steamID)
648 }
649 else
650 {
651 static queryString[] = "UPDATE `%s` SET StateViewOption = '%d' ,TeamViewOption = '%d' WHERE SteamID ='%s'"
652
653 query = SQL_PrepareQuery(connection,queryString,TableName,optionsFinal[_:OptionStateView],optionsFinal[_:OptionTeamView],steamID)
654 }
655
656 if(!SQL_Execute(query))
657 {
658 SQL_QueryError(query,DatabaseError,charsmax(DatabaseError))
659 set_fail_state(DatabaseError)
660 }
661
662 SQL_FreeHandle(query)
663 SQL_FreeHandle(connection)
664 }
665 }
666}
667
668public peepingTom(id,level,cid)
669{
670 if(cmd_access(id,level,cid,0))
671 {
672 peepingTomMenu(id)
673 return PLUGIN_HANDLED
674 }
675
676 return PLUGIN_CONTINUE
677}
678
679peepingTomMenu(id)
680{
681 new menu = menu_create("Peeping Tom User Options","handlePeepingTomMenu")
682
683 new optionString[2]
684
685 static itemFormat[] = "%s: ^"\r%s\w^""
686 static itemText[sizeof itemFormat + 20 + 20]
687
688 for(new i=0;i<Option;i++)
689 {
690 optionString[0] = i + 48
691 formatex(itemText,charsmax(itemText),itemFormat,OptionLabels[i],OptionValuesLabels[i][UserOptions[id][i]])
692
693 menu_additem(menu,itemText,optionString)
694 }
695
696 menu_display(id,menu)
697}
698public handlePeepingTomMenu(id,menu,item)
699{
700 if(item >= 0)
701 {
702 new access, callback;
703
704 new actionString[2];
705 menu_item_getinfo(menu,item,access, actionString,1,_,_, callback);
706 new action = str_to_num(actionString);
707
708 UserOptions[id][action] = (UserOptions[id][action] + 1) % OptionsLen[action]
709
710 peepingTomMenu(id)
711 }
712
713 menu_destroy(menu)
714}