Source: func/textConsole.js

/**
 * @class
 * @classdesc
 * TEXT CONSOLE DISPLAY EMU LIB 
 * javascript ncurses
 * @example
 * consl = new textConsole(80, 30);
 * consl.setFontId("std"); 
 * consl.setPrompt( ["#" ,"_"]);
 * consl.setCharwidth(8);
 * consl.setLinewidth(16);
 * 
 * consl.draw(gameCore, 0 ,0);
 */
class textConsole{
    /**
     * @constructor
     * @param {number} width textBufferWidthSize
     * @param {number} column textBufferLineSize
     * @description
     * instance create
     */
   constructor(width, column){

        const textbuffer = [];
        const rewritecount = [];

        const BUFW = width;
        const BUFH = column;

        const cursor = {x:0, y:0};
        let enableScroll = false;
        let scrollCount = 0;

        let fontId;
        let prompt;
        let charw = 8;
        let linew = 16;

        let shift = {ready:false, pos:15, v:0};

        /**
         * @member
         * @type {string[]}
         * @description
         * textBuffer
         */
        this.buffer = textbuffer;
        /**
         * @member
         * @type {object}
         * @description
         * cursorPosition
         */
        this.cursor = cursor;
        /**
         * @method
         * @param {number | string} Id
         * @description
         * 使用するフォントIDの指定/ASCII font Id
         */
        this.setFontId = (fId)=>{
            fontId = fId;
        }
        /**
         * @method
         * @param {string[]} p 
         * @description
         * カーソル文字の指定
         * @example
         * ["_","#"]
         */
        this.setPrompt = (p)=>{
            prompt = p;
        }
        /**
         * @method
         * @param {number} num 行間pixel
         * @description
         * 改行幅の指定
         */
        this.setLinewidth = (num)=>{
            linew = num;
        }
        /**
         * @method
         * @param {number} num 文字幅pixel
         * @description
         * 文字幅の指定
         */
        this.setCharwidth = (num)=>{
            charw = num;
        }

        /**
         * @method
         * @param {number} new_x 移動先x座標
         * @param {number} new_y 移動先y座標
         * @description
         * カーソルを任意の位置(座標)に移動
         */
        this.move = (new_x, new_y)=>{
            if ((new_x >= 0)&&(new_x <= BUFW-1)) cursor.x = new_x;
            if ((new_y >= 0)&&(new_y <= BUFH-1)) cursor.y = new_y;
        }
        /**
         * @method
         * @param {string} chr_to_add 文字
         * @description
         * カーソル位置に1文字表示 
         */
        this.addch = (chr_to_add) =>{
            this.printw(chr_to_add);
        }
        /**
         * @method
         * @param {string} text 文字列
         * @description
         * カーソル位置から右方向に文字列を表示 
         */
        this.printw = (text) =>{
            let s = textbuffer[cursor.y];

           // console.log(s + cursor.x + cursor.y);

            let d = s.slice(0,cursor.x);

            let n = cursor.x + text.length; 
            if (n < BUFW) {
                d = d + text + s.slice(n,BUFW-1);
            } else {
                d = d + text;
                d = d.slice(0, BUFW);
            }

            textbuffer[cursor.y] = d;
            rewritecount[cursor.y]++;
        }
        /**
         * @method
         * @param {string} text 文字列
         * @param {number} x 開始x座標
         * @param {number} y 開始y座標
         * @description
         * カーソル位置を指定して右方向に文字列を表示<br>\
         * move関数とprintw関数を組み合わせた関数
         */
        this.mvprintw = (text, x, y) =>{
            this.move(x, y);
            this.printw(text);
        }
        /**
         * @method
         * @param {string} string
         * @description
         * カーソル位置に文字を挿入し、それより右の文字列を右に1文字分ずらす 
         */
        this.insch = (str) =>{
            let s = textbuffer[cursor.y];

            let d = s.slice(0,cursor.x);

            d = d + str + s.slice(cursor.x, BUFW-1);
            //d = d.slice(0, BUFW);

            textbuffer[cursor.y] = d;
            rewritecount[cursor.y]++;
        }
        /**
         * @method
         * @description
         * カーソル位置に空行を挿入し、それより下の行を下方向に1行分スクロールする 
         */
        this.insertln = () =>{

            for (let i = BUFH-1; i>cursor.y; i-- ){
                textbuffer[i] = textbuffer[i-1];
            }
            textbuffer[cursor.y] = " ".repeat(BUFW);
            rewritecount[cursor.y]++;

            shift = {ready:true, pos:linew, v:1};
        }
        /**
         * @method
         * @description
         * カーソル位置の文字を削除し、それより右の文字列を左に1文字分ずらす
         */
        this.delch = () =>{
            let s = textbuffer[cursor.y];

            let d;
            if (cursor.x > 0){
                d = s.slice(0,cursor.x);
                d = d + s.slice(cursor.x+1, BUFW-1);
            } else {
                d = s.slice(1,BUFW-1);
            }
            textbuffer[cursor.y] = d;
            rewritecount[cursor.y]++;
        }
        /**
         * @method
         * @description
         * カーソル位置の行を削除し、それより下の行を上に1行分スクロールする
         */
        this.deleteln = () =>{
            scrollUp(cursor.y);
            /*
            for (let i = cursor.y; i<BUFH-1; i++ ){
                textbuffer[i] = textbuffer[i+1];
            }
            textbuffer[BUFH-1] = " ".repeat(BUFW);
            */
            shift = {ready:true, pos:linew, v:-1};
        }
        /**
         * @method
         * @description
         * 画面全体を消去
         */
        this.clear = ()=>{
            
            for (let i=0; i<BUFH; i++){
                textbuffer[i] = " ".repeat(BUFW); 
                rewritecount[i] = 0;               
            }
        }
        // textbuffer initialize.
        this.clear();
        /**
         * @method
         * @param {boolean} mode
         * @description
         * スクロール可能/不可を設定
         * (Scroll:false/free ,true/lock 
         */
        this.scrolllock =(mode) =>{
            enableScroll = !mode;
        }
        /**
         * @method
         * @param {number} linenum
         * @description 
         * 指定行数をスクロール
         */
        this.wscrl = (linenum) =>{

            if (enableScroll && (scrollCount < 1)) { //action 
                scrollCount = linenum;
            }
            //console.log(scrollCount +" "+ enableScroll);
        }
        /**
         * 
         * @param {number} startLn
         * @description
         * inner function 
         */
        function scrollUp(startLn){
            for (let i = startLn; i<BUFH-1; i++ ){
                textbuffer[i] = textbuffer[i+1];
                rewritecount[i]++;
            }
            textbuffer[BUFH-1] = " ".repeat(BUFW);
        }

        this.rewritecheck = ()=>{
            let c = 0;
            for (let i=0; i<BUFH; i++){
                c = c + rewritecount[i];               
            }
            c = c + scrollCount;
            c = c + ((shift.ready)?1:0);

            return c;
        }
        /**
         * @method
         * @param {GameCore} g gameCoreインスタンス
         * @param {number} [x=0] 描画位置x 
         * @param {number} [y=0] 描画位置y
         * @description
         * draw funciton/描画してscreenに反映する
        */
        this.draw = (g, x = 0, y = 0)=>{

            let pos = 0;
            if (shift.ready){
                shift.pos--;

                if (shift.v > 0){
                    pos = (-shift.pos+linew)-linew;
                }else{
                    pos = shift.pos;
                }

                if (shift.pos < 1) shift.ready = false;

            }
            if (scrollCount >0){
                scrollUp(0);
                scrollCount--;
            }
            if (Boolean(fontId)){
                for (let i in textbuffer){
                    let w = 0;
                    if (i >= cursor.y) w = pos;

                    g.font[fontId].putchr(textbuffer[i], x,  y + i*linew + w);
                }        
                if (Boolean(prompt)){   
                    let d = (g.blink())?prompt[1]:prompt[0];   
                    g.font[fontId].putchr(d,x + cursor.x*charw ,y + cursor.y*linew);    
                }
                
                //g.font[fontId].putchr(`${this.rewritecheck()}`,x,y);    
            }

            for (let i=0; i<BUFH; i++){
                rewritecount[i] = 0;               
            }
        }
    } 
}