珍走記を改造しよう

第四回:マスの種類を追加しよう

マスの仕組みを理解しよう

「マスの種類」とは、道の駅とか支払い所とかカーショップなどのことです。

ここに、新しい種類を追加してみましょう。

さて、main.js4行目を見てみましょう。 pexp={'p':'道の駅','m':'支払い所','f':'フェリー','c':'カーショップ','i':'アイテムショップ','b':'カジノ'};

これが、特別なマスの名前を表しているところです。

'*':'*****'というデータが「,」で区切られています。

これらは対応していて、例えば「p」は道の駅、「m」は支払い所、「f」はフェリーを表していることがわかります。

この1文字のアルファベットは自分で決めることができます。

第一回で場所のデータは次のようなものでした。 ['',3,171,47,'c','58','',387,47,'']

この「c」の部分がこれと対応しています。例えば、このマスはcだからカーショップであることがわかります。

実際に追加してみよう

今回追加するのはガソリンスタンドです。「そのマスに止まるとターボになる」という仕様にしたいと思います。

文字は「g」ということにしてみましょう。

それでは、main.jsの4行目にそれを追加します。 pexp={'p':'道の駅','m':'支払い所','f':'フェリー','c':'カーショップ','i':'アイテムショップ','b':'カジノ','g':'ガソリンスタンド'};

ちなみに、ガソリンスタンドの画像ですが、これは第一回で解説した通り、画像のデータと中身のデータは無関係なので、特別何かが必要というわけではありません。

さて、次に実際に止まったときの処理ですが、これはmain.cgiで行われます。

main.cgiの320行目付近を見てみます。 ###各マスの処理 ###支払い所 if(($ptype eq 'm' && $user{'*event1'} eq '') || $user{'*event1'}==2){ ($tmp,$value)=&lib'getprice($user{"${num}_car"}); $value=int($value/40+rand($value/40)); $user{"${num}_money"}-=$value; $user{"${num}_alert"}.="燃料代".$value."万円を支払いました。<br>"; ###道の駅 }elsif(($ptype eq 'p' && $user{'*event1'} eq '') || $user{'*event1'}==3){ if(rand(1)<0.4){ if($user{'*turn'}<=50){ $value=10+int(rand(100*$user{'*turn'}-$user{'*turn'}**2)/10); }else{ $value=10+int(rand(250)); } if(rand(1)<0.2){ $value*=5; $user{"${num}_alert"}.=$prfname."は景気がいいようです。".$value."万円をもらいました。<br>"; }else{ $user{"${num}_alert"}.=$value."万円をもらいました。<br>"; } $user{"${num}_money"}+=$value; }else{ my($chrs) = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"; $value = substr($chrs,int(rand(length($chrs))),1); ($itemname)=&lib'getprice($value); ($tmp,$tmp,$tmp,$capacity)=&lib'getprice($user{"${num}_car"}); if(length($user{"${num}_item"})<$capacity){ $user{"${num}_item"}.=$value; $user{"${num}_alert"}.=$itemname."を拾いました。<br>"; }else{ $user{"${num}_alert"}.=$itemname."を拾いましたが持ち切れないので捨てました。<br>"; } } ###フェリー }elsif($ptype eq 'f'){ ($tmp,$value)=&lib'getprice($user{"${num}_car"}); $value=int($value/20+rand($value/20+length($user{"${num}_item"})*10)); $user{"${num}_money"}-=$value; $user{"${num}_alert"}.="フェリー代".$value."万円を支払いました。<br>"; }

支払い所、道の駅、フェリーの処理を行っているのが何となく分かると思います。

ここで行われている処理は、マスに止まった瞬間に起こるものです。

カーショップやカジノなど、止まったあとにボタンを押して入るものは、別のところで処理があります。それは、今回は解説しません。

このソースには前の第三回の改造が施されていますが、新規に処理を追加するので今回関係ありません。

