Welcome to IOPWiki, Commander.
We are searching for new editors to keep track of Girls' Frontline 2 content, as well as veteran players to complete existing Girls' Frontline and Project Neural Cloud articles.
You can contribute without an account. Learn how to contribute and join our Discord server.

MediaWiki:Gadget-artTab.js: Difference between revisions

Welcome to IOP Wiki. This website is maintained by the Girls' Frontline community and is free to edit by anyone.
Jump to navigation Jump to search
First try making a progress bar
Fixed bad coding
Line 194: Line 194:
             var confun = function() {
             var confun = function() {
               counter++;
               counter++;
               showProgress(container, counter, maxItems)
               showProgress(container, counter, maxItems);
             }
             }
              
              
Line 205: Line 205:
             // Correct paths
             // Correct paths
             json.model = curl(modelId + "_live2d_model.moc" + ".skel");
             json.model = curl(modelId + "_live2d_model.moc" + ".skel");
             if (json.hasOwnProperty('physics')) { json.physics = curl(modelId + "_live2d_" + json.physics + ".txt"; }
             if (json.hasOwnProperty('physics')) { json.physics = curl(modelId + "_live2d_" + json.physics + ".txt"); }
             if (json.hasOwnProperty('pose')) { json.pose = curl(modelId + json.pose + ".txt"; }
             if (json.hasOwnProperty('pose')) { json.pose = curl(modelId + json.pose + ".txt"); }
              
              
             for (var idx=json.motions.idle.length-1; idx >= 0; idx--) {
             for (var idx=json.motions.idle.length-1; idx >= 0; idx--) {
Line 221: Line 221:
              
              
             for (var idx=json.motions.tap_figure.length-1; idx >= 0 ; idx--) {
             for (var idx=json.motions.tap_figure.length-1; idx >= 0 ; idx--) {
              // json.motions.tap_figure[idx].file = folder + json.motions.tap_figure[idx].file + ".txt";
               let job = makeBase64Request(curl(modelId + json.motions.tap_figure[idx].file + ".txt"));
               let job = makeBase64Request(curl(modelId + json.motions.tap_figure[idx].file + ".txt"));
               job.then(function(promiseData) {
               job.then(function(promiseData) {
Line 250: Line 249:
             $.when.apply($, loadingJobs)
             $.when.apply($, loadingJobs)
               .done(function(x) {
               .done(function(x) {
                 startLive2dStuff(json);
                 var l2d_tdoll_sprite = createLive2dSprite(json);
                callback(l2d_tdoll_sprite);
               })
               })
               .fail(function(x) { console.log("Failed ajax", x); })
               .fail(function(x) { console.log("Failed ajax", x); })

Revision as of 23:56, 16 January 2018

function switchVariant(artTabDivElement, variant) {
  var artTabDiv = $(artTabDivElement);
  artTabDiv.find('.artTabLinks').removeClass('active');
  artTabDiv.attr('data-tdoll-variant', variant);

  refreshView(artTabDiv);
  
  // Set "active" at the end
  artTabDiv.find(".artTabLinks[data-tdoll-variant='" + variant + "']").addClass('active');
}

function refreshView(artTabDiv) {
  var tdoll = $(artTabDiv).data('tdollId');
  var costume = $(artTabDiv).data('tdollCostume');
  var variant = $(artTabDiv).attr('data-tdoll-variant'); // why not data though?
  
  $(artTabDiv).find('.artTabContent .fullart').hide();
  
  var live2dActive = $(artTabDiv).find('.live2dstage').is(':visible');
  
  $(artTabDiv).addClass("loading");
  
  if (live2dActive) {
    createLive2dView(artTabDiv, function(view) {
      view.show();
      var stage = view.data('live2dStage');
      stage.removeChildren();
      loadLive2dGirl($(artTabDiv), tdoll, costume, variant, function(live2dData) {
        view.closest(".artTab").removeClass("loading");
        stage.addChild(live2dData);
      });
    });
  } else {
    $(artTabDiv).find(".fullart[data-variant='" + variant + "']").show();
    $(artTabDiv).removeClass("loading");
  }
}

function create_url(base, file) {
  return base + window.gfUtils.createWikiPathPart(file) + file;
}

function curl(file) {
  return create_url("../images/", file);
}

function createLive2dSprite(model) {
  // Live 2D Sprite
  var l2d_tdoll_sprite = new PIXI.Live2DSprite(model, {
    debugLog: false,
    randomMotion: false,
    eyeBlink: true
  });
  l2d_tdoll_sprite.startRandomMotion('idle');
  l2d_tdoll_sprite.on('mousemove', function(evt) {
    const point = evt.data.global;
    l2d_tdoll_sprite.setViewPoint(point.x, point.y);
  });
  l2d_tdoll_sprite.on('click', function(evt) {
    const point = evt.data.global;
    if (l2d_tdoll_sprite.hitTest('body', point.x, point.y)) {
      l2d_tdoll_sprite.startRandomMotionOnce('tap_figure');
      // Info: Maybe add individual hit tests?
    } else {
      l2d_tdoll_sprite.startRandomMotionOnce('tap_figure');
    }
  });
  
  return l2d_tdoll_sprite;
}

function base64ArrayBuffer(arrayBuffer) {
  var base64    = ''
  var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

  var bytes         = new Uint8Array(arrayBuffer)
  var byteLength    = bytes.byteLength
  var byteRemainder = byteLength % 3
  var mainLength    = byteLength - byteRemainder

  var a, b, c, d
  var chunk

  // Main loop deals with bytes in chunks of 3
  for (var i = 0; i < mainLength; i = i + 3) {
    // Combine the three bytes into a single integer
    chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]

    // Use bitmasks to extract 6-bit segments from the triplet
    a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
    b = (chunk & 258048)   >> 12 // 258048   = (2^6 - 1) << 12
    c = (chunk & 4032)     >>  6 // 4032     = (2^6 - 1) << 6
    d = chunk & 63               // 63       = 2^6 - 1

    // Convert the raw binary segments to the appropriate ASCII encoding
    base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
  }

  // Deal with the remaining bytes and padding
  if (byteRemainder == 1) {
    chunk = bytes[mainLength]

    a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2

    // Set the 4 least significant bits to zero
    b = (chunk & 3)   << 4 // 3   = 2^2 - 1

    base64 += encodings[a] + encodings[b] + '=='
  } else if (byteRemainder == 2) {
    chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]

    a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
    b = (chunk & 1008)  >>  4 // 1008  = (2^6 - 1) << 4

    // Set the 2 least significant bits to zero
    c = (chunk & 15)    <<  2 // 15    = 2^4 - 1

    base64 += encodings[a] + encodings[b] + encodings[c] + '='
  }

  return base64
}

function makeBase64Request(url) {
  let xhr = new XMLHttpRequest();
  let prom = new Promise(function (resolve, reject) {
    xhr.open('GET', url, true);
    xhr.responseType = 'arraybuffer';
    xhr.onload = function () {
      if (this.status >= 200 && this.status < 300) {
        resolve(base64ArrayBuffer(xhr.response));
      } else {
        reject({
          status: this.status,
          statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function () {
      reject({
        status: this.status,
        statusText: xhr.statusText
      });
    };
  });
  
  prom.startProcess = function(param) { xhr.send(param); };
  
  return prom;
}


function hideProgress(container) {
  let progress = $(container).find('.live2dloadingprogress').first();
  progress.hide();
}

function showProgress(container, done, length) {
  let progress = $(container).find('.live2dloadingprogress').first();
  
  if (progress.length == 0) {
    let centeringFix = $('<span></span>');
    let live2dprogress = $('<div></div>').addClass("live2dprogress").append(centeringFix);
    let live2dprogressbar = $('<div></div>').addClass("live2dprogressbar");
    let live2dprogressbarcontainer = $('<div></div>').addClass("live2dprogressbarcontainer").append(live2dprogressbar);
    progress = $('<div></div>').addClass("live2dloadingprogress").append(live2dprogress).append(live2dprogressbarcontainer);
    $(container).append(progress);
  }
  
  progress.show();
  
  let progressAmount = parseFloat(done / length * 100).toFixed(2);
  progress.find('.live2dprogress span').text(progressAmount);
  progress.find('.live2dprogressbar').css('width', progressAmount + "%");
}

function loadLive2dGirl(container, tdoll, costume, variant, callback) {
  var modelId = tdoll;
  if (costume.length > 0) {
    modelId = modelId + "_" + costume;
  }
  if (variant && variant.length > 0) {
    modelId = modelId + "_" + variant;
  }
  
  $.ajax({
  dataType: "json",
  mimeType: "text/plain",
  url: curl(modelId + "_live2d_model.txt"),
  success: function (data) {
            // Init
            var counter = 0;
            var maxItems = 0;
            var confun = function() {
              counter++;
              showProgress(container, counter, maxItems);
            }
            
            confun
            
            json = data;
            
            var loadingJobs = new Array();
            
            // Correct paths
            json.model = curl(modelId + "_live2d_model.moc" + ".skel");
            if (json.hasOwnProperty('physics')) { json.physics = curl(modelId + "_live2d_" + json.physics + ".txt"); }
            if (json.hasOwnProperty('pose')) { json.pose = curl(modelId + json.pose + ".txt"); }
            
            for (var idx=json.motions.idle.length-1; idx >= 0; idx--) {
              let job = makeBase64Request(curl(modelId + json.motions.idle[idx].file + ".txt"));
              job.then(function(promiseData) {
                let _datatoPush = "data:text/plain;base64," + promiseData;
                json.motions.idle.push({ "file": _datatoPush });
                confun();
              });
              json.motions.idle.splice(idx, 1);
              loadingJobs.push(job);
              
            }
            
            for (var idx=json.motions.tap_figure.length-1; idx >= 0 ; idx--) {
              let job = makeBase64Request(curl(modelId + json.motions.tap_figure[idx].file + ".txt"));
              job.then(function(promiseData) {
                let _datatoPush = "data:text/plain;base64," + promiseData;
                json.motions.tap_figure.push({ "file": _datatoPush });
                confun();
              });
              json.motions.tap_figure.splice(idx, 1);
              loadingJobs.push(job);
            }
            
            for (var idx=0; idx < json.textures.length; idx++) {
              let job = makeBase64Request(curl(modelId + json.textures[idx]));
              job.then(function(promiseData) {
                let _datatoPush = "data:image/png;base64," + promiseData;
                json.textures.push(_datatoPush);
                confun();
              });
              json.textures.splice(idx, 1);
              loadingJobs.push(job);
            }
            
            // Ajaxing
            maxItems = loadingJobs.length;
            for (var idxXhr=0; idxXhr < loadingJobs.length; idxXhr++) { loadingJobs[idxXhr].startProcess(null); }
            
            // Do it
            $.when.apply($, loadingJobs)
              .done(function(x) {
                var l2d_tdoll_sprite = createLive2dSprite(json);
                callback(l2d_tdoll_sprite);
              })
              .fail(function(x) { console.log("Failed ajax", x); })
              .always(function(x) { hideProgress(container); });
        }
  });
}

function doesLive2dAnimationExist(artTabDiv, costume) {
  var costumeId = costume;
  if (costumeId[0] === '_') {
    costumeId = costumeId.substr(1);
  }
  if (costume == "") {
    costumeId = "costume0";
  }
  
  return $(artTabDiv).attr("data-live2d-exist-" + costumeId) == "true";
}

function hideLive2dView(artTabDiv) {
  var live2dView = $(artTabDiv).find('.live2dstage');
  live2dView.hide();
  var stage = live2dView.data('live2dStage');
  if (stage) {
    stage.removeChildren();
  }

  refreshView(artTabDiv);
}

function createLive2dView(artTabDiv, callback) {
  var live2dView = $(artTabDiv).find('.live2dstage');
  
  if (live2dView.length < 1) {
    mw.loader.using('ext.gadget.live2d').then( function () {
	// Init renderer
	const l2d_tdoll_renderer = PIXI.autoDetectRenderer(550, 550, {transparent: true});
	const l2d_tdoll_stage = new PIXI.Container();

	// Start the animation
	function animate() {
		requestAnimationFrame(animate);
		l2d_tdoll_renderer.render(l2d_tdoll_stage);
	}
	animate();

        var view = $(l2d_tdoll_renderer.view);
        view.data('live2dStage', l2d_tdoll_stage);
        view.addClass('live2dstage');
        view.data('renderer', l2d_tdoll_renderer);

        callback(view);
    });
  } else {
    callback(live2dView.first());
  }
}

function modelChanged(event, costumeSuffix) {
  var artTabDiv = $(event.target).closest('.artTab');
  var tdollId = artTabDiv.data('tdollId');
  var artModelId = tdollId + costumeSuffix;
  var variantswitcher = artTabDiv.find('.variantswitcher');
  
  var costumeId = costumeSuffix;
  if (costumeId[0] === '_') {
    costumeId = costumeId.substr(1);
  }
  artTabDiv.attr('data-tdoll-costume', costumeId);
  
  hideLive2dView(artTabDiv);
  variantswitcher.find('.right').remove();
  
  var basePath = mw.config.get("wgServer") + "/images/";
  var cpp = window.gfUtils.createWikiPathPart;
  var fullartPath = basePath + "thumb/" + cpp(artModelId +".png") + artModelId +".png/600px-" + artModelId +".png";
  var fullartDamagedPath = basePath + "thumb/" + cpp(artModelId +"_D.png") + artModelId +"_D.png/600px-" + artModelId +"_D.png";
    
  var normArt = artTabDiv.find(".fullart:not(.damaged) a");
  normArt.attr("href", fullartPath );
  normArt.find('img').attr("src", fullartPath );
  normArt.find('img').attr("srcset", fullartPath );
  normArt.find('img').attr("alt", fullartPath );

  var damagedArt = artTabDiv.find(".fullart.damaged a");
  damagedArt.attr("href", fullartDamagedPath );
  damagedArt.find('img').attr("src", fullartDamagedPath );
  damagedArt.find('img').attr("srcset", fullartDamagedPath );
  damagedArt.find('img').attr("alt", fullartDamagedPath );

  var isLive2dPossible = doesLive2dAnimationExist(artTabDiv, costumeSuffix);
  if (isLive2dPossible) {
    var live2dButton = $('<button></button>');
    live2dButton.addClass('artTabLinks');
    live2dButton.addClass('right');
    live2dButton.click(function() {
      if (live2dButton.is('.enabled')) {
        hideLive2dView($(artTabDiv));
        live2dButton.removeClass('enabled');
      } else {
        $(artTabDiv).addClass("loading");
        createLive2dView(artTabDiv, function(view) {
          view.show();
          $(artTabDiv).find('.artTabContent').first().append(view);
          refreshView(artTabDiv);
        });
        live2dButton.addClass('enabled');
      }
    });
    live2dButton.text("Live2D");
    variantswitcher.append(live2dButton);
  }
  
  refreshView(artTabDiv);
};

RLQ.push(function () {
  $(document).ready(function() { 
    var artTabDiv = $('.artTab');
    artTabDiv.on('costume_changed', modelChanged);

    var buttonEventHandler = function(event) {
      var currentElement = $(event.target);
      var currentArtTab = currentElement.closest('.artTab');
      var variant = currentElement.data('tdollVariant');
      switchVariant(currentArtTab, variant);
    };
  
    var normalButton = $('<button></button>');
    normalButton.addClass('artTabLinks');
    normalButton.click(buttonEventHandler);
    normalButton.attr('data-tdoll-variant', "");
    normalButton.text("Normal");

    var damagedButton = $('<button></button>');
    damagedButton.addClass('artTabLinks');
    damagedButton.click(buttonEventHandler);
    damagedButton.attr('data-tdoll-variant', "D");
    damagedButton.text("Damaged");
  
    var variantswitcher = $('<div></div>');
    variantswitcher.addClass('variantswitcher');
    variantswitcher.append(normalButton);
    variantswitcher.append(damagedButton);
    
    artTabDiv.prepend(variantswitcher);

    normalButton.click();
  });
});