RPGの攻撃を実装する-実装編

前回設計した戦闘中の攻撃をJavaScriptで実装していきます(今回は表面化していませんが、jQueryの使用が前提です)。

攻撃力

まずは攻撃力です。一般的には自分の力(Strength)に武器の攻撃力を足す方式でしょうか。
ここで、魔法等による補正のパターンとしては力を増すパターンと攻撃力を増すパターンが思いつきます。
まず力を増すパターンは一時状態が付加されていることとして、永続状態+一時状態で力を取得します。

Creature.prototype.getSTR = function() {
    var str = this._parmanent.STR;
    for(var i = 0 ; i < this._temporary.length ; i++ ) {
        str += this._temporary[i].STR;
    }
    return str;
}

この後、攻撃力を計算するために装備のSTR値を加算していきます。必ずしも武器だけが攻撃力を増すわけではないので全ての装備の攻撃力を足していきます。
その前に装備の実装ですが、装備は装備可能なアイテムと位置づけてアイテムオブジェクトと一体で管理します。

Creature.prototype.getAttack = function() {
    var atk = this.getSTR();
    for(var nm in this.equipmentIDs) {
        if(!this.equipment.hasOwnProperty(nm)) {
            continue;
        }
        atk += this.equipment[nm] == -1 ?
               0 : world.itemCatalog.catalog[this.equipmentIDs[nm]].ATK;
    }
}

一時状態による攻撃力補正は、とりあえず0か1として実装します。

Creature.prototype.getAttack = function() {
    var atk = this.getSTR();
    for(var nm in this.equipmentIDs) {
        if(!this.equipment.hasOwnProperty(nm)) {
            continue;
        }
        atk += this.equipment[nm] == -1 ?
               0 : world.itemCatalog.catalog[this.equipmentIDs[nm]].ATK;
    }
    var isOverdrive = false;
    for(var i = 0 ; i < this._temporary.length ; i++ ) {
        var t = this._temporary[i];
        isOverdrive |= t.attack == null ? false : t.attack ;
    }
    return isOverdrive ? atk * 1.5 : atk;
}

防御力

防御力も基本は攻撃力と同様で自身の能力(VitarityやEndurance)に装備の補正を追加します。

Creature.prototype.getDefence = function() {
    var def = this.getVIT();
    for(var nm in this.equipmentIDs) {
        if(!this.equipment.hasOwnProperty(nm)) {
            continue;
        }
        def += this.equipmentIDs[nm] == -1 ?
             0 : world.itemCatalog.catalog[this.equipmentIDs[nm]].DEF;
    }

    var isOverdrive = false;
    for(var i = 0 ; i < this._temporary.length ; i++ ) {
        var t = this._temporary[i];
        isOverdrive |= t.defence == null ? false : t.defence ;
    }
    return isOverdrive ? def * 1.5 : def;
}

ステータスの取得部分の実装が重複してきたので集約します。

Creature.prototype.getState(name) {
    var sts = this._parmanent[name];
    for(var i = 0 ; i < this._temporary.length ; i++ ) {
        sts += this._temporary[i][name];
    }
    return sts;
}
Creature.prototype.getDefence = function() {
    var def = this.getState('VIT');
    for(var nm in this.equipmentIDs) {
        if(!this.equipment.hasOwnProperty(nm)) {
            continue;
        }
        def += this.equipment[nm] == -1 ?
               0 : world.itemCatalog.catalog[this.equipmentIDs].DEF;
    }
    var isOverdrive = false;
    for(var i = 0 ; i < this._temporary.length ; i++ ) {
        var t = this._temporary[i];
        isOverdrive |= t.defence == null ? false : t.defence;
    }
    return isOverdrive ? def * 1.5 : def;
}

ダメージ計算

設計編でも述べましたが、ダメージ計算は武器ストラテジーに実装します。
下記のコードではいわゆるドラクエ式をベースに計算します。

WeaponStg.prototype.getDamage(from, to)
{
    var genRandom = function (base)
    {
        var rb = Math.floor(base/8) - Math.floor(base/16) +1,
            r = Math.floor( Math.random() * rb );
        return r*2 - rb;
    }
    var dmg = (from.getAttack() - to.getDefence() / 2) / 2;
    return dmg + genRandom(dmg);
}

命中率

命中判定は、かなりゲームデザインに拠るところが大きいですが武器に依存するか技術や熟練度の概念があれば、それも影響します。
また、視界を奪ったりする状態変化の影響を受ける事があります。こらを加味して命中判定はCeatureに実装することにします。

とりあえず一時状態による影響(かつ0か1)のみを考慮します。

Creature.prototype.isHit = function(to) {
    var genRandom = function(base) { //乱数計算は割愛(これも結構奥が深いので)
        },
        isLower = false;
    for(var i = 0 ; i < this._temporary.length ; i++ ) {
        var t = this._temporary[i];
        isLower |= t.lower == null ? false : t.lower;
    }
    return isLower ? genRandom(0.5) : true;
}

回避率

回避率は敏捷(agility)や専用のステータスを使用して一定確立で回避できます。
こちらもベースとなる確立を一時状態で補正した後に最終的に回避の成否を求めます

Creature.prototype.isAvoid = function(to) {
    var agi = this.getState('AGI'),
        genRandom = function(base) { //乱数計算は割愛(これも結構奥が深いので)
        },
        isAvoid = false;
    for(var i = 0 ; i < this._temporary.length ; i++ ) {
        var t = this._temporary[i];
        isAvoid |= t.avoid == null ? false : t.avoid;
    }
    return genRandom(isAvoid ? agi + 0.3 : agi);
}

攻撃をする

武器ストラテジー内で基本的な攻撃(一対一かつ追加攻撃なし)を実装します。

WeaponStg.prototype.attack = function(from, to) {
    if(!from.isHit(to) || to.isAvoid(from)) {
        return false;
    }
    var dmg = this.getDamage(from, to);
    to.hp(to.hp() < dmg ? 0 : to.hp() - dmg);

    return true;
}

画面効果

画面効果については魔法の実装と同様に表示するメッセージのみ実装しておきます。
ただし、実装方法を少し変更します。前回は表示するメッセージをストラテジーに一時的に保持していましたが、テキストを挿入する配列を外から引き渡すようにします。

Weapon.prototype.attack = function(from, to, text) {
    text.push(from.name + "は、" + to.name + "を攻撃した。");
    this._strategy.attack(from, to, text);
}
WeaponStg.prototype.attack = function(from, to, text) {
    if(!from.isHit(to)) {
        text.push(from.name + 'の、攻撃が外れた。');
        return false;
    }
    if(to.isAvoid(from)) {
        text.push(to.name + "は、" + from.name + "の攻撃を避けた。");
        return false;
    }
    var dmg = this.getDamage(from, to);
    to.hp(to.hp() < dmg ? 0 : to.hp() - dmg);

    text.push(from.name + "は、" + dmg + "のダメージを与えた。");
    return true;
}

サンプルを用意しました。

免責

ここに示したコードは、机上で作成したものなので動作確認していません。typoがあったりちゃんと動かないかもしれませんが、怒らないでください。

優しく指摘して下されば、適宜修正致します。

M. K. の紹介

IT屋さんです。プログラミングが大好きで今はJavascriptがお気に入りです。
カテゴリー: JavaScript, ゲーム作成   タグ:   この投稿のパーマリンク