さて、ここにガソリンスタンドの処理を追加します。 ###各マスの処理 ###支払い所 if(($ptype eq 'm' && $user{'*event1'} eq '') || $user{'*event1'}==2){ ($tmp,$value)=&lib'getprice($user{"${num}_car"}); $value=int($value/40+rand($value/40)); $user{"${num}_money"}-=$value; $user{"${num}_alert"}.="燃料代".$value."万円を支払いました。<br>"; ###道の駅 }elsif(($ptype eq 'p' && $user{'*event1'} eq '') || $user{'*event1'}==3){ if(rand(1)<0.4){ if($user{'*turn'}<=50){ $value=10+int(rand(100*$user{'*turn'}-$user{'*turn'}**2)/10); }else{ $value=10+int(rand(250)); } if(rand(1)<0.2){ $value*=5; $user{"${num}_alert"}.=$prfname."は景気がいいようです。".$value."万円をもらいました。<br>"; }else{ $user{"${num}_alert"}.=$value."万円をもらいました。<br>"; } $user{"${num}_money"}+=$value; }else{ # $value=chr(65+int(rand(26))+int(rand(2))*32); my($chrs) = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!"; $value = substr($chrs,int(rand(length($chrs))),1); ($itemname)=&lib'getprice($value); ($tmp,$tmp,$tmp,$capacity)=&lib'getprice($user{"${num}_car"}); if(length($user{"${num}_item"})<$capacity){ $user{"${num}_item"}.=$value; $user{"${num}_alert"}.=$itemname."を拾いました。<br>"; }else{ $user{"${num}_alert"}.=$itemname."を拾いましたが持ち切れないので捨てました。<br>"; } } ###フェリー }elsif($ptype eq 'f'){ ($tmp,$value)=&lib'getprice($user{"${num}_car"}); $value=int($value/20+rand($value/20+length($user{"${num}_item"})*10)); $user{"${num}_money"}-=$value; $user{"${num}_alert"}.="フェリー代".$value."万円を支払いました。<br>"; ###ガソリンスタンド }elsif($ptype eq 'g'){ if($user{"${num}_status"}!~/t/){ $user{"${num}_status"} .= "t"; $user{"${num}_alert"}.="ガソ\リンスタンドでターボにしてもらいました。<br>"; &lib'chat("<$num>はガソ\リンスタンドでターボになりました。"); } }

こうなります。色が違う部分が、追加した部分です。最後にガソリンスタンドの処理が追加されているのが分かります。

これで、ガソリンスタンドは完成です・・・といいたいですが、実はまだ終わっていません。

実際にやってみると、ガソリンスタンドのマスにとまるとターボにはなりますが、「ガャ潟塔Xタンドでターボになりました。」というおかしな表示がでます。

これは、文字化けの一種で、Shift_JISの仕様によるものです。「ガソリンスタンド」の「ソ」の字がいわゆる「ダメ文字」であることが原因で、他に「表」なども使用頻度が高いダメ文字です。

なぜこのようなことが起こるのか、興味があったら調べてみましょう。

とりあえず、ダメ文字の後に「\」という文字をおくと正常になります。

$user{"${num}_alert"}.="ガソ\リンスタンドでターボにしてもらいました。<br>"; &lib'chat("<$num>はガソ\リンスタンドでターボになりました。"); しかし、今回の場合はもっとよい方法があります。というのも、この場合、あとでマスの名前を「ガソスタ」に変えたとしても、出るログは「ガソリンスタンドで〜」というログのままです。

そこで、プログラムが持っているマスの名前をそのまま表示するようにしてみましょう。 $user{"${num}_alert"}.="$lib'pexp{'g'}でターボにしてもらいました。<br>"; &lib'chat("<$num>は$lib'pexp{'g'}でターボになりました。"); この$lib'pexp{'g'}は、lib.cgiで取得されたマスの名前が代入されています。


ある程度プログラムができないと難しいかもしれませんが、これで追加の基礎は分かるようになったと思います。

補足:状態をいじる

さて、詳しい解説をしていきます。

状態とは、ターボであったり、酔っ払っていたり、バリヤーであったり、眠っていたりといった状態異常のことです。

上で追加した部分も、「ターボになる」というように状態をいじっていたものでした。

ここからの説明は、多少Perlができないと分からないかもしれません。

まず、この処理は止まったマスの種類ごとに分かれているのが分かります。

}elsif($ptype eq '*'){ 処理 }

というブロックで各マスの処理がされています。

*に当てはまる文字は、マスを表す文字です。

従って、gのガソリンスタンドは、 }elsif($ptype eq 'g'){ 処理 }

という形になります。

処理の中身は、「自分がターボになる」という簡単なものです。

しかしどうすればいいのかよく分からないので、他にも状態異常にする処理がないか近くを探してみます。

