/*========================================================================= * 多倍長整数を利用した銀行サンプルスクリプト * created by Rayce based on mod2273 * * (1) 概略 * 2G Zenyを超えるような大きな金額を管理できる銀行です。 * キャラクター単位で保存されます。 * 拡張整数が配列を利用する関係上、データのセーブはCSVファイル * にて行います。 * サーバ分散をしている場合の挙動は未定義とします。 * * (2) 仕様 * 銀行自身が資産を保持しています。 * 引き出し時に手数料が発生しますが、その徴収額は銀行の資産に加算 * されていきます。 * 2週間に1度、銀行の資産から金利が支払われます。 * 資産がなくなった場合は破産となり、それまで預けていた預金は全て * 破棄されます。 * プレイヤーは銀行に預けることで金利を獲得できますが、その反面 * 破産して預金が戻ってこないリスクを背負うことになります。 * * (3) CSVファイルのデータ構造 * ,,,, ... * * 銀行の資産はアカウントIDが0として格納されます。 * また破産した場合はキャラIDを-1にします。 * 各データは拡張整数の仕様上逆読みになっているので、直接データを * 書き換える場合は十分注意してください。 * * (4) 変数一覧 * 'file$ : CSVファイル名 * 'max_zeny : mmo.hのMAX_ZENYの値と同じにする * 'commission : 手数料 * 'com_exp : 手数料を拡張整数に変換した配列 * 'rate : 金利利率(万分率) * 'lock : CSVアクセスロック、sleepを使う場合に排他制御が必要 * 'bankrupt : 破産中フラグ *------------------------------------------------------------------------- */ - script 銀行#main 112,{ /* 銀行の総資産が保存されている行を返す */ /* GetPoolRow() */ function GetPoolRow { // アカウントID = 0で入っている set '@row,csvfind('file$,0,0); if('@row < 0) { debugmes "bank data not found!!"; gmcommand "@mapexit"; // データがないのはおかしいのでサーバを落とす end; } return '@row; } /* 銀行資産以外のデータをクリア */ /* ClearData; */ function ClearData { set '@max,csvgetrows('file$); while('@max > 1) { csvsort 'file$,1,1; csvdelete 'file$,1; // ソート後は銀行データは0行目なので1行目から消し続ければ良い set '@max,'@max-1; } return; } /* 金利の支払い */ /* PayZeny; */ function PayZeny { // 銀行の資産データの取得 set '@row,GetPoolRow(); csvreadarray 'file$,'@row,'@data; copyarray '@pool,'@data[2],126; set '@max,csvgetrows('file$); for(set '@i,0; '@i<'@max; set '@i,'@i+1) { if('@i == '@row) continue; csvreadarray 'file$,'@i,'@data; copyarray '@val,'@data[2],126; callfunc "lmul_h",'@val,'@val,'rate; // rateは10000未満のはずなのでlmun_hで良い sleep 1; // 重くなるので処理分割 // 高速化のため10000で割るのではなく、1000で割ったものを // さらに10で割る、これならldiv_hが使える callfunc "ldiv_h",'@val,'@val,1000; callfunc "ldiv_h",'@val,'@val,10; if(callfunc("lcmp",'@pool,'@val) != 1) { // 資産がなくなるので破産 set '@dummy[0],0; set '@dummy[1],-1; // キャラIDを-1にする csvwritearray 'file$,'@row,'@dummy; set 'bankrupt,1; ClearData; // データの破棄 return; } // 銀行の総資産から差し引く callfunc "lsub",'@pool,'@pool,'@val; copyarray '@balance,'@data[2],126; callfunc "ladd",'@balance,'@balance,'@val; // 計算した金利を足す copyarray '@data[2],'@balance[0],126; csvwritearray 'file$,'@i,'@data; } // 銀行の総資産をCSVへ保存する copyarray '@pool[2],'@pool[0],126; set '@pool[0],0; set '@pool[1],0; csvwritearray 'file$,'@row,'@pool; } /* 拡張整数の配列を3桁ずつ区切って表示する、ldispの流用 */ /* Print() */ function Print { set '@max,getarraysize(getarg(0))-1; set '@pos,-3; // substrは負数を使う for(set '@i,0; '@i<'@max; set '@i,'@i+1) { set '@val$,getelementofarray(getarg(0),'@i); switch(getstrlen('@val$)) { case 1: set '@val$,"0000"; break; case 2: set '@val$,"00" + '@val$; break; case 3: set '@val$,"0" + '@val$; break; } set '@val$,substr('@val$,0,'@pos) + "," + substr('@val$,'@pos); set '@pos,'@pos+1; if('@pos == 0) { set '@val$,"," + '@val$; set '@pos,-3; } set '@str$,'@val$ + '@str$; } // 最上桁は0補正しない set '@val$,getelementofarray(getarg(0),'@i); if(getstrlen('@val$) > -'@pos) set '@val$,substr('@val$,0,'@pos) + "," + substr('@val$,'@pos); set '@str$,'@val$ + '@str$; return '@str$; } /* GMモード */ /* GM_Mode; */ function GM_Mode { mes "[GM管理モード]"; mes "GMモードを利用しますか?"; next; if(select("いいえ","はい") == 1) { return; } mes "[GM管理モード]"; mes "以下の項目から選択してください。"; next; set '@s,select("データのフラッシュ","データのリロード","金利支払い","破産させる","破産解除する", "手数料変更","金利の利率変更","取消し"); if('lock) { mes "[GM管理モード]"; mes "ロック中です。"; mes "CSVファイルへアクセス出来ません。"; close; } switch('@s) { case 1: csvflush 'file$; mes "[GM管理モード]"; mes "データをフラッシュしました。"; next; return; case 2: csvreload 'file$; mes "[GM管理モード]"; mes "リロードしました。"; next; return; case 3: if('bankrupt) { mes "[GM管理モード]"; mes "現在破産中により支払いは"; mes "不可能です。"; close; } set '@rid,getcharid(3); // PayZenyは内部でsleepするのでRIDを保存 set 'lock,1; PayZeny; set 'lock,0; if(!attachrid('@rid)) { end; } set '@row,GetPoolRow(); csvreadarray 'file$,'@row,'@data; copyarray '@pool,'@data[2],126; mes "[GM管理モード]"; mes "支払いを行いました。"; mes "現在銀行の総資産は"; mes "^4A4AFF" + Print('@pool) + "^000000 Zeny"; mes "です。"; next; return; case 4: if('bankrupt) { mes "[GM管理モード]"; mes "既に破産中です。"; close; } set 'lock,1; set '@dummy[0],0; set '@dummy[1],-1; // キャラIDを-1にする csvwritearray 'file$,'@row,'@dummy; set 'bankrupt,1; ClearData; // データの破棄 set 'lock,0; mes "[GM管理モード]"; mes "破産しました。"; next; return; case 5: if(!'bankrupt) { mes "[GM管理モード]"; mes "破産していません。"; close; } mes "[GM管理モード]"; mes "初期資産を入力してください。"; next; input '@val; if('@val <= 0) { mes "[GM管理モード]"; mes "値が不正です。"; close; } callfunc "lset",'@pool,""+'@val; // 文字列に変換して渡す mes "[GM管理モード]"; mes "単位を選択してください。"; next; switch (select("なし","K(キロ)","M(メガ)","G(ギガ)","T(テラ)")) { case 1: break; case 2: callfunc "lmul_h",'@pool,'@pool,1000; break; case 3: // 1000 * 1000 callfunc "lmul_h",'@pool,'@pool,1000; callfunc "lmul_h",'@pool,'@pool,1000; break; case 4: // 1000 * 1000 * 1000 callfunc "lmul_h",'@pool,'@pool,1000; callfunc "lmul_h",'@pool,'@pool,1000; callfunc "lmul_h",'@pool,'@pool,1000; break; case 5: // 1000 * 1000 * 1000 * 1000 callfunc "lmul_h",'@pool,'@pool,1000; callfunc "lmul_h",'@pool,'@pool,1000; callfunc "lmul_h",'@pool,'@pool,1000; callfunc "lmul_h",'@pool,'@pool,1000; break; } mes "[GM管理モード]"; mes "^4A4AFF" + Print('@pool) + "^000000 Zeny"; mes "に設定してよろしいですか?"; next; if(select("いいえ","はい") == 1) { return; } if('lock) { mes "[GM管理モード]"; mes "ロック中です。"; mes "CSVファイルへアクセス出来ません。"; close; } set '@row,GetPoolRow(); copyarray '@pool[2],'@pool[0],126; set '@pool[0],0; set '@pool[1],0; // キャラIDを0に戻す csvwritearray 'file$,'@row,'@pool; set 'bankrupt,0; mes "[GM管理モード]"; mes "破産解除しました。"; next; return; case 6: mes "[GM管理モード]"; mes "現在の手数料は ^4A4AFF" + Print('com_exp) + "^000000 Zeny"; mes "です。"; mes "値を入力してください。"; next; input '@val; if('@val < 0 || '@val >= 'max_zeny) { mes "[GM管理モード]"; mes "値が不正です。"; close; } set 'commission,'@val; cleararray 'com_exp,0,128; callfunc "lset",'com_exp,""+'commission; // 拡張整数をセットし直す mes "[GM管理モード]"; mes "手数料を ^4A4AFF" + Print('com_exp) + "^000000 Zeny"; mes "に変更しました。"; next; return; case 7: mes "[GM管理モード]"; mes "現在の利率は ^4A4AFF" + 'rate + "^000000 です。"; mes "値を入力してください。"; mes "万分率で計算されます。"; next; input '@val; if('@val < 0 || '@val >= 10000) { mes "[GM管理モード]"; mes "値が不正です。"; close; } if('lock) { mes "[GM管理モード]"; mes "ロック中です。"; mes "利率の変更は取り消されました。"; close; } set 'rate,'@val; mes "[GM管理モード]"; mes "利率を ^4A4AFF" + 'rate + "^000000 に"; mes "変更しました。"; next; return; case 8: return; } } /* Zenyの入力と補正 */ /* InputZeny() */ function InputZeny { input '@val; if('@val <= 0) { mes "[銀行職員]"; mes "値が不正です。"; mes "もう一度やり直してください。"; close; } mes "[銀行職員]"; mes "単位を選択してください。"; next; switch (select("なし","K(キロ)","M(メガ)","G(ギガ)")) { case 1: break; case 2: set '@val,'@val * 1000; break; case 3: set '@val,'@val * 1000 * 1000; break; case 4: // sd->status.zenyはint型なので3G以上は有り得ない if('@val >= 3) { mes "[銀行職員]"; mes "値が不正です。"; mes "もう一度やり直してください。"; close; } set '@val,'@val * 1000 * 1000 * 1000; break; } return '@val; } /* メンテナンス中 */ /* CheckMaintenance; */ function CheckMaintenance { if('lock) { // ロックオン状態 mes "[銀行職員]"; mes "メンテナンス中です。"; mes "しばらくお待ちください。"; close; } if('bankrupt) { // 破産状態 mes "[元・銀行職員]"; mes "カプラ銀行は残念ながら破産"; mes "しました・・・。"; mes "お預かりしていた預金は払い"; mes "戻されません。"; close; } return; } /* ここからmain */ if(getgmlevel() > 0) { GM_Mode; } CheckMaintenance; mes "[銀行職員]"; mes "カプラ銀行へようこそ。"; mes "本日はどのようなご用件でしょうか。"; next; switch (select("残高照会","引き出し","お預かり","取消し")) { case 1: CheckMaintenance; set '@row,csvfind('file$,1,getcharid(0)); if('@row < 0) { mes "[銀行職員]"; mes "現在お預かりしているZenyは"; mes "ありません。"; close; } csvreadarray 'file$,'@row,'@data; copyarray '@balance,'@data[2],126; mes "[銀行職員]"; mes "現在の残高は"; mes "^4A4AFF" + Print('@balance) + "^000000 Zeny"; mes "です。"; close; case 2: CheckMaintenance; if(csvfind('file$,1,getcharid(0)) < 0) { mes "[銀行職員]"; mes "現在お預かりしているZenyは"; mes "ありません。"; close; } mes "[銀行職員]"; mes "引き出しには手数料として^FF0000" +'commission+ "^000000 Zeny"; mes "が必要になります。"; mes "引き出し金額を入力してください。"; next; set '@val,InputZeny(); callfunc "lset",'@gain,""+'@val; // 文字列に変換して渡す mes "[銀行職員]"; mes "^4A4AFF" + Print('@gain) + "^000000 Zeny"; mes "お引き出しでしょうか?"; next; if(select("はい","いいえ") == 2) { break; } if('@val < 'commission) { mes "[銀行職員]"; mes "申し訳ありませんが"; mes "手数料以下の金額は引き出せません。"; close; } CheckMaintenance; // データ処理 set '@row,csvfind('file$,1,getcharid(0)); // 行が変化する可能性があるのでもう1度探す if('@row < 0) { mes "[銀行職員]"; mes "預金データが更新された可能性が"; mes "あります。"; mes "お手数ですが最初からやり直して"; mes "ください。"; close; } csvreadarray 'file$,'@row,'@data; copyarray '@balance,'@data[2],126; if(callfunc("lcmp",'@balance,'@gain) < 0) { mes "[銀行職員]"; mes "残高が不足しています。"; mes "もう一度よくご確認ください。"; close; } if('max_zeny - Zeny < '@val) { // MAX_ZENYオーバー mes "[銀行職員]"; mes "残念ながらZenyが所持限度額を"; mes "超えてしまうようです。"; mes "もう一度よくご確認ください。"; close; } callfunc "lsub",'@balance,'@balance,'@gain; if(callfunc("ldisp",'@balance) == "0") { // 残高がなくなったので削除 csvdelete 'file$,'@row; } else { copyarray '@data[2],'@balance,126; csvwritearray 'file$,'@row,'@data; } // 銀行の総資産に徴収した手数料を加算する set '@row,GetPoolRow(); csvreadarray 'file$,'@row,'@data; copyarray '@pool,'@data[2],126; callfunc "ladd",'@pool,'@pool,'com_exp; copyarray '@data[2],'@pool,126; csvwritearray 'file$,'@row,'@data; set Zeny,Zeny + '@val - 'commission; mes "[銀行職員]"; mes "お引き出し致しました。"; mes "現在の残高は"; mes "^4A4AFF" + Print('@balance) + "^000000 Zeny"; mes "です。"; mes "ご利用ありがとうございました。"; close; case 3: CheckMaintenance; mes "[銀行職員]"; mes "預けたい金額を入力してください。"; next; set '@val,InputZeny(); callfunc "lset",'@pay,""+'@val; // 文字列に変換して渡す mes "[銀行職員]"; mes "^4A4AFF" + Print('@pay) + "^000000 Zeny"; mes "お預けでしょうか?"; next; if(select("はい","いいえ") == 2) { break; } if('@val > Zeny) { mes "[銀行職員]"; mes "所持金が不足しています。"; mes "もう一度よくご確認ください。"; close; } CheckMaintenance; // データ処理 set '@row,csvfind('file$,1,getcharid(0)); if('@row < 0) { // new data set '@data[0],getcharid(3); set '@data[1],getcharid(0); copyarray '@data[2],'@pay,126; csvwritearray 'file$,csvgetrows('file$),'@data; set '@balance,-1; } else { // update csvreadarray 'file$,'@row,'@data; if(getarraysize('@data) > 122) { // 2+120を上限にしておく mes "[銀行職員]"; mes "現在のお預かり金額が多すぎます。"; mes "これ以上の入金はお控えください。"; close; } copyarray '@balance,'@data[2],126; callfunc "ladd",'@balance,'@balance,'@pay; copyarray '@data[2],'@balance,126; csvwritearray 'file$,'@row,'@data; } set Zeny,Zeny - '@val; mes "[銀行職員]"; mes "お預かり致しました。"; mes "現在の残高は"; mes "^4A4AFF" + Print(('@balance == -1)? '@pay: '@balance) + "^000000 Zeny"; mes "です。"; mes "ご利用ありがとうございました。"; close; case 4: break; } mes "[銀行職員]"; mes "そうですか。"; mes "またのご利用をお待ちしております。"; close; OnTimer3600000: // 1時間おきにデータをフラッシュしておく if(strnpcinfo(2) == "main") { csvflush 'file$; initnpctimer; } end; OnDay14: OnDay28: // 2週間に一回金利が加算される if(strnpcinfo(2) == "main" && !'bankrupt) { // 実行するのはduplicate元のみ set 'lock,1; PayZeny; set 'lock,0; } end; OnInit: // スクリプト依存変数はduplicateだと全て共通なので実行するのはmainだけで良い if(strnpcinfo(2) == "main") { set 'file$,"bankdata"; // csvファイル名 set 'max_zeny,1000000000; // MAX_ZENY 1G set 'commission,2000; // 手数料 set 'rate,5; // 金利(万分率)、0.0005%は大手銀行並の超低金利 // 手数料は頻繁に拡張整数演算するのでセットしておく callfunc "lset",'com_exp,""+'commission; // 銀行の総資産を設定する // 資産はアカウントID = 0で入っている set '@row,csvfind('file$,0,0); if('@row < 0) { callfunc "lset",'@pool,"10000000000"; // 初期資産10G copyarray '@pool[2],'@pool[0],126; set '@pool[0],0; set '@pool[1],0; csvwritearray 'file$,csvgetrows('file$),'@pool; end; } if(csvread('file$,'@row,1) == "-1") { // キャラIDが-1は破産中 set 'bankrupt,1; ClearData; // 念のためデータの破棄をしておく } initnpctimer; } end; } prontera.gat,154,191,4 duplicate(銀行#main) 銀行 113 izlude.gat,135,116,4 duplicate(銀行#main) 銀行 114