Guest User

Yet Another Banking System

a guest
Sep 6th, 2016
497
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pawn 65.70 KB | None | 0 0
  1. /*
  2.     Yet Another Banking System by rootcause
  3.  
  4.     MySQL R40 Version
  5.     Topic: http://forum.sa-mp.com/showthread.php?t=606450
  6. */
  7.  
  8. #define     FILTERSCRIPT
  9. #include    <a_samp>
  10. #include    <a_mysql>           // by BlueG & maddinat0r - http://forum.sa-mp.com/showthread.php?t=56564
  11. #include    <izcmd>             // by Yashas - http://forum.sa-mp.com/showthread.php?t=576114
  12. #include    <sscanf2>           // by Y_Less - http://forum.sa-mp.com/showthread.php?t=602923
  13. #include    <streamer>          // by Incognito - http://forum.sa-mp.com/showthread.php?t=102865
  14. #include    <WeaponData>        // by Southclaw - https://github.com/Southclaw/AdvancedWeaponData
  15. #include    <YSI\y_iterate>     // by Y_Less - http://forum.sa-mp.com/showthread.php?t=570884
  16.  
  17. #define     MYSQL_HOST      "host"
  18. #define     MYSQL_USER      "user"
  19. #define     MYSQL_PASS      "password"
  20. #define     MYSQL_DBNAME    "dbname"
  21.  
  22. #define     MAX_BANKERS     (20)
  23. #define     MAX_ATMS        (100)
  24.  
  25. #define     BANKER_USE_MAPICON                  // comment or remove this line if you don't want bankers to have mapicons
  26. #define     ATM_USE_MAPICON                     // comment or remove this line if you don't want atms to have mapicons
  27. #define     BANKER_ICON_RANGE       (10.0)      // banker mapicon stream distance, you can remove this if you're not using banker icons (default: 10.0)
  28. #define     ATM_ICON_RANGE          (100.0)     // atm mapicon stream distance, you can remove this if you're not using banker icons (default: 100.0)
  29. #define     ACCOUNT_PRICE           (100)       // amount of money required to create a new bank account (default: 100)
  30. #define     ACCOUNT_CLIMIT          (5)         // a player can create x accounts, you can comment or remove this line if you don't want an account limit (default: 5)
  31. #define     ACCOUNT_LIMIT           (500000000) // how much money can a bank account have (default: 500,000,000)
  32.  
  33. // ATM Robbery Config
  34. //#define     ROBBABLE_ATMS           // uncomment this line if you want robbable atms
  35.  
  36. #if defined ROBBABLE_ATMS
  37.     #define     ATM_HEALTH              (350.0)     // health of an atm (Default: 350.0)
  38.     #define     ATM_REGEN               (120)       // a robbed atm will start working after x seconds (Default: 120)
  39.     #define     ATM_ROB_MIN             (1500)      // min. amount of money stolen from an atm (Default: 1500)
  40.     #define     ATM_ROB_MAX             (3500)      // max. amount of money stolen from an atm (Default: 3500)
  41. #endif
  42.  
  43. enum    _:E_BANK_DIALOG
  44. {
  45.     DIALOG_BANK_MENU_NOLOGIN = 12450,
  46.     DIALOG_BANK_MENU,
  47.     DIALOG_BANK_CREATE_ACCOUNT,
  48.     DIALOG_BANK_ACCOUNTS,
  49.     DIALOG_BANK_LOGIN_ID,
  50.     DIALOG_BANK_LOGIN_PASS,
  51.     DIALOG_BANK_DEPOSIT,
  52.     DIALOG_BANK_WITHDRAW,
  53.     DIALOG_BANK_TRANSFER_1,
  54.     DIALOG_BANK_TRANSFER_2,
  55.     DIALOG_BANK_PASSWORD,
  56.     DIALOG_BANK_REMOVE,
  57.     DIALOG_BANK_LOGS,
  58.     DIALOG_BANK_LOG_PAGE
  59. }
  60.  
  61. enum    _:E_BANK_LOGTYPE
  62. {
  63.     TYPE_NONE,
  64.     TYPE_LOGIN,
  65.     TYPE_DEPOSIT,
  66.     TYPE_WITHDRAW,
  67.     TYPE_TRANSFER,
  68.     TYPE_PASSCHANGE
  69. }
  70.  
  71. #if defined ROBBABLE_ATMS
  72. enum    _:E_ATMDATA
  73. {
  74.     IDString[8],
  75.     refID
  76. }
  77. #endif
  78.  
  79. enum    E_BANKER
  80. {
  81.     // saved
  82.     Skin,
  83.     Float: bankerX,
  84.     Float: bankerY,
  85.     Float: bankerZ,
  86.     Float: bankerA,
  87.     // temp
  88.     bankerActorID,
  89.     #if defined BANKER_USE_MAPICON
  90.     bankerIconID,
  91.     #endif
  92.     Text3D: bankerLabel
  93. }
  94.  
  95. enum    E_ATM
  96. {
  97.     // saved
  98.     Float: atmX,
  99.     Float: atmY,
  100.     Float: atmZ,
  101.     Float: atmRX,
  102.     Float: atmRY,
  103.     Float: atmRZ,
  104.     // temp
  105.     atmObjID,
  106.    
  107.     #if defined ATM_USE_MAPICON
  108.     atmIconID,
  109.     #endif
  110.    
  111.     #if defined ROBBABLE_ATMS
  112.     Float: atmHealth,
  113.     atmRegen,
  114.     atmTimer,
  115.     atmPickup,
  116.     #endif
  117.    
  118.     Text3D: atmLabel
  119. }
  120.  
  121. new
  122.     MySQL: BankSQLHandle;
  123.  
  124. new
  125.     BankerData[MAX_BANKERS][E_BANKER],
  126.     ATMData[MAX_ATMS][E_ATM];
  127.  
  128. new
  129.     Iterator: Bankers<MAX_BANKERS>,
  130.     Iterator: ATMs<MAX_ATMS>;
  131.  
  132. new
  133.     CurrentAccountID[MAX_PLAYERS] = {-1, ...},
  134.     LogListType[MAX_PLAYERS] = {TYPE_NONE, ...},
  135.     LogListPage[MAX_PLAYERS],
  136.     EditingATMID[MAX_PLAYERS] = {-1, ...};
  137.  
  138. formatInt(intVariable, iThousandSeparator = ',', iCurrencyChar = '$')
  139. {
  140.     /*
  141.         By Kar
  142.         https://gist.github.com/Kar2k/bfb0eafb2caf71a1237b349684e091b9/8849dad7baa863afb1048f40badd103567c005a5#file-formatint-function
  143.     */
  144.     static
  145.         s_szReturn[ 32 ],
  146.         s_szThousandSeparator[ 2 ] = { ' ', EOS },
  147.         s_szCurrencyChar[ 2 ] = { ' ', EOS },
  148.         s_iVariableLen,
  149.         s_iChar,
  150.         s_iSepPos,
  151.         bool:s_isNegative
  152.     ;
  153.  
  154.     format( s_szReturn, sizeof( s_szReturn ), "%d", intVariable );
  155.  
  156.     if(s_szReturn[0] == '-')
  157.         s_isNegative = true;
  158.     else
  159.         s_isNegative = false;
  160.  
  161.     s_iVariableLen = strlen( s_szReturn );
  162.  
  163.     if ( s_iVariableLen >= 4 && iThousandSeparator)
  164.     {
  165.         s_szThousandSeparator[ 0 ] = iThousandSeparator;
  166.  
  167.         s_iChar = s_iVariableLen;
  168.         s_iSepPos = 0;
  169.  
  170.         while ( --s_iChar > _:s_isNegative )
  171.         {
  172.             if ( ++s_iSepPos == 3 )
  173.             {
  174.                 strins( s_szReturn, s_szThousandSeparator, s_iChar );
  175.  
  176.                 s_iSepPos = 0;
  177.             }
  178.         }
  179.     }
  180.     if(iCurrencyChar) {
  181.         s_szCurrencyChar[ 0 ] = iCurrencyChar;
  182.         strins( s_szReturn, s_szCurrencyChar, _:s_isNegative );
  183.     }
  184.     return s_szReturn;
  185. }
  186.  
  187. #if defined ROBBABLE_ATMS
  188. RandomEx(min, max) //Y_Less
  189.     return random(max - min) + min;
  190.  
  191. ConvertToMinutes(time)
  192. {
  193.     // http://forum.sa-mp.com/showpost.php?p=3223897&postcount=11
  194.     new string[15];//-2000000000:00 could happen, so make the string 15 chars to avoid any errors
  195.     format(string, sizeof(string), "%02d:%02d", time / 60, time % 60);
  196.     return string;
  197. }
  198. #endif
  199.  
  200. IsPlayerNearBanker(playerid)
  201. {
  202.     foreach(new i : Bankers)
  203.     {
  204.         if(IsPlayerInRangeOfPoint(playerid, 3.0, BankerData[i][bankerX], BankerData[i][bankerY], BankerData[i][bankerZ])) return 1;
  205.     }
  206.  
  207.     return 0;
  208. }
  209.  
  210. GetClosestATM(playerid, Float: range = 3.0)
  211. {
  212.     new id = -1, Float: dist = range, Float: tempdist;
  213.     foreach(new i : ATMs)
  214.     {
  215.         tempdist = GetPlayerDistanceFromPoint(playerid, ATMData[i][atmX], ATMData[i][atmY], ATMData[i][atmZ]);
  216.  
  217.         if(tempdist > range) continue;
  218.         if(tempdist <= dist)
  219.         {
  220.             dist = tempdist;
  221.             id = i;
  222.         }
  223.     }
  224.  
  225.     return id;
  226. }
  227.  
  228. Bank_SaveLog(playerid, type, accid, toaccid, amount)
  229. {
  230.     if(type == TYPE_NONE) return 1;
  231.     new query[256];
  232.  
  233.     switch(type)
  234.     {
  235.         case TYPE_LOGIN, TYPE_PASSCHANGE: mysql_format(BankSQLHandle, query, sizeof(query), "INSERT INTO bank_logs SET AccountID=%d, Type=%d, Player='%e', Date=UNIX_TIMESTAMP()", accid, type, Player_GetName(playerid));
  236.         case TYPE_DEPOSIT, TYPE_WITHDRAW: mysql_format(BankSQLHandle, query, sizeof(query), "INSERT INTO bank_logs SET AccountID=%d, Type=%d, Player='%e', Amount=%d, Date=UNIX_TIMESTAMP()", accid, type, Player_GetName(playerid), amount);
  237.         case TYPE_TRANSFER: mysql_format(BankSQLHandle, query, sizeof(query), "INSERT INTO bank_logs SET AccountID=%d, ToAccountID=%d, Type=%d, Player='%e', Amount=%d, Date=UNIX_TIMESTAMP()", accid, toaccid, type, Player_GetName(playerid), amount);
  238.     }
  239.  
  240.     mysql_tquery(BankSQLHandle, query);
  241.     return 1;
  242. }
  243.  
  244. Bank_ShowMenu(playerid)
  245. {
  246.     new string[256], using_atm = GetPVarInt(playerid, "usingATM");
  247.     if(CurrentAccountID[playerid] == -1) {
  248.         format(string, sizeof(string), "{%06x}Create Account\t{2ECC71}%s\nMy Accounts\t{F1C40F}%d\nAccount Login", (using_atm ? 0xE74C3CFF >>> 8 : 0xFFFFFFFF >>> 8), (using_atm ? ("") : formatInt(ACCOUNT_PRICE)), Bank_AccountCount(playerid));
  249.         ShowPlayerDialog(playerid, DIALOG_BANK_MENU_NOLOGIN, DIALOG_STYLE_TABLIST, "{F1C40F}Bank: {FFFFFF}Menu", string, "Choose", "Close");
  250.     }else{
  251.         new balance = Bank_GetBalance(CurrentAccountID[playerid]), menu_title[64];
  252.         format(menu_title, sizeof(menu_title), "{F1C40F}Bank: {FFFFFF}Menu (Account ID: {F1C40F}%d{FFFFFF})", CurrentAccountID[playerid]);
  253.  
  254.         format(
  255.             string,
  256.             sizeof(string),
  257.             "{%06x}Create Account\t{2ECC71}%s\nMy Accounts\t{F1C40F}%d\nDeposit\t{2ECC71}%s\nWithdraw\t{2ECC71}%s\nTransfer\t{2ECC71}%s\n{%06x}Account Logs\n{%06x}Change Password\n{%06x}Remove Account\nLogout",
  258.             (using_atm ? 0xE74C3CFF >>> 8 : 0xFFFFFFFF >>> 8),
  259.             (using_atm ? ("") : formatInt(ACCOUNT_PRICE)),
  260.             Bank_AccountCount(playerid),
  261.             formatInt(GetPlayerMoney(playerid)),
  262.             formatInt(balance),
  263.             formatInt(balance),
  264.             (using_atm ? 0xE74C3CFF >>> 8 : 0xFFFFFFFF >>> 8),
  265.             (using_atm ? 0xE74C3CFF >>> 8 : 0xFFFFFFFF >>> 8),
  266.             (using_atm ? 0xE74C3CFF >>> 8 : 0xFFFFFFFF >>> 8)
  267.         );
  268.  
  269.         ShowPlayerDialog(playerid, DIALOG_BANK_MENU, DIALOG_STYLE_TABLIST, menu_title, string, "Choose", "Close");
  270.     }
  271.  
  272.     DeletePVar(playerid, "bankLoginAccount");
  273.     DeletePVar(playerid, "bankTransferAccount");
  274.     return 1;
  275. }
  276.  
  277. Bank_ShowLogMenu(playerid)
  278. {
  279.     LogListType[playerid] = TYPE_NONE;
  280.     LogListPage[playerid] = 0;
  281.     ShowPlayerDialog(playerid, DIALOG_BANK_LOGS, DIALOG_STYLE_LIST, "{F1C40F}Bank: {FFFFFF}Logs", "Deposited Money\nWithdrawn Money\nTransfers\nLogins\nPassword Changes", "Show", "Back");
  282.     return 1;
  283. }
  284.  
  285. Player_GetName(playerid)
  286. {
  287.     new name[MAX_PLAYER_NAME];
  288.     GetPlayerName(playerid, name, MAX_PLAYER_NAME);
  289.     return name;
  290. }
  291.  
  292. Bank_AccountCount(playerid)
  293. {
  294.     new query[144], Cache: find_accounts;
  295.     mysql_format(BankSQLHandle, query, sizeof(query), "SELECT null FROM bank_accounts WHERE Owner='%e' && Disabled=0", Player_GetName(playerid));
  296.     find_accounts = mysql_query(BankSQLHandle, query);
  297.  
  298.     new count = cache_num_rows();
  299.     cache_delete(find_accounts);
  300.     return count;
  301. }
  302.  
  303. Bank_GetBalance(accountid)
  304. {
  305.     new query[144], Cache: get_balance;
  306.     mysql_format(BankSQLHandle, query, sizeof(query), "SELECT Balance FROM bank_accounts WHERE ID=%d && Disabled=0", accountid);
  307.     get_balance = mysql_query(BankSQLHandle, query);
  308.  
  309.     new balance;
  310.     cache_get_value_name_int(0, "Balance", balance);
  311.     cache_delete(get_balance);
  312.     return balance;
  313. }
  314.  
  315. Bank_GetOwner(accountid)
  316. {
  317.     new query[144], owner[MAX_PLAYER_NAME], Cache: get_owner;
  318.     mysql_format(BankSQLHandle, query, sizeof(query), "SELECT Owner FROM bank_accounts WHERE ID=%d && Disabled=0", accountid);
  319.     get_owner = mysql_query(BankSQLHandle, query);
  320.  
  321.     cache_get_value_name(0, "Owner", owner);
  322.     cache_delete(get_owner);
  323.     return owner;
  324. }
  325.  
  326. Bank_ListAccounts(playerid)
  327. {
  328.     new query[256], Cache: get_accounts;
  329.     mysql_format(BankSQLHandle, query, sizeof(query), "SELECT ID, Balance, LastAccess, FROM_UNIXTIME(CreatedOn, '%%d/%%m/%%Y %%H:%%i:%%s') AS Created, FROM_UNIXTIME(LastAccess, '%%d/%%m/%%Y %%H:%%i:%%s') AS Last FROM bank_accounts WHERE Owner='%e' && Disabled=0 ORDER BY CreatedOn DESC", Player_GetName(playerid));
  330.     get_accounts = mysql_query(BankSQLHandle, query);
  331.     new rows = cache_num_rows();
  332.  
  333.     if(rows) {
  334.         new string[1024], acc_id, balance, last_access, cdate[24], ldate[24];
  335.         format(string, sizeof(string), "ID\tBalance\tCreated On\tLast Access\n");
  336.         for(new i; i < rows; ++i)
  337.         {
  338.             cache_get_value_name_int(i, "ID", acc_id);
  339.             cache_get_value_name_int(i, "Balance", balance);
  340.             cache_get_value_name_int(i, "LastAccess", last_access);
  341.             cache_get_value_name(i, "Created", cdate);
  342.             cache_get_value_name(i, "Last", ldate);
  343.            
  344.             format(string, sizeof(string), "%s{FFFFFF}%d\t{2ECC71}%s\t{FFFFFF}%s\t%s\n", string, acc_id, formatInt(balance), cdate, (last_access == 0) ? ("Never") : ldate);
  345.         }
  346.  
  347.         ShowPlayerDialog(playerid, DIALOG_BANK_ACCOUNTS, DIALOG_STYLE_TABLIST_HEADERS, "{F1C40F}Bank: {FFFFFF}My Accounts", string, "Login", "Back");
  348.     }else{
  349.         SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You don't have any bank accounts.");
  350.         Bank_ShowMenu(playerid);
  351.     }
  352.  
  353.     cache_delete(get_accounts);
  354.     return 1;
  355. }
  356.  
  357. Bank_ShowLogs(playerid)
  358. {
  359.     new query[196], type = LogListType[playerid], Cache: bank_logs;
  360.     mysql_format(BankSQLHandle, query, sizeof(query), "SELECT *, FROM_UNIXTIME(Date, '%%d/%%m/%%Y %%H:%%i:%%s') as ActionDate FROM bank_logs WHERE AccountID=%d && Type=%d ORDER BY Date DESC LIMIT %d, 15", CurrentAccountID[playerid], type, LogListPage[playerid] * 15);
  361.     bank_logs = mysql_query(BankSQLHandle, query);
  362.  
  363.     new rows = cache_num_rows();
  364.     if(rows) {
  365.         new list[1512], title[96], name[MAX_PLAYER_NAME], date[24];
  366.         switch(type)
  367.         {
  368.             case TYPE_LOGIN:
  369.             {
  370.                 format(list, sizeof(list), "By\tAction Date\n");
  371.                 format(title, sizeof(title), "{F1C40F}Bank: {FFFFFF}Login History (Page %d)", LogListPage[playerid] + 1);
  372.             }
  373.  
  374.             case TYPE_DEPOSIT:
  375.             {
  376.                 format(list, sizeof(list), "By\tAmount\tDeposit Date\n");
  377.                 format(title, sizeof(title), "{F1C40F}Bank: {FFFFFF}Deposit History (Page %d)", LogListPage[playerid] + 1);
  378.             }
  379.  
  380.             case TYPE_WITHDRAW:
  381.             {
  382.                 format(list, sizeof(list), "By\tAmount\tWithdraw Date\n");
  383.                 format(title, sizeof(title), "{F1C40F}Bank: {FFFFFF}Withdraw History (Page %d)", LogListPage[playerid] + 1);
  384.             }
  385.  
  386.             case TYPE_TRANSFER:
  387.             {
  388.                 format(list, sizeof(list), "By\tTo Account\tAmount\tTransfer Date\n");
  389.                 format(title, sizeof(title), "{F1C40F}Bank: {FFFFFF}Transfer History (Page %d)", LogListPage[playerid] + 1);
  390.             }
  391.  
  392.             case TYPE_PASSCHANGE:
  393.             {
  394.                 format(list, sizeof(list), "By\tAction Date\n");
  395.                 format(title, sizeof(title), "{F1C40F}Bank: {FFFFFF}Password Changes (Page %d)", LogListPage[playerid] + 1);
  396.             }
  397.         }
  398.  
  399.         new amount, to_acc_id;
  400.         for(new i; i < rows; ++i)
  401.         {
  402.             cache_get_value_name(i, "Player", name);
  403.             cache_get_value_name(i, "ActionDate", date);
  404.  
  405.             switch(type)
  406.             {
  407.                 case TYPE_LOGIN:
  408.                 {
  409.                     format(list, sizeof(list), "%s%s\t%s\n", list, name, date);
  410.                 }
  411.  
  412.                 case TYPE_DEPOSIT:
  413.                 {
  414.                     cache_get_value_name_int(i, "Amount", amount);
  415.                     format(list, sizeof(list), "%s%s\t{2ECC71}%s\t%s\n", list, name, formatInt(amount), date);
  416.                 }
  417.  
  418.                 case TYPE_WITHDRAW:
  419.                 {
  420.                     cache_get_value_name_int(i, "Amount", amount);
  421.                     format(list, sizeof(list), "%s%s\t{2ECC71}%s\t%s\n", list, name, formatInt(amount), date);
  422.                 }
  423.  
  424.                 case TYPE_TRANSFER:
  425.                 {
  426.                     cache_get_value_name_int(i, "ToAccountID", to_acc_id);
  427.                     cache_get_value_name_int(i, "Amount", amount);
  428.                    
  429.                     format(list, sizeof(list), "%s%s\t%d\t{2ECC71}%s\t%s\n", list, name, to_acc_id, formatInt(amount), date);
  430.                 }
  431.  
  432.                 case TYPE_PASSCHANGE:
  433.                 {
  434.                     format(list, sizeof(list), "%s%s\t%s\n", list, name, date);
  435.                 }
  436.             }
  437.         }
  438.  
  439.         ShowPlayerDialog(playerid, DIALOG_BANK_LOG_PAGE, DIALOG_STYLE_TABLIST_HEADERS, title, list, "Next", "Previous");
  440.     }else{
  441.         SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Can't find any more records.");
  442.         Bank_ShowLogMenu(playerid);
  443.     }
  444.  
  445.     cache_delete(bank_logs);
  446.     return 1;
  447. }
  448.  
  449. #if defined ROBBABLE_ATMS
  450. ATM_ReturnDmgText(id)
  451. {
  452.     new Float: health = ATMData[id][atmHealth], color, string[16];
  453.  
  454.     if(health < (ATM_HEALTH / 4)) {
  455.         color = 0xE74C3CFF;
  456.     }else if(health < (ATM_HEALTH / 2)) {
  457.         color = 0xF39C12FF;
  458.     }else{
  459.         color = 0x2ECC71FF;
  460.     }
  461.  
  462.     format(string, sizeof(string), "{%06x}%.2f%%", color >>> 8, (health * 100 / ATM_HEALTH));
  463.     return string;
  464. }
  465. #endif
  466.  
  467. public OnFilterScriptInit()
  468. {
  469.     print("  [Bank System] Initializing...");
  470.  
  471.     for(new i; i < MAX_BANKERS; i++)
  472.     {
  473.         BankerData[i][bankerActorID] = -1;
  474.  
  475.         #if defined BANKER_USE_MAPICON
  476.         BankerData[i][bankerIconID] = -1;
  477.         #endif
  478.  
  479.         BankerData[i][bankerLabel] = Text3D: -1;
  480.     }
  481.  
  482.     for(new i; i < MAX_ATMS; i++)
  483.     {
  484.         ATMData[i][atmObjID] = -1;
  485.  
  486.         #if defined ATM_USE_MAPICON
  487.         ATMData[i][atmIconID] = -1;
  488.         #endif
  489.        
  490.         #if defined ROBBABLE_ATMS
  491.         ATMData[i][atmTimer] = ATMData[i][atmPickup] = -1;
  492.         ATMData[i][atmHealth] = ATM_HEALTH;
  493.         #endif
  494.  
  495.         ATMData[i][atmLabel] = Text3D: -1;
  496.     }
  497.  
  498.     BankSQLHandle = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DBNAME);
  499.     mysql_log(ERROR | WARNING);
  500.     if(mysql_errno()) return printf("  [Bank System] Can't connect to MySQL. (Error #%d)", mysql_errno());
  501.  
  502.     // create tables if they don't exist
  503.     mysql_tquery(BankSQLHandle, "CREATE TABLE IF NOT EXISTS `bankers` (\
  504.       `ID` int(11) NOT NULL,\
  505.       `Skin` smallint(3) NOT NULL,\
  506.       `PosX` float NOT NULL,\
  507.       `PosY` float NOT NULL,\
  508.       `PosZ` float NOT NULL,\
  509.       `PosA` float NOT NULL\
  510.     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
  511.  
  512.     mysql_tquery(BankSQLHandle, "CREATE TABLE IF NOT EXISTS `bank_atms` (\
  513.       `ID` int(11) NOT NULL,\
  514.       `PosX` float NOT NULL,\
  515.       `PosY` float NOT NULL,\
  516.       `PosZ` float NOT NULL,\
  517.       `RotX` float NOT NULL,\
  518.       `RotY` float NOT NULL,\
  519.       `RotZ` float NOT NULL\
  520.     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
  521.  
  522.     mysql_tquery(BankSQLHandle, "CREATE TABLE IF NOT EXISTS `bank_accounts` (\
  523.       `ID` int(11) NOT NULL auto_increment,\
  524.       `Owner` varchar(24) NOT NULL,\
  525.       `Password` varchar(32) NOT NULL,\
  526.       `Balance` int(11) NOT NULL,\
  527.       `CreatedOn` int(11) NOT NULL,\
  528.       `LastAccess` int(11) NOT NULL,\
  529.       `Disabled` smallint(1) NOT NULL,\
  530.       PRIMARY KEY  (`ID`)\
  531.     ) ENGINE=InnoDB DEFAULT CHARSET=utf8;");
  532.  
  533.     new query[512];
  534.     mysql_format(BankSQLHandle, query, sizeof(query), "CREATE TABLE IF NOT EXISTS `bank_logs` (\
  535.         `ID` int(11) NOT NULL auto_increment,\
  536.         `AccountID` int(11) NOT NULL,\
  537.         `ToAccountID` int(11) NOT NULL default '-1',\
  538.         `Type` smallint(1) NOT NULL,\
  539.         `Player` varchar(24) NOT NULL,\
  540.         `Amount` int(11) NOT NULL,\
  541.         `Date` int(11) NOT NULL,");
  542.  
  543.     mysql_format(BankSQLHandle, query, sizeof(query), "%s\
  544.         PRIMARY KEY  (`ID`),\
  545.         KEY `bank_logs_ibfk_1` (`AccountID`),\
  546.         CONSTRAINT `bank_logs_ibfk_1` FOREIGN KEY (`AccountID`) REFERENCES `bank_accounts` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE\
  547.         ) ENGINE=InnoDB DEFAULT CHARSET=utf8;", query);
  548.  
  549.     mysql_tquery(BankSQLHandle, query);
  550.  
  551.     print("  [Bank System] Connected to MySQL, loading data...");
  552.     mysql_tquery(BankSQLHandle, "SELECT * FROM bankers", "LoadBankers");
  553.     mysql_tquery(BankSQLHandle, "SELECT * FROM bank_atms", "LoadATMs");
  554.     return 1;
  555. }
  556.  
  557. public OnFilterScriptExit()
  558. {
  559.     foreach(new i : Bankers)
  560.     {
  561.         if(IsValidActor(BankerData[i][bankerActorID])) DestroyActor(BankerData[i][bankerActorID]);
  562.     }
  563.  
  564.     print("  [Bank System] Unloaded.");
  565.     mysql_close(BankSQLHandle);
  566.     return 1;
  567. }
  568.  
  569. public OnPlayerConnect(playerid)
  570. {
  571.     CurrentAccountID[playerid] = -1;
  572.     LogListType[playerid] = TYPE_NONE;
  573.     LogListPage[playerid] = 0;
  574.  
  575.     EditingATMID[playerid] = -1;
  576.     return 1;
  577. }
  578.  
  579. public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
  580. {
  581.     switch(dialogid)
  582.     {
  583.         /* ---------------------------------------------------------------------- */
  584.         case DIALOG_BANK_MENU_NOLOGIN:
  585.         {
  586.             if(!response) return 1;
  587.             if(listitem == 0)
  588.             {
  589.                 if(GetPVarInt(playerid, "usingATM"))
  590.                 {
  591.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't do this at an ATM, visit a banker.");
  592.                     return Bank_ShowMenu(playerid);
  593.                 }
  594.  
  595.                 if(ACCOUNT_PRICE > GetPlayerMoney(playerid))
  596.                 {
  597.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You don't have enough money to create a bank account.");
  598.                     return Bank_ShowMenu(playerid);
  599.                 }
  600.  
  601.                 #if defined ACCOUNT_CLIMIT
  602.                 if(Bank_AccountCount(playerid) >= ACCOUNT_CLIMIT)
  603.                 {
  604.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't create any more bank accounts.");
  605.                     return Bank_ShowMenu(playerid);
  606.                 }
  607.                 #endif
  608.  
  609.                 ShowPlayerDialog(playerid, DIALOG_BANK_CREATE_ACCOUNT, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Create Account", "Choose a password for your new bank account:", "Create", "Back");
  610.             }
  611.  
  612.             if(listitem == 1) Bank_ListAccounts(playerid);
  613.             if(listitem == 2) ShowPlayerDialog(playerid, DIALOG_BANK_LOGIN_ID, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Login", "Account ID:", "Continue", "Cancel");
  614.             return 1;
  615.         }
  616.         /* ---------------------------------------------------------------------- */
  617.         case DIALOG_BANK_MENU:
  618.         {
  619.             if(!response) return 1;
  620.             if(listitem == 0)
  621.             {
  622.                 if(GetPVarInt(playerid, "usingATM"))
  623.                 {
  624.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't do this at an ATM, visit a banker.");
  625.                     return Bank_ShowMenu(playerid);
  626.                 }
  627.  
  628.                 if(ACCOUNT_PRICE > GetPlayerMoney(playerid))
  629.                 {
  630.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You don't have enough money to create a bank account.");
  631.                     return Bank_ShowMenu(playerid);
  632.                 }
  633.  
  634.                 #if defined ACCOUNT_CLIMIT
  635.                 if(Bank_AccountCount(playerid) >= ACCOUNT_CLIMIT)
  636.                 {
  637.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't create any more bank accounts.");
  638.                     return Bank_ShowMenu(playerid);
  639.                 }
  640.                 #endif
  641.  
  642.                 ShowPlayerDialog(playerid, DIALOG_BANK_CREATE_ACCOUNT, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Create Account", "Choose a password for your new bank account:", "Create", "Back");
  643.             }
  644.  
  645.             if(listitem == 1) Bank_ListAccounts(playerid);
  646.             if(listitem == 2) ShowPlayerDialog(playerid, DIALOG_BANK_DEPOSIT, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Deposit", "How much money do you want to deposit?", "Deposit", "Back");
  647.             if(listitem == 3) ShowPlayerDialog(playerid, DIALOG_BANK_WITHDRAW, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Withdraw", "How much money do you want to withdraw?", "Withdraw", "Back");
  648.             if(listitem == 4) ShowPlayerDialog(playerid, DIALOG_BANK_TRANSFER_1, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Transfer", "Specify an account ID:", "Continue", "Back");
  649.             if(listitem == 5)
  650.             {
  651.                 if(GetPVarInt(playerid, "usingATM"))
  652.                 {
  653.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't do this at an ATM, visit a banker.");
  654.                     return Bank_ShowMenu(playerid);
  655.                 }
  656.  
  657.                 Bank_ShowLogMenu(playerid);
  658.             }
  659.  
  660.             if(listitem == 6)
  661.             {
  662.                 if(GetPVarInt(playerid, "usingATM"))
  663.                 {
  664.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't do this at an ATM, visit a banker.");
  665.                     return Bank_ShowMenu(playerid);
  666.                 }
  667.  
  668.                 if(strcmp(Bank_GetOwner(CurrentAccountID[playerid]), Player_GetName(playerid)))
  669.                 {
  670.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only the account owner can do this.");
  671.                     return Bank_ShowMenu(playerid);
  672.                 }
  673.  
  674.                 ShowPlayerDialog(playerid, DIALOG_BANK_PASSWORD, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Change Password", "Write a new password:", "Change", "Back");
  675.             }
  676.  
  677.             if(listitem == 7)
  678.             {
  679.                 if(GetPVarInt(playerid, "usingATM"))
  680.                 {
  681.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't do this at an ATM, visit a banker.");
  682.                     return Bank_ShowMenu(playerid);
  683.                 }
  684.  
  685.                 if(strcmp(Bank_GetOwner(CurrentAccountID[playerid]), Player_GetName(playerid)))
  686.                 {
  687.                     SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only the account owner can do this.");
  688.                     return Bank_ShowMenu(playerid);
  689.                 }
  690.  
  691.                 ShowPlayerDialog(playerid, DIALOG_BANK_REMOVE, DIALOG_STYLE_MSGBOX, "{F1C40F}Bank: {FFFFFF}Remove Account", "Are you sure? This account will get deleted {E74C3C}permanently.", "Yes", "Back");
  692.                 // https://youtu.be/rcjpags7JT8 - because it doesn't get deleted actually
  693.             }
  694.  
  695.             if(listitem == 8)
  696.             {
  697.                 SendClientMessage(playerid, 0x3498DBFF, "BANK: {FFFFFF}Successfully logged out.");
  698.  
  699.                 CurrentAccountID[playerid] = -1;
  700.                 Bank_ShowMenu(playerid);
  701.             }
  702.         }
  703.         /* ---------------------------------------------------------------------- */
  704.         case DIALOG_BANK_CREATE_ACCOUNT:
  705.         {
  706.             if(!response) return Bank_ShowMenu(playerid);
  707.             if(isnull(inputtext)) return ShowPlayerDialog(playerid, DIALOG_BANK_CREATE_ACCOUNT, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Create Account", "{E74C3C}You can't leave your account password empty.\n\n{FFFFFF}Choose a password for your new bank account:", "Create", "Back");
  708.             if(strlen(inputtext) > 16) return ShowPlayerDialog(playerid, DIALOG_BANK_CREATE_ACCOUNT, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Create Account", "{E74C3C}Account password can't be more than 16 characters.\n\n{FFFFFF}Choose a password for your new bank account:", "Create", "Back");
  709.             if(ACCOUNT_PRICE > GetPlayerMoney(playerid))
  710.             {
  711.                 SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You don't have enough money to create a bank account.");
  712.                 return Bank_ShowMenu(playerid);
  713.             }
  714.  
  715.             #if defined ACCOUNT_CLIMIT
  716.             if(Bank_AccountCount(playerid) >= ACCOUNT_CLIMIT)
  717.             {
  718.                 SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't create any more bank accounts.");
  719.                 return Bank_ShowMenu(playerid);
  720.             }
  721.             #endif
  722.  
  723.             new query[144];
  724.             mysql_format(BankSQLHandle, query, sizeof(query), "INSERT INTO bank_accounts SET Owner='%e', Password=md5('%e'), CreatedOn=UNIX_TIMESTAMP()", Player_GetName(playerid), inputtext);
  725.             mysql_tquery(BankSQLHandle, query, "OnBankAccountCreated", "is", playerid, inputtext);
  726.             return 1;
  727.         }
  728.         /* ---------------------------------------------------------------------- */
  729.         case DIALOG_BANK_ACCOUNTS:
  730.         {
  731.             if(!response) return Bank_ShowMenu(playerid);
  732.  
  733.             SetPVarInt(playerid, "bankLoginAccount", strval(inputtext));
  734.             ShowPlayerDialog(playerid, DIALOG_BANK_LOGIN_PASS, DIALOG_STYLE_PASSWORD, "{F1C40F}Bank: {FFFFFF}Login", "Account Password:", "Login", "Cancel");
  735.             return 1;
  736.         }
  737.         /* ---------------------------------------------------------------------- */
  738.         case DIALOG_BANK_LOGIN_ID:
  739.         {
  740.             if(!response) return Bank_ShowMenu(playerid);
  741.             if(isnull(inputtext)) return ShowPlayerDialog(playerid, DIALOG_BANK_LOGIN_ID, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Login", "{E74C3C}You can't leave the ID empty.\n\n{FFFFFF}Account ID:", "Continue", "Cancel");
  742.  
  743.             SetPVarInt(playerid, "bankLoginAccount", strval(inputtext));
  744.             ShowPlayerDialog(playerid, DIALOG_BANK_LOGIN_PASS, DIALOG_STYLE_PASSWORD, "{F1C40F}Bank: {FFFFFF}Login", "Account Password:", "Login", "Cancel");
  745.             return 1;
  746.         }
  747.         /* ---------------------------------------------------------------------- */
  748.         case DIALOG_BANK_LOGIN_PASS:
  749.         {
  750.             if(!response) return Bank_ShowMenu(playerid);
  751.             if(isnull(inputtext)) return ShowPlayerDialog(playerid, DIALOG_BANK_LOGIN_PASS, DIALOG_STYLE_PASSWORD, "{F1C40F}Bank: {FFFFFF}Login", "{E74C3C}You can't leave the password empty.\n\n{FFFFFF}Account Password:", "Login", "Cancel");
  752.  
  753.             new query[200], id = GetPVarInt(playerid, "bankLoginAccount");
  754.             mysql_format(BankSQLHandle, query, sizeof(query), "SELECT Owner, LastAccess, FROM_UNIXTIME(LastAccess, '%%d/%%m/%%Y %%H:%%i:%%s') AS Last FROM bank_accounts WHERE ID=%d && Password=md5('%e') && Disabled=0 LIMIT 1", id, inputtext);
  755.             mysql_tquery(BankSQLHandle, query, "OnBankAccountLogin", "ii", playerid, id);
  756.             return 1;
  757.         }
  758.         /* ---------------------------------------------------------------------- */
  759.         case DIALOG_BANK_DEPOSIT:
  760.         {
  761.             if(!response) return Bank_ShowMenu(playerid);
  762.             if(CurrentAccountID[playerid] == -1) return 1;
  763.             if(isnull(inputtext)) return ShowPlayerDialog(playerid, DIALOG_BANK_DEPOSIT, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Deposit", "{E74C3C}You can't leave the input empty.\n\n{FFFFFF}How much money do you want to deposit?", "Deposit", "Back");
  764.             new amount = strval(inputtext);
  765.             if(!(1 <= amount <= (GetPVarInt(playerid, "usingATM") ? 5000000 : 250000000))) return ShowPlayerDialog(playerid, DIALOG_BANK_DEPOSIT, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Deposit", "{E74C3C}You can't deposit less than $1 or more than $250,000,000 at once. ($5,000,000 at once on ATMs)\n\n{FFFFFF}How much money do you want to deposit?", "Deposit", "Back");
  766.             if(amount > GetPlayerMoney(playerid)) return ShowPlayerDialog(playerid, DIALOG_BANK_DEPOSIT, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Deposit", "{E74C3C}You don't have enough money.\n\n{FFFFFF}How much money do you want to deposit?", "Deposit", "Back");
  767.             if((amount + Bank_GetBalance(CurrentAccountID[playerid])) > ACCOUNT_LIMIT)
  768.             {
  769.                 SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't deposit any more money to this account.");
  770.                 return Bank_ShowMenu(playerid);
  771.             }
  772.  
  773.             new query[96];
  774.             mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Balance=Balance+%d WHERE ID=%d && Disabled=0", amount, CurrentAccountID[playerid]);
  775.             mysql_tquery(BankSQLHandle, query, "OnBankAccountDeposit", "ii", playerid, amount);
  776.             return 1;
  777.         }
  778.         /* ---------------------------------------------------------------------- */
  779.         case DIALOG_BANK_WITHDRAW:
  780.         {
  781.             if(!response) return Bank_ShowMenu(playerid);
  782.             if(CurrentAccountID[playerid] == -1) return 1;
  783.             if(isnull(inputtext)) return ShowPlayerDialog(playerid, DIALOG_BANK_WITHDRAW, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Withdraw", "{E74C3C}You can't leave the input empty.\n\n{FFFFFF}How much money do you want to withdraw?", "Withdraw", "Back");
  784.             new amount = strval(inputtext);
  785.             if(!(1 <= amount <= (GetPVarInt(playerid, "usingATM") ? 5000000 : 250000000))) return ShowPlayerDialog(playerid, DIALOG_BANK_WITHDRAW, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Withdraw", "{E74C3C}You can't withdraw less than $1 or more than $250,000,000 at once. ($5,000,000 at once on ATMs)\n\n{FFFFFF}How much money do you want to withdraw?", "Withdraw", "Back");
  786.             if(amount > Bank_GetBalance(CurrentAccountID[playerid])) return ShowPlayerDialog(playerid, DIALOG_BANK_WITHDRAW, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Withdraw", "{E74C3C}Account doesn't have enough money.\n\n{FFFFFF}How much money do you want to withdraw?", "Withdraw", "Back");
  787.  
  788.             new query[96];
  789.             mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Balance=Balance-%d WHERE ID=%d && Disabled=0", amount, CurrentAccountID[playerid]);
  790.             mysql_tquery(BankSQLHandle, query, "OnBankAccountWithdraw", "ii", playerid, amount);
  791.             return 1;
  792.         }
  793.         /* ---------------------------------------------------------------------- */
  794.         case DIALOG_BANK_TRANSFER_1:
  795.         {
  796.             if(!response) return Bank_ShowMenu(playerid);
  797.             if(CurrentAccountID[playerid] == -1) return 1;
  798.             if(isnull(inputtext)) return ShowPlayerDialog(playerid, DIALOG_BANK_TRANSFER_1, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Transfer", "{E74C3C}You can't leave the input empty.\n\n{FFFFFF}Specify an account ID:", "Continue", "Back");
  799.             if(strval(inputtext) == CurrentAccountID[playerid]) return ShowPlayerDialog(playerid, DIALOG_BANK_TRANSFER_1, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Transfer", "{E74C3C}You can't transfer money to your current account.\n\n{FFFFFF}Specify an account ID:", "Continue", "Back");
  800.             SetPVarInt(playerid, "bankTransferAccount", strval(inputtext));
  801.             ShowPlayerDialog(playerid, DIALOG_BANK_TRANSFER_2, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Transfer", "Specify an amount:", "Transfer", "Back");
  802.             return 1;
  803.         }
  804.         /* ---------------------------------------------------------------------- */
  805.         case DIALOG_BANK_TRANSFER_2:
  806.         {
  807.             if(!response) return ShowPlayerDialog(playerid, DIALOG_BANK_TRANSFER_1, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Transfer", "Specify an account ID:", "Continue", "Back");
  808.             if(CurrentAccountID[playerid] == -1) return 1;
  809.             if(isnull(inputtext)) return ShowPlayerDialog(playerid, DIALOG_BANK_TRANSFER_2, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Transfer", "{E74C3C}You can't leave the input empty.\n\n{FFFFFF}Specify an amount:", "Transfer", "Back");
  810.             new amount = strval(inputtext);
  811.             if(!(1 <= amount <= (GetPVarInt(playerid, "usingATM") ? 5000000 : 250000000))) return ShowPlayerDialog(playerid, DIALOG_BANK_TRANSFER_2, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Transfer", "{E74C3C}You can't transfer less than $1 or more than $250,000,000 at once. ($5,000,000 on ATMs)\n\n{FFFFFF}Specify an amount:", "Transfer", "Back");
  812.             if(amount > Bank_GetBalance(CurrentAccountID[playerid])) return ShowPlayerDialog(playerid, DIALOG_BANK_TRANSFER_2, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Transfer", "{E74C3C}Account doesn't have enough money.\n\n{FFFFFF}Specify an amount:", "Transfer", "Back");
  813.             new id = GetPVarInt(playerid, "bankTransferAccount");
  814.             if((amount + Bank_GetBalance(id)) > ACCOUNT_LIMIT)
  815.             {
  816.                 SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Can't deposit any more money to the account you specified.");
  817.                 return Bank_ShowMenu(playerid);
  818.             }
  819.  
  820.             new query[96];
  821.             mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Balance=Balance+%d WHERE ID=%d && Disabled=0", amount, id);
  822.             mysql_tquery(BankSQLHandle, query, "OnBankAccountTransfer", "iii", playerid, id, amount);
  823.             return 1;
  824.         }
  825.         /* ---------------------------------------------------------------------- */
  826.         case DIALOG_BANK_PASSWORD:
  827.         {
  828.             if(!response) return Bank_ShowMenu(playerid);
  829.             if(CurrentAccountID[playerid] == -1) return 1;
  830.             if(isnull(inputtext)) return ShowPlayerDialog(playerid, DIALOG_BANK_PASSWORD, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Change Password", "{E74C3C}You can't leave the input empty.\n\n{FFFFFF}Write a new password:", "Change", "Back");
  831.             if(strlen(inputtext) > 16) return ShowPlayerDialog(playerid, DIALOG_BANK_PASSWORD, DIALOG_STYLE_INPUT, "{F1C40F}Bank: {FFFFFF}Change Password", "{E74C3C}New password can't be more than 16 characters.\n\n{FFFFFF}Write a new password:", "Change", "Back");
  832.  
  833.             new query[128];
  834.             mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Password=md5('%e') WHERE ID=%d && Disabled=0", inputtext, CurrentAccountID[playerid]);
  835.             mysql_tquery(BankSQLHandle, query, "OnBankAccountPassChange", "is", playerid, inputtext);
  836.             return 1;
  837.         }
  838.         /* ---------------------------------------------------------------------- */
  839.         case DIALOG_BANK_REMOVE:
  840.         {
  841.             if(!response) return Bank_ShowMenu(playerid);
  842.             if(CurrentAccountID[playerid] == -1) return 1;
  843.  
  844.             new query[96], amount = Bank_GetBalance(CurrentAccountID[playerid]);
  845.             mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Disabled=1 WHERE ID=%d", CurrentAccountID[playerid]);
  846.             mysql_tquery(BankSQLHandle, query, "OnBankAccountDeleted", "iii", playerid, CurrentAccountID[playerid], amount);
  847.             return 1;
  848.         }
  849.         /* ---------------------------------------------------------------------- */
  850.         case DIALOG_BANK_LOGS:
  851.         {
  852.             if(!response) return Bank_ShowMenu(playerid);
  853.             if(CurrentAccountID[playerid] == -1) return 1;
  854.  
  855.             new typelist[6] = {TYPE_NONE, TYPE_DEPOSIT, TYPE_WITHDRAW, TYPE_TRANSFER, TYPE_LOGIN, TYPE_PASSCHANGE};
  856.             LogListType[playerid] = typelist[listitem + 1];
  857.             LogListPage[playerid] = 0;
  858.             Bank_ShowLogs(playerid);
  859.             return 1;
  860.         }
  861.         /* ---------------------------------------------------------------------- */
  862.         case DIALOG_BANK_LOG_PAGE:
  863.         {
  864.             if(CurrentAccountID[playerid] == -1 || LogListType[playerid] == TYPE_NONE) return 1;
  865.             if(!response) {
  866.                 LogListPage[playerid]--;
  867.                 if(LogListPage[playerid] < 0) return Bank_ShowLogMenu(playerid);
  868.             }else{
  869.                 LogListPage[playerid]++;
  870.             }
  871.  
  872.             Bank_ShowLogs(playerid);
  873.             return 1;
  874.         }
  875.         /* ---------------------------------------------------------------------- */
  876.     }
  877.  
  878.     return 0;
  879. }
  880.  
  881. public OnPlayerEditDynamicObject(playerid, STREAMER_TAG_OBJECT objectid, response, Float:x, Float:y, Float:z, Float:rx, Float:ry, Float:rz)
  882. {
  883.     if(Iter_Contains(ATMs, EditingATMID[playerid]))
  884.     {
  885.         if(response == EDIT_RESPONSE_FINAL)
  886.         {
  887.             new id = EditingATMID[playerid];
  888.             ATMData[id][atmX] = x;
  889.             ATMData[id][atmY] = y;
  890.             ATMData[id][atmZ] = z;
  891.             ATMData[id][atmRX] = rx;
  892.             ATMData[id][atmRY] = ry;
  893.             ATMData[id][atmRZ] = rz;
  894.  
  895.             SetDynamicObjectPos(objectid, ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ]);
  896.             SetDynamicObjectRot(objectid, ATMData[id][atmRX], ATMData[id][atmRY], ATMData[id][atmRZ]);
  897.  
  898.             #if defined ATM_USE_MAPICON
  899.             Streamer_SetFloatData(STREAMER_TYPE_MAP_ICON, ATMData[id][atmIconID], E_STREAMER_X, ATMData[id][atmX]);
  900.             Streamer_SetFloatData(STREAMER_TYPE_MAP_ICON, ATMData[id][atmIconID], E_STREAMER_Y, ATMData[id][atmY]);
  901.             Streamer_SetFloatData(STREAMER_TYPE_MAP_ICON, ATMData[id][atmIconID], E_STREAMER_Z, ATMData[id][atmZ]);
  902.             #endif
  903.  
  904.             Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, ATMData[id][atmLabel], E_STREAMER_X, ATMData[id][atmX]);
  905.             Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, ATMData[id][atmLabel], E_STREAMER_Y, ATMData[id][atmY]);
  906.             Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, ATMData[id][atmLabel], E_STREAMER_Z, ATMData[id][atmZ] + 0.85);
  907.  
  908.             new query[144];
  909.             mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_atms SET PosX='%f', PosY='%f', PosZ='%f', RotX='%f', RotY='%f', RotZ='%f' WHERE ID=%d", x, y, z, rx, ry, rz, id);
  910.             mysql_tquery(BankSQLHandle, query);
  911.  
  912.             EditingATMID[playerid] = -1;
  913.         }
  914.  
  915.         if(response == EDIT_RESPONSE_CANCEL)
  916.         {
  917.             new id = EditingATMID[playerid];
  918.             SetDynamicObjectPos(objectid, ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ]);
  919.             SetDynamicObjectRot(objectid, ATMData[id][atmRX], ATMData[id][atmRY], ATMData[id][atmRZ]);
  920.             EditingATMID[playerid] = -1;
  921.         }
  922.     }
  923.  
  924.     return 1;
  925. }
  926.  
  927. #if defined ROBBABLE_ATMS
  928. public OnPlayerShootDynamicObject(playerid, weaponid, STREAMER_TAG_OBJECT objectid, Float:x, Float:y, Float:z)
  929. {
  930.     if(Streamer_GetIntData(STREAMER_TYPE_OBJECT, objectid, E_STREAMER_MODEL_ID) == 19324)
  931.     {
  932.         new dataArray[E_ATMDATA];
  933.         Streamer_GetArrayData(STREAMER_TYPE_OBJECT, objectid, E_STREAMER_EXTRA_ID, dataArray);
  934.  
  935.         if(strlen(dataArray[IDString]) && !strcmp(dataArray[IDString], "atm_sys") && Iter_Contains(ATMs, dataArray[refID]) && ATMData[ dataArray[refID] ][atmRegen] == 0)
  936.         {
  937.             new id = dataArray[refID], string[64], Float: damage = GetWeaponDamageFromDistance(weaponid, GetPlayerDistanceFromPoint(playerid, ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ])) / 1.5;
  938.             ATMData[id][atmHealth] -= damage;
  939.  
  940.             if(ATMData[id][atmHealth] < 0.0) {
  941.                 ATMData[id][atmHealth] = 0.0;
  942.  
  943.                 format(string, sizeof(string), "ATM (%d)\n\n{FFFFFF}Out of Service\n{E74C3C}%s", id, ConvertToMinutes(ATM_REGEN));
  944.                 UpdateDynamic3DTextLabelText(ATMData[id][atmLabel], 0x1ABC9CFF, string);
  945.  
  946.                 ATMData[id][atmRegen] = ATM_REGEN;
  947.                 ATMData[id][atmTimer] = SetTimerEx("ATM_Regen", 1000, true, "i", id);
  948.                 Streamer_SetIntData(STREAMER_TYPE_OBJECT, objectid, E_STREAMER_MODEL_ID, 2943);
  949.  
  950.                 new Float: a = ATMData[id][atmRZ] + 180.0;
  951.                 ATMData[id][atmPickup] = CreateDynamicPickup(1212, 1, ATMData[id][atmX] + (1.25 * floatsin(-a, degrees)), ATMData[id][atmY] + (1.25 * floatcos(-a, degrees)), ATMData[id][atmZ] - 0.25);
  952.  
  953.                 if(IsValidDynamicPickup(ATMData[id][atmPickup]))
  954.                 {
  955.                     new pickupDataArray[E_ATMDATA];
  956.                     format(pickupDataArray[IDString], 8, "atm_sys");
  957.                     pickupDataArray[refID] = id;
  958.                     Streamer_SetArrayData(STREAMER_TYPE_PICKUP, ATMData[id][atmPickup], E_STREAMER_EXTRA_ID, pickupDataArray);
  959.                 }
  960.  
  961.                 Streamer_Update(playerid);
  962.             }else{
  963.                 format(string, sizeof(string), "ATM (%d)\n\n{FFFFFF}Use {F1C40F}/atm!\n%s", id, ATM_ReturnDmgText(id));
  964.                 UpdateDynamic3DTextLabelText(ATMData[id][atmLabel], 0x1ABC9CFF, string);
  965.             }
  966.  
  967.             PlayerPlaySound(playerid, 17802, 0.0, 0.0, 0.0);
  968.         }
  969.     }
  970.  
  971.     return 1;
  972. }
  973.  
  974. public OnPlayerPickUpDynamicPickup(playerid, pickupid)
  975. {
  976.     if(Streamer_GetIntData(STREAMER_TYPE_PICKUP, pickupid, E_STREAMER_MODEL_ID) == 1212)
  977.     {
  978.         new dataArray[E_ATMDATA];
  979.         Streamer_GetArrayData(STREAMER_TYPE_PICKUP, pickupid, E_STREAMER_EXTRA_ID, dataArray);
  980.  
  981.         if(strlen(dataArray[IDString]) && !strcmp(dataArray[IDString], "atm_sys"))
  982.         {
  983.             new money = RandomEx(ATM_ROB_MIN, ATM_ROB_MAX), string[64];
  984.             format(string, sizeof(string), "ATM: {FFFFFF}You stole {2ECC71}%s {FFFFFF}from the ATM.", formatInt(money));
  985.             SendClientMessage(playerid, 0x3498DBFF, string);
  986.             GivePlayerMoney(playerid, money);
  987.  
  988.             ATMData[ dataArray[refID] ][atmPickup] = -1;
  989.             DestroyDynamicPickup(pickupid);
  990.         }
  991.     }
  992.  
  993.     return 1;
  994. }
  995. #endif
  996.  
  997. forward LoadBankers();
  998. public LoadBankers()
  999. {
  1000.     new rows = cache_num_rows();
  1001.     if(rows)
  1002.     {
  1003.         new id, label_string[64];
  1004.         for(new i; i < rows; i++)
  1005.         {
  1006.             cache_get_value_name_int(i, "ID", id);
  1007.             cache_get_value_name_int(i, "Skin", BankerData[id][Skin]);
  1008.             cache_get_value_name_float(i, "PosX", BankerData[id][bankerX]);
  1009.             cache_get_value_name_float(i, "PosY", BankerData[id][bankerY]);
  1010.             cache_get_value_name_float(i, "PosZ", BankerData[id][bankerZ]);
  1011.             cache_get_value_name_float(i, "PosA", BankerData[id][bankerA]);
  1012.  
  1013.             BankerData[id][bankerActorID] = CreateActor(BankerData[id][Skin], BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ], BankerData[id][bankerA]);
  1014.             if(!IsValidActor(BankerData[id][bankerActorID])) {
  1015.                 printf("  [Bank System] Couldn't create an actor for banker ID %d.", id);
  1016.             }else{
  1017.                 SetActorInvulnerable(BankerData[id][bankerActorID], true); // people may use a version where actors aren't invulnerable by default
  1018.             }
  1019.  
  1020.             #if defined BANKER_USE_MAPICON
  1021.             BankerData[id][bankerIconID] = CreateDynamicMapIcon(BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ], 58, 0, .streamdistance = BANKER_ICON_RANGE);
  1022.             #endif
  1023.  
  1024.             format(label_string, sizeof(label_string), "Banker (%d)\n\n{FFFFFF}Use {F1C40F}/bank!", id);
  1025.             BankerData[id][bankerLabel] = CreateDynamic3DTextLabel(label_string, 0x1ABC9CFF, BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ] + 0.25, 5.0, .testlos = 1);
  1026.  
  1027.             Iter_Add(Bankers, id);
  1028.         }
  1029.     }
  1030.  
  1031.     printf("  [Bank System] Loaded %d bankers.", Iter_Count(Bankers));
  1032.     return 1;
  1033. }
  1034.  
  1035. forward LoadATMs();
  1036. public LoadATMs()
  1037. {
  1038.     new rows = cache_num_rows();
  1039.     if(rows)
  1040.     {
  1041.         new id, label_string[64];
  1042.         #if defined ROBBABLE_ATMS
  1043.         new dataArray[E_ATMDATA];
  1044.         #endif
  1045.        
  1046.         for(new i; i < rows; i++)
  1047.         {
  1048.             cache_get_value_name_int(i, "ID", id);
  1049.             cache_get_value_name_float(i, "PosX", ATMData[id][atmX]);
  1050.             cache_get_value_name_float(i, "PosY", ATMData[id][atmY]);
  1051.             cache_get_value_name_float(i, "PosZ", ATMData[id][atmZ]);
  1052.             cache_get_value_name_float(i, "RotX", ATMData[id][atmRX]);
  1053.             cache_get_value_name_float(i, "RotY", ATMData[id][atmRY]);
  1054.             cache_get_value_name_float(i, "RotZ", ATMData[id][atmRZ]);
  1055.  
  1056.             ATMData[id][atmObjID] = CreateDynamicObject(19324, ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ], ATMData[id][atmRX], ATMData[id][atmRY], ATMData[id][atmRZ]);
  1057.  
  1058.             #if defined ROBBABLE_ATMS
  1059.             if(IsValidDynamicObject(ATMData[id][atmObjID])) {
  1060.                 format(dataArray[IDString], 8, "atm_sys");
  1061.                 dataArray[refID] = id;
  1062.  
  1063.                 Streamer_SetArrayData(STREAMER_TYPE_OBJECT, ATMData[id][atmObjID], E_STREAMER_EXTRA_ID, dataArray);
  1064.             }else{
  1065.                 printf("  [Bank System] Couldn't create an ATM object for ATM ID %d.", id);
  1066.             }
  1067.             #else
  1068.             if(!IsValidDynamicObject(ATMData[id][atmObjID])) printf("  [Bank System] Couldn't create an ATM object for ATM ID %d.", id);
  1069.             #endif
  1070.            
  1071.             #if defined ATM_USE_MAPICON
  1072.             ATMData[id][atmIconID] = CreateDynamicMapIcon(ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ], 52, 0, .streamdistance = ATM_ICON_RANGE);
  1073.             #endif
  1074.  
  1075.             format(label_string, sizeof(label_string), "ATM (%d)\n\n{FFFFFF}Use {F1C40F}/atm!", id);
  1076.             ATMData[id][atmLabel] = CreateDynamic3DTextLabel(label_string, 0x1ABC9CFF, ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ] + 0.85, 5.0, .testlos = 1);
  1077.  
  1078.             Iter_Add(ATMs, id);
  1079.         }
  1080.     }
  1081.  
  1082.     printf("  [Bank System] Loaded %d ATMs.", Iter_Count(ATMs));
  1083.     return 1;
  1084. }
  1085.  
  1086. forward OnBankAccountCreated(playerid, pass[]);
  1087. public OnBankAccountCreated(playerid, pass[])
  1088. {
  1089.     GivePlayerMoney(playerid, -ACCOUNT_PRICE);
  1090.  
  1091.     new id = cache_insert_id(), string[64];
  1092.     SendClientMessage(playerid, 0x3498DBFF, "BANK: {FFFFFF}Successfully created an account for you!");
  1093.  
  1094.     format(string, sizeof(string), "BANK: {FFFFFF}Your account ID: {F1C40F}%d", id);
  1095.     SendClientMessage(playerid, 0x3498DBFF, string);
  1096.  
  1097.     format(string, sizeof(string), "BANK: {FFFFFF}Your account password: {F1C40F}%s", pass);
  1098.     SendClientMessage(playerid, 0x3498DBFF, string);
  1099.     return 1;
  1100. }
  1101.  
  1102. forward OnBankAccountLogin(playerid, id);
  1103. public OnBankAccountLogin(playerid, id)
  1104. {
  1105.     if(cache_num_rows() > 0) {
  1106.         new string[128], owner[MAX_PLAYER_NAME], last_access, ldate[24];
  1107.         cache_get_value_name(0, "Owner", owner);
  1108.         cache_get_value_name_int(0, "LastAccess", last_access);
  1109.         cache_get_value_name(0, "Last", ldate);
  1110.  
  1111.         format(string, sizeof(string), "BANK: {FFFFFF}This account is owned by {F1C40F}%s.", owner);
  1112.         SendClientMessage(playerid, 0x3498DBFF, string);
  1113.         format(string, sizeof(string), "BANK: {FFFFFF}Last Accessed On: {F1C40F}%s", (last_access == 0) ? ("Never") : ldate);
  1114.         SendClientMessage(playerid, 0x3498DBFF, string);
  1115.  
  1116.         CurrentAccountID[playerid] = id;
  1117.         Bank_ShowMenu(playerid);
  1118.  
  1119.         new query[96];
  1120.         mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET LastAccess=UNIX_TIMESTAMP() WHERE ID=%d && Disabled=0", id);
  1121.         mysql_tquery(BankSQLHandle, query);
  1122.  
  1123.         Bank_SaveLog(playerid, TYPE_LOGIN, id, -1, 0);
  1124.     }else{
  1125.         SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid credentials.");
  1126.         Bank_ShowMenu(playerid);
  1127.     }
  1128.  
  1129.     return 1;
  1130. }
  1131.  
  1132. forward OnBankAccountDeposit(playerid, amount);
  1133. public OnBankAccountDeposit(playerid, amount)
  1134. {
  1135.     if(cache_affected_rows() > 0) {
  1136.         new string[64];
  1137.         format(string, sizeof(string), "BANK: {FFFFFF}Successfully deposited {2ECC71}%s.", formatInt(amount));
  1138.         SendClientMessage(playerid, 0x3498DBFF, string);
  1139.  
  1140.         GivePlayerMoney(playerid, -amount);
  1141.         Bank_SaveLog(playerid, TYPE_DEPOSIT, CurrentAccountID[playerid], -1, amount);
  1142.     }else{
  1143.         SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Transaction failed.");
  1144.     }
  1145.  
  1146.     Bank_ShowMenu(playerid);
  1147.     return 1;
  1148. }
  1149.  
  1150. forward OnBankAccountWithdraw(playerid, amount);
  1151. public OnBankAccountWithdraw(playerid, amount)
  1152. {
  1153.     if(cache_affected_rows() > 0) {
  1154.         new string[64];
  1155.         format(string, sizeof(string), "BANK: {FFFFFF}Successfully withdrawn {2ECC71}%s.", formatInt(amount));
  1156.         SendClientMessage(playerid, 0x3498DBFF, string);
  1157.  
  1158.         GivePlayerMoney(playerid, amount);
  1159.         Bank_SaveLog(playerid, TYPE_WITHDRAW, CurrentAccountID[playerid], -1, amount);
  1160.     }else{
  1161.         SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Transaction failed.");
  1162.     }
  1163.  
  1164.     Bank_ShowMenu(playerid);
  1165.     return 1;
  1166. }
  1167.  
  1168. forward OnBankAccountTransfer(playerid, id, amount);
  1169. public OnBankAccountTransfer(playerid, id, amount)
  1170. {
  1171.     if(cache_affected_rows() > 0) {
  1172.         new query[144];
  1173.         mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Balance=Balance-%d WHERE ID=%d && Disabled=0", amount, CurrentAccountID[playerid]);
  1174.         mysql_tquery(BankSQLHandle, query, "OnBankAccountTransferDone", "iii", playerid, id, amount);
  1175.     }else{
  1176.         SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Transaction failed.");
  1177.         Bank_ShowMenu(playerid);
  1178.     }
  1179.  
  1180.     return 1;
  1181. }
  1182.  
  1183. forward OnBankAccountTransferDone(playerid, id, amount);
  1184. public OnBankAccountTransferDone(playerid, id, amount)
  1185. {
  1186.     if(cache_affected_rows() > 0) {
  1187.         new string[128];
  1188.         format(string, sizeof(string), "BANK: {FFFFFF}Successfully transferred {2ECC71}%s {FFFFFF}to account ID {F1C40F}%d.", formatInt(amount), id);
  1189.         SendClientMessage(playerid, 0x3498DBFF, string);
  1190.  
  1191.         Bank_SaveLog(playerid, TYPE_TRANSFER, CurrentAccountID[playerid], id, amount);
  1192.     }else{
  1193.         SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Transaction failed.");
  1194.  
  1195.     }
  1196.  
  1197.     Bank_ShowMenu(playerid);
  1198.     return 1;
  1199. }
  1200.  
  1201. forward OnBankAccountPassChange(playerid, newpass[]);
  1202. public OnBankAccountPassChange(playerid, newpass[])
  1203. {
  1204.     if(cache_affected_rows() > 0) {
  1205.         new string[128];
  1206.         format(string, sizeof(string), "BANK: {FFFFFF}Account password set to {F1C40F}%s.", newpass);
  1207.         SendClientMessage(playerid, 0x3498DBFF, string);
  1208.  
  1209.         Bank_SaveLog(playerid, TYPE_PASSCHANGE, CurrentAccountID[playerid], -1, 0);
  1210.     }else{
  1211.         SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Password change failed.");
  1212.     }
  1213.  
  1214.     Bank_ShowMenu(playerid);
  1215.     return 1;
  1216. }
  1217.  
  1218. forward OnBankAccountDeleted(playerid, id, amount);
  1219. public OnBankAccountDeleted(playerid, id, amount)
  1220. {
  1221.     if(cache_affected_rows() > 0) {
  1222.         GivePlayerMoney(playerid, amount);
  1223.  
  1224.         foreach(new i : Player)
  1225.         {
  1226.             if(i == playerid) continue;
  1227.             if(CurrentAccountID[i] == id) CurrentAccountID[i] = -1;
  1228.         }
  1229.  
  1230.         new string[128];
  1231.         format(string, sizeof(string), "BANK: {FFFFFF}Account removed, you got the {2ECC71}%s {FFFFFF}left in the account.", formatInt(amount));
  1232.         SendClientMessage(playerid, 0x3498DBFF, string);
  1233.     }else{
  1234.         SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Account removal failed.");
  1235.     }
  1236.  
  1237.     CurrentAccountID[playerid] = -1;
  1238.     Bank_ShowMenu(playerid);
  1239.     return 1;
  1240. }
  1241.  
  1242. forward OnBankAccountAdminEdit(playerid);
  1243. public OnBankAccountAdminEdit(playerid)
  1244. {
  1245.     if(cache_affected_rows() > 0) {
  1246.         SendClientMessage(playerid, 0x3498DBFF, "BANK: {FFFFFF}Account edited.");
  1247.     }else{
  1248.         SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Account editing failed. (No affected rows)");
  1249.     }
  1250.  
  1251.     return 1;
  1252. }
  1253.  
  1254. #if defined ROBBABLE_ATMS
  1255. forward ATM_Regen(id);
  1256. public ATM_Regen(id)
  1257. {
  1258.     new string[64];
  1259.  
  1260.     if(ATMData[id][atmRegen] > 1) {
  1261.         ATMData[id][atmRegen]--;
  1262.  
  1263.         format(string, sizeof(string), "ATM (%d)\n\n{FFFFFF}Out of Service\n{E74C3C}%s", id, ConvertToMinutes(ATMData[id][atmRegen]));
  1264.         UpdateDynamic3DTextLabelText(ATMData[id][atmLabel], 0x1ABC9CFF, string);
  1265.     }else if(ATMData[id][atmRegen] == 1) {
  1266.         if(IsValidDynamicPickup(ATMData[id][atmPickup])) DestroyDynamicPickup(ATMData[id][atmPickup]);
  1267.         KillTimer(ATMData[id][atmTimer]);
  1268.  
  1269.         ATMData[id][atmHealth] = ATM_HEALTH;
  1270.         ATMData[id][atmRegen] = 0;
  1271.         ATMData[id][atmTimer] = ATMData[id][atmPickup] = -1;
  1272.  
  1273.         Streamer_SetIntData(STREAMER_TYPE_OBJECT, ATMData[id][atmObjID], E_STREAMER_MODEL_ID, 19324);
  1274.  
  1275.         format(string, sizeof(string), "ATM (%d)\n\n{FFFFFF}Use {F1C40F}/atm!", id);
  1276.         UpdateDynamic3DTextLabelText(ATMData[id][atmLabel], 0x1ABC9CFF, string);
  1277.     }
  1278.  
  1279.     return 1;
  1280. }
  1281. #endif
  1282.  
  1283. // Player Commands
  1284. CMD:bank(playerid, params[])
  1285. {
  1286.     if(!IsPlayerNearBanker(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near a banker.");
  1287.     SetPVarInt(playerid, "usingATM", 0);
  1288.     Bank_ShowMenu(playerid);
  1289.     return 1;
  1290. }
  1291.  
  1292. CMD:atm(playerid, params[])
  1293. {
  1294.     new id = GetClosestATM(playerid);
  1295.     if(id == -1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near an ATM.");
  1296.     #if defined ROBBABLE_ATMS
  1297.     if(ATMData[id][atmRegen] > 0) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}This ATM is out of service.");
  1298.     #endif
  1299.  
  1300.     SetPVarInt(playerid, "usingATM", 1);
  1301.     Bank_ShowMenu(playerid);
  1302.     return 1;
  1303. }
  1304.  
  1305. // Admin Commands
  1306. CMD:asetowner(playerid, params[])
  1307. {
  1308.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1309.     new id, owner[MAX_PLAYER_NAME];
  1310.     if(sscanf(params, "is[24]", id, owner)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/asetowner [account id] [new owner]");
  1311.     new query[128];
  1312.     mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Owner='%e' WHERE ID=%d", owner, id);
  1313.     mysql_tquery(BankSQLHandle, query, "OnBankAccountAdminEdit", "i", playerid);
  1314.     return 1;
  1315. }
  1316.  
  1317. CMD:asetpassword(playerid, params[])
  1318. {
  1319.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1320.     new id, password[16];
  1321.     if(sscanf(params, "is[16]", id, password)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/asetpassword [account id] [new password]");
  1322.     new query[128];
  1323.     mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Password=md5('%e') WHERE ID=%d", password, id);
  1324.     mysql_tquery(BankSQLHandle, query, "OnBankAccountAdminEdit", "i", playerid);
  1325.     return 1;
  1326. }
  1327.  
  1328. CMD:asetbalance(playerid, params[])
  1329. {
  1330.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1331.     new id, balance;
  1332.     if(sscanf(params, "ii", id, balance)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/asetbalance [account id] [balance]");
  1333.     if(balance > ACCOUNT_LIMIT) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Balance you specified exceeds account money limit.");
  1334.     new query[128];
  1335.     mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Balance=%d WHERE ID=%d", balance, id);
  1336.     mysql_tquery(BankSQLHandle, query, "OnBankAccountAdminEdit", "i", playerid);
  1337.     return 1;
  1338. }
  1339.  
  1340. CMD:aclearlogs(playerid, params[])
  1341. {
  1342.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1343.     new id, type;
  1344.     if(sscanf(params, "iI(0)", id, type))
  1345.     {
  1346.         SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/aclearlogs [account id] [log type (optional)]");
  1347.         SendClientMessage(playerid, 0xE88732FF, "TYPES: {FFFFFF}0- All | 1- Logins | 2- Deposits | 3- Withdraws | 4- Transfers | 5- Password Changes");
  1348.         return 1;
  1349.     }
  1350.  
  1351.     new query[128];
  1352.     if(type > 0) {
  1353.         mysql_format(BankSQLHandle, query, sizeof(query), "DELETE FROM bank_logs WHERE AccountID=%d && Type=%d", id, type);
  1354.     }else{
  1355.         mysql_format(BankSQLHandle, query, sizeof(query), "DELETE FROM bank_logs WHERE AccountID=%d", id);
  1356.     }
  1357.  
  1358.     mysql_tquery(BankSQLHandle, query, "OnBankAccountAdminEdit", "i", playerid);
  1359.     return 1;
  1360. }
  1361.  
  1362. CMD:aremoveaccount(playerid, params[])
  1363. {
  1364.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1365.     new id;
  1366.     if(sscanf(params, "i", id)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/aremoveaccount [account id]");
  1367.     foreach(new i : Player)
  1368.     {
  1369.         if(CurrentAccountID[i] == id) CurrentAccountID[i] = -1;
  1370.     }
  1371.  
  1372.     new query[128];
  1373.     mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Disabled=1 WHERE ID=%d", id);
  1374.     mysql_tquery(BankSQLHandle, query, "OnBankAccountAdminEdit", "i", playerid);
  1375.     return 1;
  1376. }
  1377.  
  1378. CMD:areturnaccount(playerid, params[])
  1379. {
  1380.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1381.     new id;
  1382.     if(sscanf(params, "i", id)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/areturnaccount [account id]");
  1383.     new query[128];
  1384.     mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bank_accounts SET Disabled=0 WHERE ID=%d", id);
  1385.     mysql_tquery(BankSQLHandle, query, "OnBankAccountAdminEdit", "i", playerid);
  1386.     return 1;
  1387. }
  1388.  
  1389. // Admin Commands for Bankers
  1390. CMD:createbanker(playerid, params[])
  1391. {
  1392.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1393.     new id = Iter_Free(Bankers);
  1394.     if(id == -1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Can't create any more bankers.");
  1395.     new skin;
  1396.     if(sscanf(params, "i", skin)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/createbanker [skin id]");
  1397.     if(!(0 <= skin <= 311)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid skin ID.");
  1398.     BankerData[id][Skin] = skin;
  1399.     GetPlayerPos(playerid, BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ]);
  1400.     GetPlayerFacingAngle(playerid, BankerData[id][bankerA]);
  1401.     SetPlayerPos(playerid, BankerData[id][bankerX] + (1.0 * floatsin(-BankerData[id][bankerA], degrees)), BankerData[id][bankerY] + (1.0 * floatcos(-BankerData[id][bankerA], degrees)), BankerData[id][bankerZ]);
  1402.  
  1403.     BankerData[id][bankerActorID] = CreateActor(skin, BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ], BankerData[id][bankerA]);
  1404.     if(IsValidActor(BankerData[id][bankerActorID])) SetActorInvulnerable(BankerData[id][bankerActorID], true);
  1405.  
  1406.     #if defined BANKER_USE_MAPICON
  1407.     BankerData[id][bankerIconID] = CreateDynamicMapIcon(BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ], 58, 0, .streamdistance = BANKER_ICON_RANGE);
  1408.     #endif
  1409.  
  1410.     new label_string[64];
  1411.     format(label_string, sizeof(label_string), "Banker (%d)\n\n{FFFFFF}Use {F1C40F}/bank!", id);
  1412.     BankerData[id][bankerLabel] = CreateDynamic3DTextLabel(label_string, 0x1ABC9CFF, BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ] + 0.25, 5.0, .testlos = 1);
  1413.  
  1414.     new query[144];
  1415.     mysql_format(BankSQLHandle, query, sizeof(query), "INSERT INTO bankers SET ID=%d, Skin=%d, PosX='%f', PosY='%f', PosZ='%f', PosA='%f'", id, skin, BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ], BankerData[id][bankerA]);
  1416.     mysql_tquery(BankSQLHandle, query);
  1417.  
  1418.     Iter_Add(Bankers, id);
  1419.     return 1;
  1420. }
  1421.  
  1422. CMD:setbankerpos(playerid, params[])
  1423. {
  1424.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1425.     new id;
  1426.     if(sscanf(params, "i", id)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/setbankerpos [banker id]");
  1427.     if(!Iter_Contains(Bankers, id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid banker ID.");
  1428.     GetPlayerPos(playerid, BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ]);
  1429.     GetPlayerFacingAngle(playerid, BankerData[id][bankerA]);
  1430.  
  1431.     DestroyActor(BankerData[id][bankerActorID]);
  1432.     BankerData[id][bankerActorID] = CreateActor(BankerData[id][Skin], BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ], BankerData[id][bankerA]);
  1433.     if(IsValidActor(BankerData[id][bankerActorID])) SetActorInvulnerable(BankerData[id][bankerActorID], true);
  1434.  
  1435.     #if defined BANKER_USE_MAPICON
  1436.     Streamer_SetFloatData(STREAMER_TYPE_MAP_ICON, BankerData[id][bankerIconID], E_STREAMER_X, BankerData[id][bankerX]);
  1437.     Streamer_SetFloatData(STREAMER_TYPE_MAP_ICON, BankerData[id][bankerIconID], E_STREAMER_Y, BankerData[id][bankerY]);
  1438.     Streamer_SetFloatData(STREAMER_TYPE_MAP_ICON, BankerData[id][bankerIconID], E_STREAMER_Z, BankerData[id][bankerZ]);
  1439.     #endif
  1440.  
  1441.     Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, BankerData[id][bankerLabel], E_STREAMER_X, BankerData[id][bankerX]);
  1442.     Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, BankerData[id][bankerLabel], E_STREAMER_Y, BankerData[id][bankerY]);
  1443.     Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, BankerData[id][bankerLabel], E_STREAMER_Z, BankerData[id][bankerZ]);
  1444.  
  1445.     SetPlayerPos(playerid, BankerData[id][bankerX] + (1.0 * floatsin(-BankerData[id][bankerA], degrees)), BankerData[id][bankerY] + (1.0 * floatcos(-BankerData[id][bankerA], degrees)), BankerData[id][bankerZ]);
  1446.  
  1447.     new query[144];
  1448.     mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bankers SET PosX='%f', PosY='%f', PosZ='%f', PosA='%f' WHERE ID=%d", BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ], BankerData[id][bankerA], id);
  1449.     mysql_tquery(BankSQLHandle, query);
  1450.     return 1;
  1451. }
  1452.  
  1453. CMD:setbankerskin(playerid, params[])
  1454. {
  1455.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1456.     new id, skin;
  1457.     if(sscanf(params, "ii", id, skin)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/setbankerskin [banker id] [skin id]");
  1458.     if(!Iter_Contains(Bankers, id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid banker ID.");
  1459.     if(!(0 <= skin <= 311)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid skin ID.");
  1460.     BankerData[id][Skin] = skin;
  1461.  
  1462.     if(IsValidActor(BankerData[id][bankerActorID])) DestroyActor(BankerData[id][bankerActorID]);
  1463.     BankerData[id][bankerActorID] = CreateActor(BankerData[id][Skin], BankerData[id][bankerX], BankerData[id][bankerY], BankerData[id][bankerZ], BankerData[id][bankerA]);
  1464.     if(IsValidActor(BankerData[id][bankerActorID])) SetActorInvulnerable(BankerData[id][bankerActorID], true);
  1465.  
  1466.     new query[48];
  1467.     mysql_format(BankSQLHandle, query, sizeof(query), "UPDATE bankers SET Skin=%d WHERE ID=%d", BankerData[id][Skin], id);
  1468.     mysql_tquery(BankSQLHandle, query);
  1469.     return 1;
  1470. }
  1471.  
  1472. CMD:removebanker(playerid, params[])
  1473. {
  1474.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1475.     new id;
  1476.     if(sscanf(params, "i", id)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/removebanker [banker id]");
  1477.     if(!Iter_Contains(Bankers, id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid banker ID.");
  1478.     if(IsValidActor(BankerData[id][bankerActorID])) DestroyActor(BankerData[id][bankerActorID]);
  1479.     BankerData[id][bankerActorID] = -1;
  1480.  
  1481.     #if defined BANKER_USE_MAPICON
  1482.     if(IsValidDynamicMapIcon(BankerData[id][bankerIconID])) DestroyDynamicMapIcon(BankerData[id][bankerIconID]);
  1483.     BankerData[id][bankerIconID] = -1;
  1484.     #endif
  1485.  
  1486.     if(IsValidDynamic3DTextLabel(BankerData[id][bankerLabel])) DestroyDynamic3DTextLabel(BankerData[id][bankerLabel]);
  1487.     BankerData[id][bankerLabel] = Text3D: -1;
  1488.  
  1489.     Iter_Remove(Bankers, id);
  1490.  
  1491.     new query[48];
  1492.     mysql_format(BankSQLHandle, query, sizeof(query), "DELETE FROM bankers WHERE ID=%d", id);
  1493.     mysql_tquery(BankSQLHandle, query);
  1494.     return 1;
  1495. }
  1496.  
  1497. // Admin Commands for ATMs
  1498. CMD:createatm(playerid, params[])
  1499. {
  1500.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1501.     new id = Iter_Free(ATMs);
  1502.     if(id == -1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Can't create any more ATMs.");
  1503.     ATMData[id][atmRX] = ATMData[id][atmRY] = 0.0;
  1504.  
  1505.     GetPlayerPos(playerid, ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ]);
  1506.     GetPlayerFacingAngle(playerid, ATMData[id][atmRZ]);
  1507.  
  1508.     ATMData[id][atmX] += (2.0 * floatsin(-ATMData[id][atmRZ], degrees));
  1509.     ATMData[id][atmY] += (2.0 * floatcos(-ATMData[id][atmRZ], degrees));
  1510.     ATMData[id][atmZ] -= 0.3;
  1511.  
  1512.     ATMData[id][atmObjID] = CreateDynamicObject(19324, ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ], ATMData[id][atmRX], ATMData[id][atmRY], ATMData[id][atmRZ]);
  1513.     if(IsValidDynamicObject(ATMData[id][atmObjID]))
  1514.     {
  1515.         #if defined ROBBABLE_ATMS
  1516.         new dataArray[E_ATMDATA];
  1517.         format(dataArray[IDString], 8, "atm_sys");
  1518.         dataArray[refID] = id;
  1519.         Streamer_SetArrayData(STREAMER_TYPE_OBJECT, ATMData[id][atmObjID], E_STREAMER_EXTRA_ID, dataArray);
  1520.         #endif
  1521.        
  1522.         EditingATMID[playerid] = id;
  1523.         EditDynamicObject(playerid, ATMData[id][atmObjID]);
  1524.     }
  1525.  
  1526.     #if defined ATM_USE_MAPICON
  1527.     ATMData[id][atmIconID] = CreateDynamicMapIcon(ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ], 52, 0, .streamdistance = ATM_ICON_RANGE);
  1528.     #endif
  1529.  
  1530.     new label_string[64];
  1531.     format(label_string, sizeof(label_string), "ATM (%d)\n\n{FFFFFF}Use {F1C40F}/atm!", id);
  1532.     ATMData[id][atmLabel] = CreateDynamic3DTextLabel(label_string, 0x1ABC9CFF, ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ] + 0.85, 5.0, .testlos = 1);
  1533.  
  1534.     new query[144];
  1535.     mysql_format(BankSQLHandle, query, sizeof(query), "INSERT INTO bank_atms SET ID=%d, PosX='%f', PosY='%f', PosZ='%f', RotX='%f', RotY='%f', RotZ='%f'", id, ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ], ATMData[id][atmRX], ATMData[id][atmRY], ATMData[id][atmRZ]);
  1536.     mysql_tquery(BankSQLHandle, query);
  1537.  
  1538.     Iter_Add(ATMs, id);
  1539.     return 1;
  1540. }
  1541.  
  1542. CMD:editatm(playerid, params[])
  1543. {
  1544.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1545.     new id;
  1546.     if(sscanf(params, "i", id)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/editatm [ATM id]");
  1547.     if(!Iter_Contains(ATMs, id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid ATM ID.");
  1548.     if(!IsPlayerInRangeOfPoint(playerid, 30.0, ATMData[id][atmX], ATMData[id][atmY], ATMData[id][atmZ])) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near the ATM you want to edit.");
  1549.     if(EditingATMID[playerid] != -1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're already editing an ATM.");
  1550.     EditingATMID[playerid] = id;
  1551.     EditDynamicObject(playerid, ATMData[id][atmObjID]);
  1552.     return 1;
  1553. }
  1554.  
  1555. CMD:removeatm(playerid, params[])
  1556. {
  1557.     if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
  1558.     new id;
  1559.     if(sscanf(params, "i", id)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/removeatm [ATM id]");
  1560.     if(!Iter_Contains(ATMs, id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid ATM ID.");
  1561.     if(IsValidDynamicObject(ATMData[id][atmObjID])) DestroyDynamicObject(ATMData[id][atmObjID]);
  1562.     ATMData[id][atmObjID] = -1;
  1563.  
  1564.     #if defined ATM_USE_MAPICON
  1565.     if(IsValidDynamicMapIcon(ATMData[id][atmIconID])) DestroyDynamicMapIcon(ATMData[id][atmIconID]);
  1566.     ATMData[id][atmIconID] = -1;
  1567.     #endif
  1568.  
  1569.     if(IsValidDynamic3DTextLabel(ATMData[id][atmLabel])) DestroyDynamic3DTextLabel(ATMData[id][atmLabel]);
  1570.     ATMData[id][atmLabel] = Text3D: -1;
  1571.  
  1572.     #if defined ROBBABLE_ATMS
  1573.     if(ATMData[id][atmTimer] != -1) KillTimer(ATMData[id][atmTimer]);
  1574.     ATMData[id][atmTimer] = -1;
  1575.  
  1576.     if(IsValidDynamicPickup(ATMData[id][atmPickup])) DestroyDynamicPickup(ATMData[id][atmPickup]);
  1577.     ATMData[id][atmPickup] = -1;
  1578.  
  1579.     ATMData[id][atmHealth] = ATM_HEALTH;
  1580.     ATMData[id][atmRegen] = 0;
  1581.     #endif
  1582.    
  1583.     Iter_Remove(ATMs, id);
  1584.    
  1585.     new query[48];
  1586.     mysql_format(BankSQLHandle, query, sizeof(query), "DELETE FROM bank_atms WHERE ID=%d", id);
  1587.     mysql_tquery(BankSQLHandle, query);
  1588.     return 1;
  1589. }
Add Comment
Please, Sign In to add comment