JavaScriptで遊べるゲーム【Screeps: Arena】チュートリアル（随時更新）

「JavaScript」で遊べるゲームがsteamで出たということで、チュートリアルの内容ざっくり日本語にして書いてみる。プレイするタイミングでまったり随時更新していく予定なので間違ってたらそっとコメントでおしえてください:innocent:


# Screeps: Arenaとは
プログラミング言語を打ち込んでキャラクターを操作する対戦ゲーム。
基本的なJavaScriptはわかっていること前提っぽい。無料のオンラインチュートリアルっぽいのもあるのでぜひ。

[Screeps: Arena（Steam）](https://store.steampowered.com/app/1137320/Screeps_Arena/?l=japanese)
[公式サイト](https://screeps.com/)
[ドキュメント](https://docs.screeps.com/)
[API　リファレンス](https://docs.screeps.com/api/)

■買うのはちょっとためらうけど、少し遊んでみたい方はオンラインチュートリアルっぽいのあったのでこちらへ。
[チュートリアル](https://screeps.com/a/#!/sim/tutorial)


## 選べる3つのモード
![demo](https://gyazo.com/fab648047eff89dc59237450a9787481/raw)
### Capture the Flag
クリープチームを使用して、味方の旗を捕まえられるよりも早く敵の旗を捕まえます。
建物はありませんが、忍び寄る体は成長する可能性があります。

マップはランダムな地形で100×100マスです。あなたとあなたの対戦相手の両方がマップコーナーに基地を持っています。川が地図の中心を横切っています。
体の異なる14のクリープ、旗と塔があります。

このゲームの目的は敵があなたの旗を捕まえる前に、あなたはクリープで敵の旗を踏んで敵を捕まえることにあります。

※タワーとは1tickごとに1エネルギー充填され、クールダウンなしですべてのtickにダメージを与えたり回復したりすることができます。

川の真ん中で時々発生するボディパーツと呼ばれるアイテムがあります。クリープがそのようなオブジェクトを踏むと追加のボディパーツがそのボディに追加されます。

![demo](https://gyazo.com/eff8e722b3ed68f98a0e2f7c24c395ad/raw)

制限時間は2000tickです。制限時間が終了すると、ゲームは引き分けで終了します。

### Spawn and Swamp
エネルギーを集めたり収穫したりし、クリープをスポーンしたり基地を建築したり、ほとんど沼で覆われた部屋で敵を倒します。（スタークラフトとかRTSみたいな感じ？）


### Collect and Control
執筆中。

# チュートリアル

## Loop and import
チュートリアルへようこそ！ここでは、コーディングによってScreepsArenaをプレイする方法の基本を学びます。
### イントロダクション
「Screeps」は「クリープのスクリプト」を意味します。ゲームでは、プログラミング言語として実際のJavaScriptを使用して、ユニットまたはクリープの動作をスクリプト化します。JavaScriptについては説明しませんが、基本から初めてなれることをお勧めします。ゲームをマスターしたい場合はその言語で。

ローカルフォルダにあるコードファイルに書き込んでプレイします。ゲーム内のコードエディタはまだ準備ができていないため、MicrosoftVSCodeなどの外部エディタを使用することをお勧めします。

[MicrosoftVSCode](https://code.visualstudio.com/)

これらのファイルは【PLAY】ボタンをクリックするとサーバーに送信されます。コードに構文エラーがない場合は、ゲームが開始され視聴できます。
ゲーム中に送信されたコードを変更することはできません。
別のゲームを開始する必要があります。ゲームはほんの数秒で終了します。ただし、どの速度でも視聴できます。
![demo](https://gyazo.com/efbddf4c830248836559144aeec9f544/raw)

### スクリプトループ
毎ターン（私たちはTickと呼んでいる）、ゲームはloop()と呼ばれるあなたのメイン関数を実行します。main.mjsファイルですでに定義されています。

```main.mjs
import { } from '/game/utils';
import { } from '/game/prototypes';
import { } from '/game/constants';
import { } from '/arena';

export function loop() {
    // Your code goes here
}
```

この関数の巣b手は、ゲームが終了するまで何度も何度も実行されます。ただし、ここではコマンドのみをスケジュールすることに注意してください。
これらは、後でループ関数が終了したときに実行されます。


### インポートする
getTicks() 関数を呼び出すことで、現在のtickがなんであるかを判断することができます。これらを呼び出すには/game/utilsモジュールからあなたのコードにインポートする必要があります。

```
import { getTicks } from '/game/utils';

export function loop() {
    console.log('Current tick:', getTicks());
}
```
この方法でインポートできるメソッドとオブジェクトはたくさんあります。完全なリストについては、ドキュメントを参照してください。
（ドキュメントについては、ゲーム内のリンクから確認することができます。どこかで見れるかな？）

上記のconsole.log関数に注目してください。実行中のコードをデバック及び検査できるように、ゲームコンソールパネルに任意の値を出力します。
この関数をインポートする必要はありません。グローバルに利用できます。



### さぁはじめよう
チュートリアルの最初のステップでは、ゲームオブジェクトではなく、ゲームマップは完全にからです。
この手順を完了するためには、ループ関数でconsole.logを使用して、コンソールに情報を出力することだけです。

先ほどのサンプルコードをコピーしてmain.mjsファイルに貼り付け、【PLAY】ボタンをクリックしてみてください。
![demo](https://gyazo.com/2af08c778b967826678afb93ca9311fd/raw)

# Simple Move
素晴らしい！デフォルトのスクリプトフォルダはステップごとに異なるため、ここで別の場所でファイルを編集する必要のあることに注意してください。ただしフォルダはいつでも変更できます。


それでは、ユニット、つまりクリープに会いましょう。
ドキュメントからわかるようにすべてのクリープは様々なアクションを実行でき、明らかに移動できます。
```
creep.moveTo(target);
```
このコマンドは、クリープをターゲットに向かって1ステップ移動します。ループの反復ごとに実行すると、クリープは目的地まで移動します。この関数を呼び出したときにその位置がすぐにへんこうされるのではなく、ループ関数が終了した後にのみ変更されることに注意してください。

コードからクリープにアクセスする方法は？
`getObjectsByPrototype`と呼ばれる特別な関数があります。
指定されたプロトタイプまたはクラスを持つすべてのゲームオブジェクトを含む配列を返します。
次のようにゲームのすべてのクリープを選択できます。

```
import { getObjectsByPrototype } from '/game/utils';
import { Creep } from '/game/prototypes';

export function loop() {
    var creeps = getObjectsByPrototype(Creep);
}
```
このチュートリアルではプロトタイプ名も持つ`Flag`オブジェクトがあります。
moveToメソッドを使用してクリープステップを旗に移動させます。

（サンプルコードがありますが、ゲーム内で確認してみてください）

# First attack
Screeps ArenaはマルチプレイヤーPvPゲームです。あなたのクリープはあなたの対戦相手と戦います。
この章では戦い方を学びましょう。

どのクリープがあなたのものであるかを判断する方法は？
指定された条件の配列要素を返す標準のJavaScript`Array.filter`または、`Array.find`メソッドを使用します。
詳しくはCreepのドキュメントを確認してください。
[Creep](https://docs.screeps.com/api/#Creep.room)

```
import { getObjectsByPrototype } from '/game/utils';
import { Creep } from '/game/prototypes';

export function loop() {
    var myCreep = getObjectsByPrototype(Creep).find(creep => creep.my);
    var enemyCreep = getObjectsByPrototype(Creep).find(creep => !creep.my);
}
```

これで、攻撃方法を使用して敵を倒すことができます。
ターゲットに到達できない場合はERR_NOT_IN_RANGEエラーが返されるため、最初にmoveToを使用する必要があります。

```
if(myCreep.attack(enemyCreep) == ERR_NOT_IN_RANGE) {
    myCreep.moveTo(enemyCreep);
}
```

目的：敵のクリープを倒してください。

<details><summary>Sample Code</summary>

```
import { getObjectsByPrototype } from '/game/utils';
import { Creep } from '/game/prototypes';
import { ERR_NOT_IN_RANGE } from '/game/constants';

export function loop() {
    //自分のCreepを変数myCreepに入れる
    var myCreep = getObjectsByPrototype(Creep).find(creep => creep.my);
    //自分以外のCreepを変数enemyCreepに入れる
    var enemyCreep = getObjectsByPrototype(Creep).find(creep => !creep.my);
    
    if(myCreep.attack(enemyCreep) == ERR_NOT_IN_RANGE) {
        myCreep.moveTo(enemyCreep);
    }
}
```
</details>


# Creeps bodies
様々なクリープ関数を呼び出すことができますが、すべてのクリープですべての関数を使用できるわけではありません。
クリープが実行できるアクションはクリープの体の部分によって異なります。
その本体の特性を確認できます。これは、各要素が特定のタイプの1つのボディ部分を表す配列です。

- Move（移動）→ クリープを移動できるようにする。
- ATTACK（攻撃）→ 接近範囲で攻撃できるようにします。
- RANGED_ATTACK（遠距離攻撃）→ 3タイル離れたターゲットを攻撃できるようにします。
- HEAL（回復）→ 味方（自分自身も含む）を回復することができます。（回復の範囲は1タイル？）
- WORK（労働）→ 建造物を構築したり、エネルギーを収集したりすることができます。
- CARRY（運ぶ）→ リソースを運ぶクリープ能力高め
- TOUGH（タフ）→ 効果なし？

```
if(creep.body.some(bodyPart => bodyPart.type == ATTACK)) {
    // ATTACK(攻撃)のボディパーツがあるクリープが対象
}
```

ボディパーツごとにクリープのヒット数が100増加します。クリープがダメージを受けると、ボディパーツもダメージを受け、パーツのヒットカウンターが0になると機能しなくなります。同じタイプのボディパーツが多いほど、このタイプの効果はより強くなります。

さて、このチュートリアルのステップでは、3つの異なるクリープを操作してもらいます：ATTACK、RANGED_ATTACK、およびHEALボディパーツをもつクリープ。
相手の行動を調整した場合にのみ、相手を倒すことができます。

これを行うには、前のチュートリアルステップと同じアプローチを使用しますが、各クリープのボディーパーツに基づいてアクションを区別します。ダメージディーラーはさまざまな方法（攻撃と遠隔攻撃）でダメージを与え、ヒーラーはダメージを受けたクリープをヒールでヒールする必要があります。

目標：敵を倒してください。


<details><summary>Sample Code</summary>

```
import { getObjectsByPrototype } from '/game/utils';
import { Creep } from '/game/prototypes';
import { ERR_NOT_IN_RANGE, ATTACK, RANGED_ATTACK, HEAL } from '/game/constants';

export function loop() {
    var myCreeps = getObjectsByPrototype(Creep).filter(creep => creep.my);
    var enemyCreep = getObjectsByPrototype(Creep).find(creep => !creep.my);

    for(var creep of myCreeps) {
        //もし、ATTACKのボディパーツを持っているクリープだったら
        if(creep.body.some(bodyPart => bodyPart.type == ATTACK)) {
            if(creep.attack(enemyCreep) == ERR_NOT_IN_RANGE) {
                //このクリープを敵のほうに移動させる
                creep.moveTo(enemyCreep);
            }
        }
        //もし、RANGED_ATTACKのボディパーツを持っているクリープだったら
        if(creep.body.some(bodyPart => bodyPart.type == RANGED_ATTACK)) {
            if(creep.rangedAttack(enemyCreep) == ERR_NOT_IN_RANGE) {
                //このクリープを敵のほうに移動させる
                creep.moveTo(enemyCreep);
            }
        }
        //もし、HEALのボディパーツを持っているクリープだったら
        if(creep.body.some(bodyPart => bodyPart.type == HEAL)) {
            //変数myDamagedCreepsにhitsがMAXでない（ダメージを受けている）クリープを入れる
            var myDamagedCreeps = myCreeps.filter(i => i.hits < i.hitsMax);
            //変数myDamagedCreepsにダメージを受けているクリープが1体以上いたら
            if(myDamagedCreeps.length > 0) {
                if(creep.heal(myDamagedCreeps[0]) == ERR_NOT_IN_RANGE) {
                    //ダメージを受けているクリープのほうにHEALクリープを移動させる
                    creep.moveTo(myDamagedCreeps[0]);
                }
            }
        }
    }
}
```
</details>


TODO:同時にダメージ受けることあったらどういう順番で格納されるのか