RPGツクール 文字入力してデータ更新

**RPGツクールでTodoリストを作ってみよう**
挑戦中

[RPGツクール 改めて開発環境構築](https://qiita.com/JQinglong/items/42b58c887b9e76e50534)
[RPGツクール リスト表示](https://qiita.com/JQinglong/items/e304fc3198dc7e1187b4)
[RPGツクール 外部API連携](https://qiita.com/JQinglong/items/7478aa91aab71bd996c0)
からの続きです。

## RPGツクール 文字入力プラグイン

文字入力については、こちらのプラグインを利用させていただきます。
[キーボードでテキストフォーム入力（修正版）](https://tm.lucky-duet.com/viewtopic.php?t=1937)

使い方は簡単です。
widthも指定できたら良いかなと思いましたが、全然良いです。

InputForm x=140;y=100;v=11;max=40
で、こんな見た目です。
<img width="320" alt="入力.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77307/1892fef7-9891-c1ce-6d6d-45f50b8bcaea.png">

この場合、変数11を使用するので、変数11に名前をつけてバッティングしないようにしておきます。

## GASへのPost

POSTもfetcheでやってみます。
こちらも参照。
[fetch API でも POST したい！](https://qiita.com/legokichi/items/801e88462eb5c84af97d)

```js:
} else if (args[0] == "add") {
    let params = new URLSearchParams();
    params.set('task', $gameVariables.value(11));

    const method = "POST";
    const body = params.toString();
    const headers = {
    'Accept': 'application/json',
    'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
    };
    fetch("https://script.google.com/macros/s/AKfycbyd8DRBpmbvcdtGgUbNv632Bf_2KBfe5X-phBwJWxeRsvg5j265/exec?",
        {method, headers, body})
        .then((res)=> res.json())
        .then(console.log)
        .catch(console.error);

};
```

新規のTodoが登録されました！

## データ更新のための実装方式

新規登録ができたので（１項目だけなので、複数項目の話も考えなければいけませんが、いったん置いておいて）、
次は、チェックのON・OFFを切り替えるように、データ更新を実装します。

データ更新のためには、各データのキー情報を保持する必要があります。
今回だと、uuid列の情報です。

これに対して、SNSプラグインで写真付きトークという、画像をクリックしたらコモンスクリプトを呼び出せる仕組みが用意されていますが、できるのは呼び出すコモンスクリプトの番号指定だけですので、呼び出した際にコモンスクリプト側でその行のuuidの情報を認識できるようにする必要があります。
これは流石に厳しいので、SNSプラグインを改造させていただきます。

写真付きトークは文章の表示の内容として「\Picture[画像名,コモンイベントID]」を登録する仕組みですが、それの引数追加版を作成します。
追加引数として、uuidを渡します。

SNSプラグインの文章内の処理、Entity_AtsuTalk.prototype._convertEscapeCharacters　で、通常はコモンスクリプトのIDをセットするthis._p[6]に、uuidも付加しておきます。
そして、Menu_Base.prototype._onClickEntry　で、このコモンスクリプトを実行しますが、その直前にuidを所定の変数にセットしておきます。

```js:SnsTalc.js（改造部分）
Entity_AtsuTalk.prototype._convertEscapeCharacters = function(text, timestamp) {
    text = text.replace(/\\/g, '\x1b');
    text = text.replace(/\x1b\x1b/g, '\\');
・・・
    // JQ：改造
    text = text.replace(/\x1bPicture\[(\w+),(\d+)-(.+)\]/gi, function(){
        // $gameVariables.setValue(12, arguments[3]); //12固定（本当はプラグインの起動パラメタにすれば良いですね）
        this._p[3] = _Oggy_AtsuTalkEntityType_Picture;
        this._p[5] = arguments[1];
        // this._p[6] = Number(arguments[2]);
        this._p[6] = arguments[2] + '-' + arguments[3];
        return '';
    }.bind(this));
    return text;
};
・・・
Menu_Base.prototype._onClickEntry = function(node) {
    if (node._entity !== null && node._entity.entityType() === _Oggy_AtsuTalkEntityType_Picture) {
        // JQ：改造
        var commonE = node._entity.commonEventId().toString();
        var commonId = commonE.substr(0,commonE.indexOf('-'));
        var uuid = commonE.slice(commonE.indexOf('-') + 1);
        $gameVariables.setValue(12,uuid);
        var event    = $dataCommonEvents[commonId];
        if (commonId > 0 && !$gameMap.isEventRunning() && event) {
            $gameMap._interpreter.setup(event.list, null);
            return true;
        }
    }
    return false;
};
```

```js:JQTodos.js
//=============================================================================
// JQTodo.js  2020/5/5
// The MIT License (MIT)
//=============================================================================

/*:
 * @plugindesc テスト用プラグイン
 * @author JQ
 *
 * @help このプラグインにはプラグインコマンドはありません。
 * 
 * Plugin Command:
 *   JQTodo list             # 一覧表示
 *   JQTodo add              # 追加
 *   JQTodo update           # 更新　※事前に変数12にuuidをセットしておく
 */

(function(_global) {
    // ここにプラグイン処理を記載
    var N = 'JQTodo';
    var todos = {};

    // プラグインコマンド
    var _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
    Game_Interpreter.prototype.pluginCommand = function(command, args) {
        _Game_Interpreter_pluginCommand.call(this, command, args);
        if (command == N) {
            if (args[0] == "list") {

                var listData;

                fetch("https://script.google.com/macros/s/AKfycbyd8DRBpmbvcdtGgUbNv632Bf_2KBfe5X-phBwJWxeRsvg5j265/exec", {
                    method: "GET",
                }).then(response => response.json())
                .then(text => {
                    // console.log(text);
                    listData = text;

                    for(let todoObj of listData) {
    
                        todos[todoObj.uuid] = todoObj;

                        var faceImageName = 'chkbox';
                        var faceImageIndex = '';
                        var positionType = 1;
                        var texts = [];

                        texts.push(todoObj.task)
                        texts.push(todoObj.description)
                        if (todoObj.status == '1') {
                            faceImageName = 'checkon'
                            texts.push(`\\Picture[checkon,7-${todoObj.uuid}]`)
                        } else {
                            faceImageName = 'checkoff'
                            texts.push(`\\Picture[checkoff,7-${todoObj.uuid}]`)
                        }
    
                        var timestamp = Date.now();
    
                        var entity = new Entity_AtsuTalk(faceImageName, faceImageIndex, positionType, texts, timestamp);
                        SceneManager.currentScene()._atsuTalkService.addAtsuTalk(entity);
                        this.setWaitMode('talk');
                    }
        
                });
            } else if (args[0] == "add") {
                let params = new URLSearchParams();
                params.set('task', $gameVariables.value(11));

                const method = "POST";
                const body = params.toString();
                const headers = {
                'Accept': 'application/json',
                'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
                };
                fetch("https://script.google.com/macros/s/AKfycbyd8DRBpmbvcdtGgUbNv632Bf_2KBfe5X-phBwJWxeRsvg5j265/exec?",
                 {method, headers, body})
                 .then((res)=> res.json())
                 .then(console.log)
                 .catch(console.error);

            } else if (args[0] == "update") {
                let params = new URLSearchParams();
                params.set('action', 'update');
                params.set('uuid', $gameVariables.value(12));
                params.set('task', todos[$gameVariables.value(12)].task);
                params.set('description', todos[$gameVariables.value(12)].description);
                params.set('status', (todos[$gameVariables.value(12)].status === '1') ? '0' : '1');

                const method = "POST";
                const body = params.toString();
                const headers = {
                'Accept': 'application/json',
                'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
                };
                fetch("https://script.google.com/macros/s/AKfycbyd8DRBpmbvcdtGgUbNv632Bf_2KBfe5X-phBwJWxeRsvg5j265/exec?",
                    {method, headers, body})
                    .then((res)=> res.json())
                    .then(console.log)
                    .catch(console.error);

            };
        };
    };
})(this);
```

これで更新はできているわけですが、エラーが出ています。
CORSとタイプエラー。エラーなのに更新できている・・・
要調査ですが、いったんここまで。

今出ているエラー

```
Access to fetch at 'https://script.google.com/macros/s/・・・/exec?' from origin 'file://' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. [file:///・・・/games/RPGMV-SmartDeploy-forWEB/src/DevLab/index.html]
Failed to load resource: net::ERR_FAILED [https://script.google.com/macros/s/・・・/exec?]
TypeError: Failed to fetch
```

```
TypeError: Failed to fetch
message:"Failed to fetch"
stack:"TypeError: Failed to fetch"
__proto__:Error {constructor: , name: "TypeError", message: ""}
constructor:function TypeError() { … }
[[Scopes]]:Scopes[0]
arguments:TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
caller:TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
length:1
name:"TypeError"
prototype:Error {constructor: , name: "TypeError", message: ""}
```