@パーティーII 改造の手引き

こちらのページでは@パーティーIIのちょっとした改造方法や不具合とその修正方法などを説明します!
初期の@パーティーIIについてのネタバレも多少存在するので、それが嫌な方はそっと閉じてください
基本的なことは基本的に説明しません
このページは初心者にやさしくないページだと思います
このページにかかれたことを実行するのは自己責任でお願いします
ソース自体違ってたらごめんなさい

やっておくべき改造編

この項ではやっておいたほうがいいよ!っていう改造を説明したりしなかったりします
強制ではないのでやってもやらなくてもどちらでもかまいません
@パーティーIIを設置するまでの過程はこのページでは説明しません

ブログパーツを使えるようにしよう

せっかくあるのに使わないのはもったいない!
ということでブログパーツの設定方法を説明します
config.cgiを開いてブログパーツ用リンクの部分を見ると初期の状態では
# 設置Path(/index.cgiを除いたhttp://〜のURL)ブログパーツ用リンク
$game_path = 'http://party.xii.jp';
となっているはずです
これを自分の@パーティーIIのトップページにあわせて書き直します
ここの@ぱの場合トップページのリンクが http://seiryuu.rosx.net/game/party2/index.cgi なので
設置Pathは最後の/index.cgiを除いて http://seiryuu.rosx.net/game/party2 となります
↓はこの@ぱの場合のブログパーツ用リンクです。参考程度にみていただくと良いと思います
# 設置Path(/index.cgiを除いたhttp://〜のURL)ブログパーツ用リンク
$game_path = 'http://seiryuu.rosx.net/game/party2';
 
次に、アメブロなどで@ぱのブログパーツが文字化けをしないようにするためにやっておくべきことを説明します
まず、文字化けの発生の原因は文字コードというものにあります
@パーティーIIのブログパーツはShift_JISという文字コードが使用されているのに対してアメブロ自体はUTF-8という文字コードが使用されています
このように文字コードが違っているために文字化けが発生するのです
文字コード自体の説明は興味があったら調べてみてください
じゃあどうやったら文字化けしなくなるの!と思っている人もいると思いますのでそろそろ修正箇所を説明しますね
player.cgiの内容を修正します。青色の文字が追加したところです!

元のソース

#================================================
sub run {

・・・省略・・・

	print qq|<h3>@ブログパーツ</h3>|;
	print qq|<script type="text/javascript" src="$game_path/my.cgi?$in{id}"></script><textarea class="textarea1"><script type="text/javascript" src="$game_path/my.cgi?$in{id}"></script></textarea>|;
	print qq|<h3>@ブログパーツ2</h3>|;
	print qq|<script type="text/javascript" src="$game_path/my2.cgi?$in{id}"></script><textarea class="textarea1"><script type="text/javascript" src="$game_path/my2.cgi?$in{id}"></script></textarea>|;

・・・省略・・・

}

変更後のソース

#================================================
sub run {

・・・省略・・・

	print qq|<h3>@ブログパーツ</h3>|;
	print qq|<script type="text/javascript" src="$game_path/my.cgi?$in{id}"></script><textarea class="textarea1"><script type="text/javascript" src="$game_path/my.cgi?$in{id}" charset="Shift_JIS"></script></textarea>|;
	print qq|<h3>@ブログパーツ2</h3>|;
	print qq|<script type="text/javascript" src="$game_path/my2.cgi?$in{id}"></script><textarea class="textarea1"><script type="text/javascript" src="$game_path/my2.cgi?$in{id}" charset="Shift_JIS"></script></textarea>|;

・・・省略・・・

}

クッキーのセキュリティーを強化しよう

題名どおり、クッキーの強化をしようと思います
別にしなくてもいいんじゃないの?って思った方はいるとは思いますが、今回これを行うのには理由があります
同じサーバに他のゲームを設置した際に他のゲームからクッキーのパスワード等のデータが盗まれる可能性もありえるからです(例えば初期の状態のTOWNとか・・・)
たった一行変更するだけなので是非行ってください
login.cgiの内容を修正します。青色の文字が追加したところです!

