famibee/SKYNovel

View on GitHub
src/sn/Variable.ts

Summary

Maintainability
A
0 mins
Test Coverage
/* ***** BEGIN LICENSE BLOCK *****
    Copyright (c) 2018-2024 Famibee (famibee.blog38.fc2.com)

    This software is released under the MIT License.
    http://opensource.org/licenses/mit-license.php
** ***** END LICENSE BLOCK ***** */

import {uint, int, getDateStr, argChk_Boolean, argChk_Num} from './CmnLib';
import {HArg, IHTag} from './Grammar';
import {IVariable, ISetVal, typeProcVal, ISysBase, IData4Vari, IMark, IFncHook, IValMp, Scope} from './CmnInterface';
import {Config} from './Config';
import {Areas} from './Areas';
import {PropParser} from './PropParser';

import platform from 'platform';

export class Variable implements IVariable {
    #hScopes    : {[name: string]: any}    = {sys:{}, save:{}, tmp:{}, mp:{}};
    #hSave    : any    = this.#hScopes.save;
    #hTmp    : any    = this.#hScopes.tmp;


    constructor(private readonly cfg: Config, hTag: IHTag) {
        //    変数操作
        hTag.let            = o=> this.#let(o);            // 変数代入・演算
        hTag.let_abs        = o=> this.#let_abs(o);        // 絶対値
        hTag.let_char_at    = o=> this.#let_char_at(o);    // 文字列から一字取りだし
        hTag.let_index_of    = o=> this.#let_index_of(o);    // 文字列で検索
        hTag.let_length        = o=> this.#let_length(o);    // 文字列の長さ
        // let_mlはScriptIteratorにて定義                // インラインテキスト代入
        hTag.let_replace    = o=> this.#let_replace(o);    // 正規表現で置換
        hTag.let_round        = o=> this.#let_round(o);    // 四捨五入
        hTag.let_search        = o=> this.#let_search(o);    // 正規表現で検索
        hTag.let_substr        = o=> this.#let_substr(o);    // 文字列から抜きだし

        //    デバッグ・その他
        hTag.clearsysvar    = ()=> this.#clearsysvar();    // システム変数の全消去
        hTag.clearvar        = ()=> this.#clearvar();        // ゲーム変数の全消去
        hTag.dump_val        = ()=> this.#dump_val();        // 変数のダンプ

        // しおり
        hTag.copybookmark    = o=> this.#copybookmark(o);    // しおりの複写
        hTag.erasebookmark    = o=> this.#erasebookmark(o);// しおりの消去
        //hTag.load            // ScriptIterator.ts内で定義    // しおりの読込
        //hTag.record_place    // ScriptIterator.ts内で定義    // セーブポイント指定
        //hTag.save            // ScriptIterator.ts内で定義    // しおりの保存

        // save:
        this.#hSave['sn.userFnTail']    = '';
        this.defTmp('const.sn.bookmark.json', ()=> {
            const a: object[] = [];
            for (const k of Object.keys(this.#data.mark).sort()) {
                const o = {...this.#data.mark[k].json};
                o.place = k;
                a.push(o);    // パスを searchPath() で展開してはいけない
            }
            return JSON.stringify(a);
        });

        // tmp:
        this.#hTmp['const.sn.isFirstBoot'] = true;

        this.#hTmp['const.sn.last_page_text'] = '';
        this.#hTmp['const.sn.last_page_plain_text'] = '';

        //this.hTmp['const.sn.mouse.middle']    // ScriptIterator で定義

        //this.hTmp['const.sn.vctCallStk.length']    // ScriptIterator で定義

/*        this.hTmp['const.Stage.supportsOrientationChange']
            = Stage.supportsOrientationChange;
        if (this.hTmp['const.Stage.supportsOrientationChange']) {
            this.hTmp['const.Stage.orientation']
                = ()=> {return stage.orientation};
            this.hTmp['const.Stage.deviceOrientation']
                = ()=> {return stage.deviceOrientation};

            const lenSO:uint = stage.supportedOrientations.length;
            for (let iSO:uint=0; iSO<lenSO; ++iSO) {
                this.hTmp['const.Stage.supportedOrientations.'
                    + stage.supportedOrientations[iSO]
                ] = true;
            }
        }
        else {
            //    import flash.display.StageOrientation;
            this.hTmp['const.Stage.orientation'] =
            this.hTmp['const.Stage.deviceOrientation'] =
            //        StageOrientation.DEFAULT;
                'default';
        }
*/
        this.#hTmp['const.sn.displayState'] = false;
            // const.flash.display.Stage.displayState

        this.#hTmp['const.Date.getTime'] = ()=> (new Date).getTime();
        this.#hTmp['const.Date.getDateStr'] = ()=> getDateStr();

        this.#hTmp['const.sn.platform'] = JSON.stringify(platform);

        this.#clearsysvar();
        this.#clearvar();

        // prj.json
        this.#hTmp['const.sn.config.window.width'] = cfg.oCfg.window.width;
        this.#hTmp['const.sn.config.window.height']= cfg.oCfg.window.height;
        this.#hTmp['const.sn.config.book.title'] = cfg.oCfg.book.title;
        this.#hTmp['const.sn.config.book.version'] = cfg.oCfg.book.version;

        this.#hTmp['const.sn.Math.PI'] = Math.PI;


        if (typeof window === 'undefined') return;
        const win: any = window;
        const ac = win['AudioContext'] ?? win['webkitAudioContext'];
        this.#hTmp['const.sn.needClick2Play'] = ()=> new ac().state === 'suspended';
    }


    #sys    : ISysBase;
    #data    : IData4Vari    = {sys:{}, mark:{}, kidoku:{}};
    #hSys    : any;
    #hAreaKidoku    : {[name: string]: Areas}    = {};
    #callHook        : IFncHook;
    async    setSys(sys: ISysBase) {
        this.#sys = sys;
        await sys.initVal(this.#data, this.#hTmp, data=> {
            this.updateData(data);

            sessionStorage.clear();
            const ns = this.cfg.getNs();
            this.#flush = this.cfg.oCfg.debug.variable ?()=> {
                const oSys: any = {};
                for (const [k, v] of Object.entries(this.#hSys)) {
                    oSys['sys:'+ k] = (v instanceof Function) ?v(): v;
                }
                sessionStorage[ns +'sys'] = JSON.stringify(oSys);

                const oSave: any = {};
                for (const [k, v] of Object.entries(this.#hSave)) {
                    oSave['save:'+ k] = (v instanceof Function) ?v(): v;
                }
                sessionStorage[ns+'save'] = JSON.stringify(oSave);

                const oTmp: any = {};
                for (const [k, v] of Object.entries(this.#hTmp)) {
                    oTmp[k] = (v instanceof Function) ?v(): v;
                }
                sessionStorage[ns +'tmp'] = JSON.stringify(oTmp);

                const oMp: any = {};
                for (const [k, v] of Object.entries(this.#hScopes.mp)) {
                    oMp[k] = (v instanceof Function) ?v(): v;
                }
                sessionStorage[ns +'mp'] = JSON.stringify(oMp);

                const oMark: any = {};
                for (const [k, v] of Object.entries(this.#data.mark)) {
                    oMark[k] = (v instanceof Function) ?v(): v;
                }
                sessionStorage[ns+'mark'] = JSON.stringify(oMark);

                const oKidoku: any = {};
                for (const [k, v] of Object.entries(this.#data.kidoku)) {
                    oKidoku[k] = (v instanceof Function) ?v(): v;
                }
                sessionStorage[ns +'kidoku'] = JSON.stringify(oKidoku);

                sys.flush();
            }
            : ()=> sys.flush();

            this.#callHook = (type, o)=> sys.callHook(type, o);
        //x    this.callHook = sys.callHook;
            sys.addHook((type, o)=> this.#hProcDbgRes[type]?.(type, o));

            // 初回の初期化と、v1.11.0 まで未初期化変数があった件の対策
            const tm = this.getVal('sys:sn.tagCh.msecWait', -1);
            if (this.#hTmp['const.sn.isFirstBoot'] || tm === -1) this.#clearsysvar();
        });
    }
    readonly    #hProcDbgRes
    : {[type: string]: (type: string, o: any)=> void}    = {
        auth: (_, o)=> this.#set_data_break(o.hBreakpoint.aData),
        var    : (_,o)=> this.#sys.send2Dbg(o.ri, {v: this.#hScopes[o.scope] ?? {}}),
        set_var    : (_, o)=> {
            try {this.#setVal(o.nm, o.val); this.#sys.send2Dbg(o.ri, {})} catch {}
        },
        set_data_break    : (_, o)=> {
            this.#set_data_break(o.a);
            this.#sys.send2Dbg(o.ri, {});
        },
        disconnect: _=> Variable.#hSetEvent = {},
    }
    #set_data_break(a: any[]) {    // o.a.length === 0 なら削除
        Variable.#hSetEvent = {};
        for (const v of a) Variable.#hSetEvent[v.dataId] = 1;
    }

    updateData(data: IData4Vari): void {
        this.#data = data;
        this.#hSys = this.#hScopes.sys = this.#data.sys;

        this.#hAreaKidoku = {};
        for (const [fn, v] of Object.entries(this.#data.kidoku)) {
            const areas = new Areas;
            areas.hAreas = {...v};
            this.#hAreaKidoku[fn] = areas;
        }
    }
    #flush    = ()=> {};
    flush() {this.#flush()}    // 先にこのメソッドへの参照を配ってしまうので、中身を入れ替える

    setDoRecProc(fnc: (doRec: boolean)=> void) {this.#doRecProc = fnc}
    #doRecProc = (_doRec: boolean)=> {};

    defTmp(name: string, fnc: typeProcVal) {this.#hTmp[name] = fnc};
    cloneMp() {return {...this.#hScopes.mp}}
    setMp(mp: IValMp) {this.#hScopes.mp = mp}
    setMark(place: number, mark: IMark) {this.#data.mark[place] = mark; this.flush()}
    readonly    getMark = (place: number)=> this.#data.mark[place];
    cloneSave() {return {...this.#hScopes.save}}
    mark2save(mark: IMark) {
        this.#hSave = this.#hScopes.save = {...mark.hSave};
        this.#doRecLog    = this.#hSave['sn.doRecLog'] ?? false;
    }


    // 既読系
    touchAreaKidoku(fn: string): Areas {return this.#hAreaKidoku[fn] ??= new Areas}
    readonly    getAreaKidoku = (fn: string)=> this.#hAreaKidoku[fn];
    saveKidoku() {
        for (const [fn, {hAreas}] of Object.entries(this.#hAreaKidoku)) {
            this.#data.kidoku[fn] = {...hAreas};
        }
        this.flush();
    }


//    // しおり
    // しおりの複写
    #copybookmark(hArg: HArg) {
        if (! ('from' in hArg)) throw 'fromは必須です';
        if (! ('to' in hArg)) throw 'toは必須です';

        const from = Number(hArg.from);
        const to = Number(hArg.to);
        if (from === to) return false;

        if (! (from in this.#data.mark)) throw `from:${from} のセーブデータは存在しません`;
        this.setMark(to, {...this.#data.mark[from]});
        this.#sys.copyBMFolder(from, to);

        return false;
    }

    // しおりの消去
    #erasebookmark(hArg: HArg) {
        const {place} = hArg;
        if (! place) throw 'placeは必須です';

        delete this.#data.mark[place];
        this.flush();

        this.#sys.eraseBMFolder(place);

        return false;
    }


//    //    変数操作
    // 変数代入・演算
    #let(hArg: HArg) {
        if (! hArg.name) throw 'nameは必須です';

        let autocast = true;
        if (hArg.cast) {
            //switch (trim(hArg.cast)) {
            switch (hArg.cast) {
            case 'num':
                argChk_Num(hArg, 'text', NaN);
                break;
            case 'int':
                hArg.text = String(int(argChk_Num(hArg, 'text', NaN)));
                break;
            case 'uint':
                hArg.text = String(uint(argChk_Num(hArg, 'text', NaN)));
                break;
            case 'bool':
                argChk_Boolean(hArg, 'text', false);
                break;
            case 'str':
                autocast = false;
                break;
            default:
                throw 'cast【'+ hArg.cast +'】は未定義です';
            }
        }

        this.#setVal(hArg.name, hArg.text, autocast);

        return false;
    }

    // 絶対値
    #let_abs(hArg: HArg) {
        const n = argChk_Num(hArg, 'text', 0);
        //hArg.text = Math.abs(n);
        hArg.text = String((n < 0) ?-n :n);
            // JavaScriptのMath.abs()で絶対値を取得しないほうが良い理由 | iwb.jp https://iwb.jp/javascript-math-abs-deprecated/
            // 数値以外だとほとんどがNaNを返し、booleanは0や1を返しているため使い方によってはバグの原因になることがある。
        this.#let(hArg);

        return false;
    }

    // 文字列から一字取りだし
    #let_char_at(hArg: HArg) {
        hArg.text = (hArg.text ?? '').charAt(argChk_Num(hArg, 'pos', 0));
        this.#let(hArg);

        return false;
    }

    // 文字列で検索
    #let_index_of(hArg: HArg) {
        const {val} = hArg;
        if (! val) throw 'valは必須です';
        const start = argChk_Num(hArg, 'start', 0);

        hArg.text = String((hArg.text ?? '').indexOf(val, start));
        this.#let(hArg);

        return false;
    }

    // 文字列の長さ
    #let_length(hArg: HArg) {
        hArg.text = String((hArg.text ?? '').length);
        this.#let(hArg);

        return false;
    }

    // 正規表現で置換
    #let_replace(hArg: HArg) {
        if (! hArg.reg) throw 'regは必須です';

        const {flags} = hArg;
        const reg = (! flags)
            ? new RegExp(hArg.reg)
            : new RegExp(hArg.reg, flags);
        hArg.text = String(hArg.text ?? '').replace(reg, String(hArg.val));
        this.#let(hArg);

        return false;
    }

    // 四捨五入
    #let_round(hArg: HArg) {
        const n = argChk_Num(hArg, 'text', 0);
        hArg.text = String(Math.round(n));
        this.#let(hArg);

        return false;
    }

    // 正規表現で検索
    #let_search(hArg: HArg) {
        if (! hArg.reg) throw 'regは必須です';

        const {flags} = hArg;
        const reg = (! flags)
            ? new RegExp(hArg.reg)
            : new RegExp(hArg.reg, flags);
        hArg.text = String((hArg.text ?? '').search(reg));
        this.#let(hArg);

        return false;
    }

    // 文字列から抜きだし
    #let_substr(hArg: HArg) {
        const i = argChk_Num(hArg, 'pos', 0);
        hArg.text = (hArg.len !== 'all')
            ? (hArg.text ?? '').slice(i, i +int(argChk_Num(hArg, 'len', 1)))
            : (hArg.text ?? '').slice(i);
        this.#let(hArg);

        return false;
    }


//    // デバッグ・その他
    // システム変数の全消去
    #clearsysvar() {
        const sys = this.#hSys = this.#hScopes['sys'] = this.#data.sys = {};

        const is_nw = (typeof process !== 'undefined');
        if (is_nw) {
        //    //    this.setVal_Sub('sys:const.sn.window.x', stage.nativeWindow.x);
        //    //    this.setVal_Sub('sys:const.sn.window.y', stage.nativeWindow.y);
        }
        else {
            this.setVal_Nochk('sys', 'const.sn.window.x', 0);
            this.setVal_Nochk('sys', 'const.sn.window.y', 0);
        }

        // 文字表示Waitをかけるか
        this.setVal_Nochk('sys', 'sn.tagCh.doWait', true);
        this.setVal_Nochk('sys', 'sn.tagCh.doWait_Kidoku', true);    // 【既読】
        // 文字表示Wait時間
        this.setVal_Nochk('sys', 'sn.tagCh.msecWait', this.cfg.oCfg.init.tagch_msecwait);
        this.setVal_Nochk('sys', 'sn.tagCh.msecWait_Kidoku', this.cfg.oCfg.init.tagch_msecwait);
            // 【既読】
        // 文字表示Wait中スキップのモード
        this.setVal_Nochk('sys', 'sn.tagCh.canskip', true);

        // スキップのモード
        this.setVal_Nochk('sys', 'sn.skip.mode', 's');    // l, p, s

        // 自動読みすすみモード時の改ページ時のウェイト
        //    //    runFirst_sys_an_auto_msecPageWait('sn.auto.msecPageWait', '');
        this.setVal_Nochk('sys', 'sn.auto.msecPageWait', argChk_Num(sys, 'sn.auto.msecPageWait', this.cfg.oCfg.init.auto_msecpagewait ?? 3500));
        this.setVal_Nochk('sys', 'sn.auto.msecPageWait_Kidoku', argChk_Num(sys, 'sn.auto.msecPageWait', this.cfg.oCfg.init.auto_msecpagewait ?? 3500));
        // 自動読みすすみモード時の行クリック待ち時のウェイト
        this.setVal_Nochk('sys', 'sn.auto.msecLineWait', 500);
        this.setVal_Nochk('sys', 'sn.auto.msecLineWait_Kidoku', 500);    // 【既読】

        //    SoundMixer.soundTransform = new SoundTransform(
        //        (sys['flash.media.SoundMixer.soundTransform.volume'] = 1)
        //    );
        this.setVal_Nochk('sys', 'const.sn.sound.BGM.volume', 1);
        this.setVal_Nochk('sys', 'const.sn.sound.SE.volume', 1);
        this.setVal_Nochk('sys', 'const.sn.sound.SYS.volume', 1);
        for (const [fn, v] of Object.entries(this.#data.kidoku)) {
            v.hAreas = {};
            this.#hAreaKidoku[fn].clear();
        }


        this.setVal_Nochk('sys', 'TextLayer.Back.Alpha', 0.5);


        this.#hScopes['mark'] = this.#data.mark = {};
        this.setVal_Nochk('sys', 'const.sn.save.place', 1);


        this.flush();

        return false;
    }

    // ゲーム変数の全消去
    #clearvar() {
        const mesLayer    = this.#hSave['const.sn.mesLayer'] ?? '';
        const doRecLog    = this.#hSave['sn.doRecLog'] ?? false;
        const sLog        = this.#hSave['const.sn.sLog'] ?? '[]';

        this.#hSave = this.#hScopes.save = {};

        this.setVal_Nochk('save', 'const.sn.mesLayer', mesLayer);
        this.setVal_Nochk('save', 'sn.doRecLog', doRecLog);
        this.setVal_Nochk('save', 'const.sn.sLog', sLog);

        return false;
    }

    readonly #setVal = (arg_name: string, val: any, autocast = true)=> {
        if (! arg_name) throw '[変数に値セット] nameは必須です';
        if (val == null) throw '[変数に値セット] textは必須です(空文字はOK)';

        const o = PropParser.getValName(arg_name);
        if (o === null) throw '[変数参照] name('+ arg_name +')が変数名として異常です';

        const hScope = this.#hScopes[o.scope];
        if (! hScope) throw '[変数に値セット] scopeが異常【'+ o.scope +'】です';

        const nm = o['name'];
        if (nm.slice(0, 6) === 'const.' && (nm in hScope)) {
            throw '[変数に値セット] 変数【'+ nm +'】は書き換え不可です';
        }

        this.setVal_Nochk(<Scope>o.scope, nm, val, autocast);
    }
    setVal_Nochk(scope: Scope, nm: string, val: any, autocast = false) {
        const hScope = this.#hScopes[scope];
        if (autocast) val = this.#castAuto(val);

        const fullnm = scope +':'+ nm;
        if (fullnm in Variable.#hSetEvent) {
            const old_v = hScope[nm];
            const new_v = val;
            if (old_v != new_v) this.#callHook('data_break', {
                dataId: fullnm,
                old_v: old_v,
                new_v: new_v,
            });
        }

        hScope[nm] = val;

        this.#hValTrg[fullnm]?.(nm, val);

        // if (scope === 'sys') this.flush()
            // 厳密にはここですべきだが、パフォーマンスに問題があるので
            // クリック待ちを期待できるwait、waitclick、s、l、pタグで
            // saveKidoku()をコール。(中で保存しているのでついでに)

        //console.log(`\tlet s[${scope}] n[${nm}]='${val}' trg[${(trg)}]`);
    }
    // reload 再生成 Main に受け渡すため static
    static    #hSetEvent: {[fullnm: string]: 1} = {};

    readonly getVal = (arg_name: string, def?: number | string)=> {
        if (! arg_name) throw '[変数参照] nameは必須です';

        const o = PropParser.getValName(arg_name);
        if (o === null) throw '[変数参照] name('+ arg_name +')が変数名として異常です';

        const hScope = this.#hScopes[o['scope']];
        if (! hScope) throw '[変数参照] scopeが異常【'+ o['scope'] +'】です';

        const val_name = o['name'];
        let val = hScope[val_name];
//console.log(`fn:Variable.ts line:527 ・getVal arg_name=${arg_name}= val_name=${val_name}= A:${! (val_name in hScope)} val:%o`, val);
        if (! (val_name in hScope)) {    // 存在しない変数名の場合、刻んで調べていく
            val = def;

            let nm = '';
            const aNm = val_name.split('.');
            const len = aNm.length;
            for (let i=0; i<len; ++i, nm += '.') {
                nm += aNm[i];
//console.log(`fn:Variable.ts line:546  nm:${nm}`);
                if (! (nm in hScope)) continue;    // 存在しない変数名の場合、延ばす

                let v = JSON.parse(hScope[nm]);
//console.log(`fn:Variable.ts line:550   nm:${nm} type:${Object.prototype.toString.call(v)} v:%o`, v);
                if (Object.prototype.toString.call(v) !== '[object Object]') {
//console.log(`fn:Variable.ts line:552   != o i:${i} len:${len} C:${i +1 === len}`);
                    if (i +1 === len) {val = v; break}    // 最下層ならそのまま返す
                    continue;
                        // 短い名前でヒットしたが、JSONでもなく
                        // 変数名にはまだ続きがあるので探索続行
                }

                let j = i;    // JSONオブジェクトの階層を降りつつ探索
                while (++j < len) {
//console.log(`fn:Variable.ts line:561   nm:${nm} j:${j} aNm[j]=${aNm[j]}= A:${! (aNm[j] in v)}`);
                    if (! (aNm[j] in v)) {val = def; break}
                    v = v[aNm[j]];
//console.log(`fn:Variable.ts line:564   v:${v} J:${j +1 === len}`);
                    if (Object.prototype.toString.call(v) !== '[object Object]'
                        || j +1 === len) {val = v; break}// 最下層ならそのまま返す
                }

                if (val instanceof Object) val = JSON.stringify(val);
                break;
            }
        }
        if (val instanceof Function) val = val();
        //console.log('\tget ['+ arg_name +'] -> s['+ o['scope'] +'] a['+ o['at'] +'] n['+ name +'] ret['+ val +']('+ typeof val +')');

        return (o['at'] === '@str') ?val :this.#castAuto(val);
    }

    #castAuto(val: Object): any {
        const s_val = val as string;
        if (s_val === 'true') return true;
        if (s_val === 'false') return false;
        if (s_val === 'null') return null;
        if (s_val === 'undefined') return undefined;
        if (this.#REG_NUMERICLITERAL.test(s_val)) return parseFloat(s_val);

        return val;
    }
    #REG_NUMERICLITERAL    :RegExp    = /^-?[\d\.]+$/;


    // 変数のダンプ
    readonly #dump_val = ()=> {
        const val: {[nm: string]: any} = {tmp:{}, sys:{}, save:{}, mp:{}};
        for (let scope in val) {
            const hVal: {[nm: string]: any} = this.#hScopes[scope];
            const hRet = val[scope];
            for (let [key, v] of Object.entries(hVal)) {
                hRet[key] = Object.prototype.toString.call(v) === '[object Function]' ?v() :v;
            }
        }
        console.info('🥟 [dump_val]', val);

        return false;
    }

    #doRecLog    = false;
    doRecLog() {return this.#doRecLog}


    #hValTrg    : {[name: string]: ISetVal}    = {
        // sys
        'sys:sn.tagCh.doWait'            : name=>
            this.#runFirst_Bool_hSysVal_true(name),
        'sys:sn.tagCh.doWait_Kidoku'    : name=>
            this.#runFirst_Bool_hSysVal_true(name),
        'sys:sn.tagCh.msecWait'            : name=>
            this.#runFirst_sys_an_tagCh_msecWait(name),
        'sys:sn.tagCh.msecWait_Kidoku'    : name=>
            this.#runFirst_sys_an_tagCh_msecWait_Kidoku(name),
        'sys:sn.tagCh.canskip'            : name=>
            this.#runFirst_Bool_hSysVal_true(name),

        'sys:sn.auto.msecPageWait'            : name=>
            this.#runFirst_sys_an_auto_msecPageWait(name),
        'sys:sn.auto.msecPageWait_Kidoku'    : name=>
            this.#runFirst_sys_an_auto_msecPageWait(name),
        'sys:sn.auto.msecLineWait'            : name=>
            this.#runFirst_sys_an_auto_msecLineWait(name),
        'sys:sn.auto.msecLineWait_Kidoku'    : name=>
            this.#runFirst_sys_an_auto_msecLineWait(name),

        // save
        'save:sn.doRecLog'        : name=> {
            this.#doRecProc(
                this.#doRecLog = this.#runFirst_Bool_hSaveVal_true(name)
            );
        },
        'save:sn.userFnTail'    : (_name, val)=> {
            if (val.includes('@')) throw `この変数では文字「@」は禁止です`;
            this.cfg.userFnTail = val;
        },

        // tmp
        'tmp:flash.desktop.NativeApplication.nativeApplication.systemIdleMode'    : (
            ()=> {
            //    NativeApplication.nativeApplication.systemIdleMode = val;
            }
        ),
    };
    defValTrg(name: string, fnc: ISetVal) {this.#hValTrg[name] = fnc}
    #runFirst_Bool_hSysVal_true(name: string): void {
            argChk_Boolean(this.#hSys, name, true);
        }
    #runFirst_sys_an_tagCh_msecWait(name: string): void {
            argChk_Num(this.#hSys, name, 10);
            if (this.#hSys['sn.tagCh.doWait']) {
//                LayerMng.msecChWait = this.hSysVal[name];
            }
        }
    #runFirst_sys_an_tagCh_msecWait_Kidoku(name: string): void {
        argChk_Num(this.#hSys, name,
            (this.cfg.oCfg.init.tagch_msecwait === undefined)
                ? 10
                : this.cfg.oCfg.init.tagch_msecwait
        );
        if (this.#hSys['sn.tagCh.doWait_Kidoku']) {
//            LayerMng.msecChWait = this.hSysVal[name];
        }
    }
    #runFirst_sys_an_auto_msecPageWait(name: string): void {
        argChk_Num(this.#hSys, name,
            (this.cfg.oCfg.init.auto_msecpagewait === undefined)
                ? 3500
                : this.cfg.oCfg.init.auto_msecpagewait
        );
    }
    #runFirst_sys_an_auto_msecLineWait(name: string): void {
        argChk_Num(this.#hSys, name, 500);
    }

    #runFirst_Bool_hSaveVal_true(name: string) {
        return argChk_Boolean(this.#hSave, name, true);
    }

};