HLMOD.HU Forrás Megtekintés - www.hlmod.hu
  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.  
  18. new const Plugin[] = "Peeping Tom - Jack86"
  19. new const Author[] = "joaquimandrade"
  20. new const Version[] = "2.0"
  21.  
  22. new SpritesPath[CsTeams][] = {"","sprites/peeping_tom/t.spr","sprites/peeping_tom/ct.spr",""}
  23. new SpritesCachedIDs[CsTeams]
  24.  
  25. const MaxSlots = 32
  26.  
  27. new bool:OnFirstPersonView[MaxSlots+1]
  28. new HasPermissions[MaxSlots+1]
  29.  
  30. new SpectatingUser[MaxSlots+1]
  31.  
  32. const PermissionFlag = ADMIN_BAN
  33.  
  34. enum _:Vector
  35. {
  36. X,
  37. Y,
  38. Z
  39. }
  40.  
  41. enum Individual
  42. {
  43. Spectated,
  44. Viewed
  45. }
  46.  
  47. enum OriginOffset
  48. {
  49. FrameSide,
  50. FrameTop,
  51. FrameBottom,
  52. }
  53.  
  54. enum FramePoint
  55. {
  56. TopLeft,
  57. TopRight,
  58. BottomLeft,
  59. BottomRight
  60. }
  61.  
  62. new Float:OriginOffsets[OriginOffset] = {_:13.0,_:25.0,_:36.0}
  63.  
  64. new Float:ScaleMultiplier = 0.013;
  65. new Float:ScaleLower = 0.005
  66.  
  67. new Float:SomeNonZeroValue = 1.0
  68.  
  69. new EntitiesOwner
  70.  
  71. new MaxPlayers
  72.  
  73. enum StateViewOption
  74. {
  75. StateViewSpec,
  76. #if CanUseAlive
  77. StateViewAlways,
  78. #endif
  79. StateViewDisabled
  80.  
  81. }
  82.  
  83. enum TeamViewOption
  84. {
  85. TeamViewEnemies,
  86. TeamViewEverybody
  87. }
  88.  
  89. enum _:Option
  90. {
  91. OptionStateView,
  92. OptionTeamView
  93. }
  94.  
  95. new 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.  
  114. new OptionLabels[][] =
  115. {
  116. "State",
  117. "View"
  118. }
  119.  
  120. new OptionsLen[Option] = {_:StateViewOption,_:TeamViewOption}
  121.  
  122. new UserOptions[MaxSlots+1][Option]
  123.  
  124. new DatabaseName[] = "peepingTom"
  125. new TableName[] = "users"
  126.  
  127. new 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.  
  140. new DatabaseError[511]
  141.  
  142. new Handle:DatabaseInfoTuple
  143.  
  144. new Array:SteamIDsList
  145. new Trie:SteamIDToListID
  146. new Array:PlayerOptionsOriginal
  147. new Array:PlayerOptionsFinal
  148.  
  149. new PlayerListID[MaxSlots+1]
  150.  
  151. #if !CanUseAlive
  152. new ForwardAddToFullPack
  153. new OnFirstPersonViewN
  154. #endif
  155.  
  156. public 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.  
  162. public 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.  
  181. public 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.  
  203. initializeDatabase()
  204. {
  205. SQL_SetAffinity("sqlite")
  206. DatabaseInfoTuple = SQL_MakeDbTuple("","","",DatabaseName)
  207. }
  208.  
  209. databaseLayoutCreated()
  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.  
  220. createDatabaseLayout()
  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.  
  237. Handle: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.  
  252. public 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.  
  270. public 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.  
  374. calculateFramePoints(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.  
  405. traceEyesFrame(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.  
  436. moveToPlane(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.  
  453. handleJoiningFirstPersonView(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.  
  465. handleQuitingFirstPersonView(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.  
  478. public playerSpawn(id)
  479. {
  480. if(HasPermissions[id])
  481. {
  482. if(OnFirstPersonView[id] && is_user_alive(id))
  483. {
  484. handleQuitingFirstPersonView(id)
  485. }
  486. }
  487. }
  488.  
  489. public 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.  
  547. public 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.  
  562. public 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.  
  580. public 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.  
  602. public 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.  
  668. public 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.  
  679. peepingTomMenu(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. }
  698. public 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. }