元のソース

#=================================================
# クッキーセット
#=================================================
sub set_cookie {

・・・省略・・・

	print "Set-Cookie: party=$cook; expires=$expirese_time\n";
}

変更後のソース

#=================================================
# クッキーセット
#=================================================
sub set_cookie {

・・・省略・・・

	print "Set-Cookie: party=$cook; expires=$expirese_time; HttpOnly\n";
}

ユーザーデータのバックアップを有効にしよう!

ユーザーデータは頻繁に更新されるので、突然のサーバの不調などが原因でデータが壊れてしまうことがあります
通常、ユーザーデータが壊れた場合バックアップでもない限り復活することはありえません
しかし、手動でバックアップを取るには手間がかかってしまう
そこで、@ぱのもともと存在しているバックアップ機能を使えるようにしたいと思います
まず、login.cgiの「プレイヤー一覧作成」の部分を修正します。青色の文字が追加したところで、赤色の文字が削除されるところです!

元のソース

#=================================================
# プレイヤー一覧作成
#=================================================
sub write_player_list_html {

・・・省略・・・

		# データ破損チェック(バックアップがあれば復旧)
		if (-s "$usrdir/$dir_name/user.cgi" <= 0) {
			&copy("$usrdir/$dir_name/backup.cgi", "$usrdir/$dir_name/user.cgi") if -f "$usrdir/$dir_name/backup.cgi";
		}
		# バックアップ
		else {
			&copy("$usrdir/$dir_name/user.cgi", "$usrdir/$dir_name/backup.cgi");
		}

・・・省略・・・

}

変更後のソース

#=================================================
# プレイヤー一覧作成
#=================================================
sub write_player_list_html {

・・・省略・・・

		# データ破損チェック(バックアップがあれば復旧)
		if (-s "$userdir/$dir_name/user.cgi" <= 0) {
			&copy("$userdir/$dir_name/backup.cgi", "$userdir/$dir_name/user.cgi") if -f "$userdir/$dir_name/backup.cgi";
		}
		# バックアップ
		else {
			&copy("$userdir/$dir_name/user.cgi", "$userdir/$dir_name/backup.cgi");
		}

・・・省略・・・

}
 
次にユーザーごとにbackup.cgiを用意します
admin.cgiのユーザー毎にファイルを作成する機能を利用します
#=================================================
# 全ユーザーのデータを取得
#=================================================
sub get_all_users {

・・・省略・・・

#		for my $k (qw/recipe/) {
#			unless (-f "$userdir/$id/$k.cgi") {
#				open my $fh, "> $userdir/$id/$k.cgi";
#				close $fh;
#				chmod $chmod, "$userdir/$id/$k.cgi";
#			}
#		}

・・・省略・・・

}
まず、↑のソースの recipebackup に変更します
# を削除します
管理パスをつかって管理画面にアクセスしてください
これで、ユーザー毎にbackup.cgiが作成されました
作業が終わったら # を元に戻してください
 
最後に、新規登録した人にもデータをバックアップできるようにしましょう
new_entry.cgiを編集します。青色の文字が追加したところです!

元のソース

#================================================
# 登録処理
#================================================
sub create_user {
	$id = unpack 'H*', $in{name};
	
	# フォルダ・ファイル作成
	mkdir "$userdir/$id", $mkdir or &error("その名前はすでに登録されています");
	for my $file_name (qw/collection depot hanasu home home_member item_send_mes job_master letter letter_log memory money monster monster_book profile recipe reload screen_shot send_item_mes stock user/) {
		my $output_file = "$userdir/$id/$file_name.cgi";
		open my $fh, "> $output_file" or &error("$output_file ファイルが作れませんでした");
		close $fh;
		chmod $chmod, $output_file;
	}

・・・省略・・・

}

変更後のソース

