ほげほげー

C#メインにプログラミング周りから日常のあれこれとかを不定期に書いていきます。

Stone Story RPG Stonescript 実用編

基本は前の記事を参照のこと。

tyheeeee.hateblo.jp

それでは実用例を挙げて解説も併記していきます。

ちなみに私は PC 版を持っておらず、外部ファイルの扱いには詳しくないのでここでは触れません。

はじめに

前回のスクリプトの解説だけで組める人は最後の方のハック集読むだけで良いと思います。

Stonescript 準備編

実装環境

PCで書く場合は VS Code で書いたりするとよいです。ノートパッドでもなんでもいいですが、オートインデントが効くエディタだと楽ができます。秀丸なんかもおすすめです。

VS Code で書く場合、自動判定で Swift 判定になりますが、まぁ大体いい感じにハイライトしてくれるので上手く使いましょう。画面出力の >` あたりを使ってると割と壊れるので画面出力周りを色々やりたい場合は最下部で書くとよいかも。

スマホで書く場合は好きなメモ帳使ってください。キーボードを使うと楽ですが、まぁなかったらなかったで何とかなります。記号系の入力が面倒くさいので覚悟してください。プログラミング特化のソフトウェアキーボードがあってもいいと思うのだけど、軽く調べた限りだとあまりなさそう。

コーディングルール

この辺は各自適当に自分でルールを作って実装を進めてください。ここでは自分はこういうルールでやってるよ、という事を軽くまとめておきます。

コメント

基本最小限にします。日本語(マルチバイト文字)があるとスマホの場合色々と面倒なので、コメントを書く場合でも英語がベター。

なお、本稿ではわかりやすさを重視して日本語でコメントを入れていきます。

変数

変数がグローバルに定義されるのと、1度しか初期化されない都合上、コードを読む際のノイズにならないようできるだけ一か所にまとめた方が良いと思っているので、最上部にまとめます。

通常のプログラミング言語のように使う直前に、としても良いのですが、グローバルに定義される都合上、重複が発生したりすると面倒なので最上部で重複を意識できるようにしておくと脳のリソース利用量を減らせます。

また、通常のプログラミング言語のように英単語は省略せず使いたいですが、1行の文字数制限があるので省略系の使用はやむなし。

// 装備関連
var mainSword = "vigor socketed_sword +15 D"
var subSword = "vigor socketed_sword +14 D"
var mainWand = "vigor wand +15 D"
var subWand = "vigor wand +14 D"
var shield = "vigor shield +15 A"

// プレイヤー関連
var switchL = false
var switchR = false

// 敵関連
var isBoss = false
var isMiniBoss = false

// 関数用
var spEq= [] // 本来は関数内に書いて splitted とか、そもそも a みたいな1文字にしたいけど、すべてがグローバルなので衝突を避けるために冗長にする。文字数制限もあるので省略形を使う。かなりめんどい。
....
関数

Stonescript の仕様上、変数がグローバルになるのでスコープを抑えて変数名をシンプルに、みたいな扱いができずちょっと難しいのですが、書く場合は上部にまとめて書くようにします。

関数のサイズは大きくなりすぎると見通しが悪くなりがちなので5行とかそこらで済むように心がけます。しかしながらそうもいかないこともあるので意識する程度。

変数宣言は変数の項で触れた通り最上部にまとめて書いておき、定数的に使っている変数以外は関数内の最初で初期化して疑似的にスコープを狭くする運用をしています。

命名は CamelCase で(慣れているので)。

いろいろな仕様*1との相性が悪いので関数の利用は最小限です。

var spEq = []

func FormatEquipment(eq)
  spEq = []
  spEq = string.Split(eq)
  return spEq[1]+" "+spEq[2]
  ^+" "+spEq[4]+" "+spEq[5] // 変数名を1文字にすると1行に収まる。割り切って1文字にするのも良し。どうせ自分しか読まないのだし。

実用編

前置きはここまでにして、実用編に入ります。

実際に使えるスクリプトも置いておきますので参考になれば幸いです。

フラグ管理する

そこら辺の野良スクリプトを眺めてるとフラグ管理を 1, 0 の数値でやっているのを何故かよく見かけるのですが、個人的に好みではない&フラグを反転する際に文字数的に非効率なので boolean で管理することをお勧めします。

まず、数値で管理するスクリプトの場合このようになります。

var flag = 0
?フラグを反転する条件
  flag = (flag + 1) % 2

?flag = 1
  // フラグが立っているときの処理

?flag = 0
  // フラグが立っていないときの処理

boolean に置き換えると否定が ! で表現できるのでシンプルになります。

var flag = false
?フラグを反転する条件
  flag = !flag

?flag
  // フラグが立っているときの処理

?!flag
  // フラグが立っていないときの処理

簡潔に書けるので boolean を活用していきましょう。

マップごとの実装

最適化して動かすことを考えると、当然のことながらマップ毎に実装を変える必要があります。しかしながら関数がグローバルに影響してしまう都合上非常に見通しが悪くなりがち&?loc で分岐していると重厚長大になり最大文字数の問題に当たるので、面倒ではありますが各マップ毎に用意しておくのがベターです。

自分はPCとスマホ(Android と iPhone 両方)間のやり取りの容易さと、雑に履歴が取れる Google Keep を利用しています。iPhone と Mac しか使ってないなら AirDrop で適当にやればいいんじゃないかなと思います。

PC版の場合はファイルを分けて読み込む、というやり方がありそうですが、スマホでそれができるかは不明なのと試すのも面倒なので無いものとします。(誰か知ってたら教えてください)

道中とボスで処理を分ける

このゲームはステージのレベルが高くなればなるほど導入の雑魚敵の数が増えるので、道中では攻撃速度が遅くてもスプラッシュを持った装備の方が良く、他方でボスではDPSの高いスプラッシュのない装備の方が良い、という都合があります。*2

そのため、基本的には道中とボスで立ち回りを変える必要があります。

ザックリであれば以下のスクリプトだけで対処できます。

?foe = boss
  // ボス戦時のふるまい
:
  // 道中のふるまい

しかしながら、呪われた会館のボスのように雑魚敵を召喚するステージなどではたまにボス以外判定をされてしまって思ったようにスクリプトが動作しないことがあります。foe が持つ値が現在プレイヤーがターゲットにしている敵なためです。

そのため、雑魚敵がボスよりも前に来るとターゲットが変わって foe = boss が false になるようになります。地味に迷惑。

そんな時はフラグを用意して、ボス戦に入った時だけフラグを立てるとよいです。

var isBoss = false
// 変数はループしても引き継ぐのでステージ開始から10フレーム以内の間初期化しておく
// loc.begin ではなぜか初期化できなかった。これはもしかしたらループ時には発動しない、ステージ開始時のみに有効な値なのかも。
?totaltime < 10
  isBoss = false

?foe = boss
  isBoss = true // 一度フラグを立てたらループするまでフラグを解除しない

?isBoss
  // ボス時のふるまい
:
  // 道中のふるまい

そして☆☆☆☆☆まではこれで対処できますが以降は中ボスが出てくるようになるので、これでは対処できません。

仕方ないので、ボス判定にボスの名前も含めるようにします。 ついでに中ボスでふるまいを変えたい時用にフラグを追加します。

// 呪われた館をサンプルにスクリプトを記述します
var isBoss = false
var isMiniBoss = false

?totaltime < 10
  isBoss = false

?foe = boss
  // skeleton 以外の boss は中ボス
  // ボス戦では解除したいので skeleton かどうかでフラグのON/OFFをしている
  isMiniBoss = foe ! skeleton

?foe = boss & foe = skeleton
  isBoss = true

?isBoss
  // ボス時のふるまい
:?isMiniBoss
  // 中ボス時のふるまい
:
  // 道中のふるまい

と、まぁなんか長くなってきてしまったのでいったんここまで。

続きは後日時間のある時に書いておきます。

今後は飽きない限り大体以下のような内容を書いていく予定です。増えたり減ったりするかもしれません。

  • 実践編続き
    • スキル使用
    • 時間を短縮する
      • ダッシュの盾(dashing shield)で距離を詰める
      • スプラッシュ攻撃でまとめて倒す
      • スキルを活用する

ここまでは実践編2に書きました。

tyheeeee.hateblo.jp

  • 実践編続き
    • ボスの状態に合わせて装備を変える
      • 攻撃を避ける
      • 防御する
      • 最適な武器を装備する
      • 細かく最適化する

ここまでは実践編3に書きました。

tyheeeee.hateblo.jp

残りは以下。

  • Stonescript ハック集
    • 装備持ち替えによるDPS向上
    • 堕神の剣のスキルを効率よく使う
    • オフラインでの周回に最適化する
    • 地獄の鉱山の中ボスに対処する

*1:1行の文字数制限を考えると関数名を省略形で記述しないと考慮しないといけないことが増えるだとか、狭いスコープ内だから変数名を単純にしたいけど変数宣言するとグローバルになるからできないだとか

*2:ロスト系のアイテムが極まれば話は別かもしれないけど廃課金か時間を相当かけるかしないと無理なので無かったことに