すると、390行目付近に if(($user{"${num}_status"}.$adv)!~/p/ && rand(1)<0.01){ $user{"${num}_status"}.="p"; $user{"${num}_alert"}.="タイヤがパンクしました。<br>"; &lib'chat("<$num>はタイヤがパンクしました。"); } という処理があります。

「タイヤがパンクしました」という言葉があるため、移動したときにパンクしてしまう処理だと分かります。

条件となる($user{"${num}_status"}.$adv)!~/p/ && rand(1)<0.01ですが、&&の右側は「1%」ということです。1%の確率でパンクするということですね。

左側は「($user{"${num}_status"}.$adv)に"p"が含まれていない場合」というものです。

間の"."は、2つの文字列を結合する演算子なので、$user{"${num}_status"}または$advにpが含まれている場合ということになります。

$user{"${num}_status"}という変数ですが、これは連想配列で、$numという変数がキーに使われています。

これは、main.cgiの8行目で代入されている、個別のプレイヤーを表す数字です。

つまり、この変数の実体は、$user{"1_status"}$user{"5_status"}といったもので、それぞれの番号の状態を表していました。

ちなみに、連想配列%userは、main.cgiの12行目で代入されていて、ゲームデータがまとまって入っています。

この変数には、次の行で「p」を追加しているので、「p」がパンクを表しているのだろうと予想がつきます。しかも、一文字につき1つの状態異常を表しているということも予想がつきます。

つまり、「p」が含まれているかチェックしているのは、既にパンクしていたら再びパンクしないようにするためということになります。

さて、同時に$advもチェックされていますが、この変数はmain.js68行目付近の ($tmp,$tmp,$tmp,$tmp,$adv)=&lib'getprice($user{"${num}_car"}); で代入されています。

このgetprice関数は、アイテムや車の情報を取得するもので、main.js3行目の ['乗用車',200,6,5,''],['スポーツカー',600,7,5,''],['F1',1500,9,5,''],['軽トラ',500,6,10,''],['トラック',600,5,20,''],['バキュームカー',1000,6,10,''],['ヘリコプター',3000,8,5,'bpt'],['戦車',5000,7,10,'bgt'],['チャリンコ','0',3,1,'b'] という部分の、"["〜"]"で囲まれた部分の情報をまとめて返す関数です。今回は車ですが、アイテムの情報も同様に返すことができます。

さて、$advには、どうやら車の特殊能力?のようなものが入っているようです。区切られた部分の一番右にあるものですね。

ヘリコプター・戦車・チャリンコが何か持っているようです。今回は「p」をチェックしていますが、pを持つのはヘリコプターだけですね。

他に見られるのは、b・g・tがあります。状態異常としては、pはパンク、bはバリヤー、gは追い越し禁止、tはターボを表しています。これらのデータは、main.jsの8行目と9行目にあります。マスの種類と同じ形式です。 statusname={'t':'ターボ','b':'バリヤー','g':'追い越し禁止','p':'パンク','s':'居眠り','y':'飲酒運転','v':'速度制限5','w':'速度制限4'}; statusexp={'t':'サイコロの大きい目が出やすく、通行止めを通過できます。','b':'すべての妨害を防ぎます。','g':'他の車に追い越されません。','p':'サイコロの目は1しか出ません。','s':'しばらくサイコロを振れません。','y':'他の車に追い抜かれませんが、酔ってアイテムを捨てたりします。','v':'サイコロの目は最高5までです。','w':'サイコロの目は最高4までです。'};

しかし、$advにその能力を持っているからといって、常にその状態と同じ能力を持つというわけではないようです。

例えば、パンクを表すpを$advに持っていても、サイコロで出る目が1のみになるということはありません。

また、ターボを表すtを持っていても、サイコロで出る目が大きくなるということもありません。

tを持っていると、追い越し禁止や通行止めを通り抜けることができます。その点はターボと同じなので、文字がターボと同じなのは都合が良いのでしょう。

追い越し禁止のgは、状態異常のそれと全く同じです。

さて、問題のpは、常にパンクしているわけではなく、パンクに対する耐性を持つと考えるのがよさそうです。現に、くぎやまきびし、当たり屋などの処理において、$advもチェックされています。

さらに、残るbは、状態異常としてはバリヤーを表しますが、さすがに常にバリヤー状態であるというわけではないようです。

これは非常に微妙で、当たり屋とレッカー車の効果を受けない処理をとりあえず見つけました。チャリンコがこれらの車両の影響を受けないのはこのためです。

うまく使っていかないといけないということですね。

さて、$advもチェックする意味がわかりました。パンク耐性を持つ車を対象から除外する意味がありましたね。

まとめると、1行目は結局「既にパンクしているか、パンクしない車は、絶対にパンクしない」ということを表しています。

2行目では、$user{"${num}_status"}にpを追加、つまりパンク状態にしています。

次の2行はログを出しているということは簡単に想像がつくでしょう。

ちなみに、$user{"${num}_alert"}の中身はプレイヤーがログインしたときにポップアップで出るログで、lib'chatでは全体のログを出力しています。「<$num>」という謎の表現がありますが、どうやら色つきでユーザー名に変換されるようです。

さて、これを踏まえてターボにする処理を }elsif($ptype eq 'g'){ 処理 } の中身に書いていきましょう。

まず、同じように「既にターボの場合はターボにならない」という処理を書くことにします。止まると絶対にターボになるので、確率判定は必要ありません。確率による判定を付けたい場合、上記のやつを参考にしてやってみましょう。

なお、$advにt(追い越し禁止を無視)を持っていてもターボにはなれるので、今回は$advの判定もしません。 }elsif($ptype eq 'g'){ if($user{"${num}_status"}!~/t/){ } }

その後の処理も簡単です。状態異常にターボのtを追加し、適当なログを出すだけです。上記のパンクの処理がかなり参考にできます。 }elsif($ptype eq 'g'){ if($user{"${num}_status"}!~/t/){ $user{"${num}_status"} .= "t"; $user{"${num}_alert"}.="ガソ\リンスタンドでターボにしてもらいました。<br>"; &lib'chat("<$num>はガソ\リンスタンドでターボになりました。"); } }

これで、最初に書いた完成したものと同じになりました。