#================================================
# 登録処理
#================================================
sub create_user {
	$id = unpack 'H*', $in{name};
	
	# フォルダ・ファイル作成
	mkdir "$userdir/$id", $mkdir or &error("その名前はすでに登録されています");
	for my $file_name (qw/backup collection depot hanasu home home_member item_send_mes job_master letter letter_log memory money monster monster_book profile recipe reload screen_shot send_item_mes stock user/) {
		my $output_file = "$userdir/$id/$file_name.cgi";
		open my $fh, "> $output_file" or &error("$output_file ファイルが作れませんでした");
		close $fh;
		chmod $chmod, $output_file;
	}

・・・省略・・・

	&copy("$userdir/$id/user.cgi", "$userdir/$id/backup.cgi"); # バックアップ
}

不具合の修正方法

このページのメインです!
不具合の内容場所修正方法
賢者さんの@マジックバリア./lib/_skill.cgi賢者のスキル(マジックバリア)の return if $ms{$y}{hp} <= 0; の return を next にする
青魔道師の@MP5デスを使うと敵が消えちゃうのよ。./lib/_skill.cgi青魔道師のスキル(MP5デス)の後ろの &defeat(); を &defeat($y); にする。経験値ももらえますぜ!
魔法の粉を仲間に使ったのに自分が不思議な光に包まれてる・・・./lib/_data.cgi「$mは不思議な光に包まれた!」の $m を $y にする
福引でアイテム受け取ったのに図鑑に登録されないよ!./lib/lot.cgiこちらで説明
ジョブマスター率が-99%ってなっておかしいよ!./lib/job_master.cgimy $comp_par = int($count / $#jobs-1 * 100); の $#jobs-1 を ($#jobs-1) にする
天界の@ねがうのメイドのあれ./lib/god.cgi追加改造も兼ねてこちらで説明します
睡眠時にログインするとクッキーが消えます!./lib/sleep.cgi背景画像を $htmldir/space.gif にするか背景画像を設定する
あれ?ソースがみれるぞ????screen_shot.cgiいろいろ書きたいのでこちらで説明します

福引で初めてのアイテムを受け取ったときに図鑑に登録されない

./lib/lot.cgiの内容を修正します。青色の文字が追加したところです!

元のソース

#=================================================
# @ふくびき
#=================================================
sub fukubiki {

・・・省略・・・

			else {
				$npc_com .= qq|はい。どうぞ!|;
				$m{ite} = $prizes[$i][1];
			}

・・・省略・・・

}

変更後のソース

#=================================================
# @ふくびき
#=================================================
sub fukubiki {

・・・省略・・・

			else {
				$npc_com .= qq|はい。どうぞ!|;
				$m{ite} = $prizes[$i][1];
				require "./lib/_add_collection.cgi";
				&add_collection;
			}

・・・省略・・・

}

screen_shot.cgiに潜む危険な脆弱性

これをみた@パを設置している君!もしかするとソース見られてるかもよ?
この脆弱性は設置しているゲームの全データが見られてもおかしくないくらい危険です
これをみた@パを設置している方は早急に修正してください
 
修正箇所を説明します
screen_shot.cgiの
$in{path} ||= "$logdir";
となっている部分を
$in{path} = "$logdir" unless $in{path} =~ /^$userdir\/(?:[0-9a-z]+)$/;
と修正してください
この脆弱性について詳しく知りたい方はセキュリティ注意点 > ディレクトリトラバーサル @みっちーわーるどをみてください
この脆弱性を利用して不正にデータにアクセスしたりパスワードを盗むのなどの行為は不正アクセス禁止法に抵触する恐れがあります
※この脆弱性を発見したのは僕でなくZako氏です。発見者であるZako氏を讃えましょう!

@ぱ改造ソースだよ!

適当にソースを公開したり解説したりするかもしれないですね
役に立つものも立たないものも公開してるから全部使えばいいってわけでもないよ

天界の@ねがうを改造しよう!

@ねがうのシークレットであるメイドを雇えるようにします
ついでに、シークレットを増やす方法も記載しようと思ってます
./lib/god.cgiの内容を修正します。青色の文字が追加したところで、赤色の文字が削除されるところです!

元のソース

#=================================================
# @ねがう
#=================================================
sub negau {

・・・省略・・・

	for my $i (0 .. $#prizes-1) {
		if ($prizes[$i][0] eq $target) {
			&{ $prizes[$i][2] };
			return if $mes;
			
			$npc_com = "ふむ。$mの願いは「$prizes[$i][0]」だな。<br />$mの願いを叶えたぞ…。機会があればまたあえるだろう…。さらばだ…";
			$m{lib} = 'home';
			&write_memory("$mの願い『$prizes[$i][0]』を叶えてもらう");
			return;
		}
		$p .= qq|<tr onclick="text_set('@ねがう>$prizes[$i][0] ')"><td>$prizes[$i][0]($prizes[$i][1])</td></tr>|;
	}

・・・省略・・・

}

変更後のソース

#=================================================
# @ねがう
#=================================================
sub negau {

・・・省略・・・

	for my $i (0 .. $#prizes) {
		if ($prizes[$i][0] eq $target) {
			&{ $prizes[$i][2] };
			return if $mes;
			
			$npc_com = "ふむ。$mの願いは「$prizes[$i][0]」だな。<br />$mの願いを叶えたぞ…。機会があればまたあえるだろう…。さらばだ…";
			$m{lib} = 'home';
			&write_memory("$mの願い『$prizes[$i][0]』を叶えてもらう");
			return;
		}
		$p .= qq|<tr onclick="text_set('@ねがう>$prizes[$i][0] ')"><td>$prizes[$i][0]($prizes[$i][1])</td></tr>| if $i <= $#prizes-1;
	}

・・・省略・・・

}
これで、メイドさんを雇えるようになりました!
今後、@ねがうのシークレットを追加するときは「# シークレット」より下に
@ねがうで内容がわかるふつうの願い事は「# シークレット」より上に書きましょう。
シークレットを増やした数だけマイナスの数を増やしてください
以下シークレットが一つのときと二つのときの例です

シークレットが一つなら

		$p .= qq|<tr onclick="text_set('@ねがう>$prizes[$i][0] ')"><td>$prizes[$i][0]($prizes[$i][1])</td></tr>| if $i <= $#prizes-1;

シークレットが二つなら

		$p .= qq|<tr onclick="text_set('@ねがう>$prizes[$i][0] ')"><td>$prizes[$i][0]($prizes[$i][1])</td></tr>| if $i <= $#prizes-2;

ギルドを改名できるようにしよう!

ギルド名を突然変えたいがために作り直すのはめんどうだ!と思ったことのある方はもいることでしょう
そこで、ギルドの名前を改名できるようにしましょう!
ギルド協会のスクリプトを見たなら分かると思いますがなんと @かいめい が存在するではありませんか!?
ならば、この機能をそのまま開放しよう・・・と、そのまま開放して使おうとするも暗転して動かないのです
ということで、本当に使えるようにするためにいろいろ修正していきましょう!
./lib/guild.cgiの@かいめいのスクリプトを修正していきます!青色の文字が追加したところで、赤色の文字が削除されるところです!

元のソース

#=================================================
# @かいめい
#=================================================
sub kaimei {

・・・省略・・・

	unless (&is_guild_master($old_gid)) {
		$mes = "ギルド名を変更できるのは、ギルドマスターだけです";
		return;
	}

	$mes = "ギルド名に不正な空白が含まれています"					if $target =~ / |\s/;
	$mes = "ギルド名に不正な文字( ,;\"\'&<>\@ )が含まれています"	if $target =~ /[,;\"\'&<>\@]/;
	$mes = "ギルド名に不正な文字( @ )が含まれています"				if $target =~ /@/;
	$mes = "ギルド名は半角$max_guild_name文字までです"				if length $target > $max_guild_name;
	$mes = "ギルド名を変更するのに <b>$make_money</b> G必要です"	if $make_money > $m{money};
	return if $mes;
	
	my $gid = unpack 'H*', $target;
	if (-d "$guilddir/$gid") {
		$mes = "すでに同じ名前のギルドが存在します";
		return;
	}
	
	# 新規ギルド作成+データコピー
	mkdir "$guilddir/$gid", $mkdir or &error("$guilddir/$gidディレクトリが作成できません");
	opendir my $dh, "$guilddir/$old_gid" or &error("$guilddir/$old_gidディレクトリが開けません");
	while (my $file_name = readdir $dh) {
		next if $file =~ /^\./;
		&copy("$guilddir/$old_gid/$file", "$guildir/$gid/$file");
	}
	closedir $dh;
	
	# 所属しているメンバーのギルド名を変更
	open my $fh, "< $guilddir/$dir_name/member.cgi" or &error("$guilddir/$dir_name/member.cgiファイルが読み込めません");
	while (my $line = <$fh>) {
		my($name, $position) = split /<>/, $line;
		next if $position eq '参加申請中';
		&regist_you_data($name, 'guild', $target);
	}
	close $fh;
	
	# 旧ギルドの削除
	&delete_directory("$guilddir/$old_gid");
	
	$npc_com = qq|<span class="st_up">@ギルド改名<b>$m{guild}</b>あらため<b>$target</b>ギルドに改名します</span>|;
	&write_memory(qq|<span class="st_up">ギルド名を<b>$target</b>に変更</span>|);
	&write_news(qq|<span class="st_up">$mが $m{guild} ギルドを <b>$target</b> ギルドに変更しました!</span>|);

	$m{lib}    = 'guild';
	$m{guild}  = $target;
	$m{money} -= $make_money;
}

変更後のソース

#=================================================
# @かいめい
#=================================================
sub kaimei {

・・・省略・・・

	unless (&is_guild_master($old_gid)) {
		$mes = "ギルド名を変更できるのは、ギルドマスターだけです";
		return;
	}

	my $max_guild_name_z = int($max_guild_name * 0.5);
	unless ($target) {
		$mes  = "ギルド名の変更には $make_money G必要です。<br />";
		$mes .= "@かいめい>○○○ ○○○には新しいギルド名をいれてください(最大全角$max_guild_name_z文字[半角$max_guild_name文字]まで)";
	}

	$mes = "ギルド名に不正な空白が含まれています"					if $target =~ / |\s/;
	$mes = "ギルド名に不正な文字( ,;\"\'&<>\@ )が含まれています"	if $target =~ /[,;\"\'&<>\@]/;
	$mes = "ギルド名に不正な文字( @ )が含まれています"				if $target =~ /@/;
	$mes = "ギルド名は半角$max_guild_name文字までです"				if length $target > $max_guild_name;
	$mes = "ギルド名を変更するのに <b>$make_money</b> G必要です"	if $make_money > $m{money};
	return if $mes;
	
	my $gid = unpack 'H*', $target;
	if (-d "$guilddir/$gid") {
		$mes = "すでに同じ名前のギルドが存在します";
		return;
	}
	
	my $old_guild = $m{guild};

	# 新規ギルド作成+データコピー
	mkdir "$guilddir/$gid", $mkdir or &error("$guilddir/$gidディレクトリが作成できません");
	opendir my $dh, "$guilddir/$old_gid" or &error("$guilddir/$old_gidディレクトリが開けません");
	while (my $file_name = readdir $dh) {
		next if $file_name =~ /^\./;
		&copy("$guilddir/$old_gid/$file_name", "$guilddir/$gid/$file_name");
	}
	closedir $dh;
	
	# 所属しているメンバーのギルド名を変更
	open my $fh, "< $guilddir/$old_gid/member.cgi" or &error("$guilddir/$old_gid/member.cgiファイルが読み込めません");
	while (my $line = <$fh>) {
		my($name, $position) = split /<>/, $line;
		next if $position eq '参加申請中';
		&regist_you_data($name, 'guild', $target);
		&send_letter($name, "【+改名+】$old_guild (ギルマス $m) を $target に改名しました");
	}
	close $fh;
	
	# ギルドチャットにギルド名の変更をお知らせ
	open my $fh, "+< $guilddir/$gid/log.cgi" or &error("$guilddir/$gid/log.cgiファイルが開けません");
	eval { flock $fh, 2 };
	while (my $line = <$fh>) {
		push @lines, $line;
	}
	unshift @lines, "$time<>$date<>$npc_name<><>$npc_color<>ギルド名を$targetに変更することを許可します@ギルド>$old_guild<><>\n";
	seek  $fh, 0, 0;
	truncate $fh, 0;
	print $fh @lines;
	close $fh;

	# ギルドデータの更新
	open my $fh, "+< $guilddir/$gid/data.cgi" or &error("$guilddir/$gid/data.cgiファイルが開けません");
	eval { flock $fh, 2; };
	my $line = <$fh>;
	my($gname,$gmaster,$gcolor,$gbgimg,$gmes,$gpoint) = split /<>/, $line;
	seek  $fh, 0, 0;
	truncate $fh, 0;
	print $fh "$target<>$gmaster<>$gcolor<>$gbgimg<>$gmes<>$gpoint<>";
	close $fh;

	# 旧ギルドの削除
	&delete_directory("$guilddir/$old_gid");
	
	$npc_com = qq|<span class="st_up">@ギルド改名<b>$old_guild</b>あらため<b>$target</b>ギルドに改名します</span>|;
	&write_memory(qq|<span class="st_up">ギルド名を<b>$target</b>に変更</span>|);
	&write_news(qq|<span class="st_up">$mが $old_guild ギルドを <b>$target</b> ギルドに変更しました!</span>|);

	$m{lib}    = 'guild';
	$m{money} -= $make_money;
}
変更を終えたら、忘れずにコマンドを開放しよう!

NPC算術士も「@MP3@×デス」!?

えーっと、敵の職業が算術士の場合でも「@MP○@×○○」などのスキルを使えるようにしようと思います
./lib/_npc_action.cgiを改造していきます。青色の文字が追加したところで、赤色の文字が削除されるところです

元のソース

#=================================================
# NPCの反撃(何度も同じNPCが攻撃しないように並び替える)
#=================================================
sub npc_turn {

・・・省略・・・

		for my $name (@buf_ps) {
			next if $ms{$name}{hp} <= 0;
			push @enemys, $name;
		}
		
		&npc_action if @enemys >= 1;

		@enemys = @partys;
		@partys = @buf_ps;
		$npc_com = $com;
		$m   = $buf_m;
		$com = $buf_com;

・・・省略・・・

}
# ------------------
# NPCのアクション処理
sub npc_action {
	push @npc_skills, &{ 'skill_'.$ms{$m}{job}     };
	
	my @npc_actions = ();
	my %npc_actions = ();
	for my $i (0.. $#npc_skills) {
		next if $npc_skills[$i][0] > $ms{$m}{sp};
		next if $npc_skills[$i][1] > $ms{$m}{mp};
		push @npc_actions, "$npc_skills[$i][2]";
		$npc_actions{$npc_skills[$i][2]} = [ $npc_skills[$i][1], $npc_skills[$i][3] ];
	}
	@npc_skills = &{ 'skill_'.$ms{$m}{old_job} };
	for my $i (0.. $#npc_skills) {
		next if $npc_skills[$i][0] > $ms{$m}{old_sp};
		next if $npc_skills[$i][1] > $ms{$m}{mp};
		push @npc_actions, "$npc_skills[$i][2]";
		$npc_actions{$npc_skills[$i][2]} = [ $npc_skills[$i][1], $npc_skills[$i][3] ];
	}

・・・省略・・・

	else { # 正常
		if ($ms{$m}{state} eq '混乱') {
			if (rand(3) < 1) {
				$com .="$mは混乱がなおりました!";
				$ms{$m}{state} = '';
			}
			else {
				$com .= "$mは混乱している!";
			}
		}
		
		my $npc_action = $npc_actions[int(rand(@npc_actions))];
		$com .= "@$npc_action ";
		$ms{$m}{tmp} = '' if $ms{$m}{tmp} =~ /防御|反撃/ || rand(3) < 1; # 防御と反撃以外は数ターン残る
		&{ $npc_actions{$npc_action}[1] };
		$ms{$m}{mp} -= $npc_actions{$npc_action}[0];
		$ms{$m}{mp}  = 0 if $ms{$m}{mp} < 0;

・・・省略・・・

	}

	$ms{$m}{time} = $time;
}

変更後のソース

#=================================================
# NPCの反撃(何度も同じNPCが攻撃しないように並び替える)
#=================================================
sub npc_turn {

・・・省略・・・

		for my $name (@buf_ps) {
			next if $ms{$name}{hp} <= 0;
			push @enemys, $name;
		}

		if (@enemys >= 1) {
			my %buf_actions = %actions;
			%actions = ();
			&npc_action;
			%actions = %buf_actions;
		}

		@enemys = @partys;
		@partys = @buf_ps;
		$npc_com = $com;
		$m   = $buf_m;
		$com = $buf_com;

・・・省略・・・

}
# ------------------
# NPCのアクション処理
sub npc_action {
	push @npc_skills, &{ 'skill_'.$ms{$m}{job}     };
	
	my @npc_actions = ();
	my @npc_actions2 = ();
	for my $i (0.. $#npc_skills) {
		next if $npc_skills[$i][0] > $ms{$m}{sp};
		next if $npc_skills[$i][1] > $ms{$m}{mp};
		if ($npc_skills[$i][2] =~ /^×/) { push @npc_actions2, "$npc_skills[$i][2]"; }
		else { push @npc_actions, "$npc_skills[$i][2]"; }
		$actions{$npc_skills[$i][2]} = [ $npc_skills[$i][1], $npc_skills[$i][3] ];
	}
	@npc_skills = &{ 'skill_'.$ms{$m}{old_job} };
	for my $i (0.. $#npc_skills) {
		next if $npc_skills[$i][0] > $ms{$m}{old_sp};
		next if $npc_skills[$i][1] > $ms{$m}{mp};
		if ($npc_skills[$i][2] =~ /^×/) { push @npc_actions2, "$npc_skills[$i][2]"; }
		else { push @npc_actions, "$npc_skills[$i][2]"; }
		$actions{$npc_skills[$i][2]} = [ $npc_skills[$i][1], $npc_skills[$i][3] ];
	}

・・・省略・・・

	else { # 正常
		if ($ms{$m}{state} eq '混乱') {
			if (rand(3) < 1) {
				$com .="$mは混乱がなおりました!";
				$ms{$m}{state} = '';
			}
			else {
				$com .= "$mは混乱している!";
			}
		}
		
		my $npc_action = $npc_actions[int(rand(@npc_actions))];
		$com .= "@$npc_action ";
		if ($npc_action =~ /^MP[0-9]+$/) {
			my $npc_action2 = $npc_actions2[int(rand(@npc_actions2))];
			$com .= "@$npc_action2 ";
		}
		$ms{$m}{tmp} = '' if $ms{$m}{tmp} =~ /防御|反撃/ || rand(3) < 1; # 防御と反撃以外は数ターン残る
		&{ $actions{$npc_action}[1] };
		$ms{$m}{mp} -= $actions{$npc_action}[0];
		$ms{$m}{mp}  = 0 if $ms{$m}{mp} < 0;

・・・省略・・・

	}

	$ms{$m}{time} = $time;
}

・・・修正するのが面倒だっていうならここからダウンロードして使えばいいよ。ほかにバグがあっても自力でがんばって