XDUPlayer/Js/Player.js
louis 1d469d4f3e webpack, eslint
javascript didn't have proper modules until es6 what the fuck

bingo's code scares me so i hope i didn't break it when fixing
the linting errors
2019-05-12 20:59:45 -04:00

1559 lines
56 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
/* global PIXI */
import 'Pixi';
import { baseDimensions, commonFunctions } from './Common.js';
class Player {
constructor(pixi, utage, text, audio, shaderscript) {
this.pixi = pixi;
this.loader = pixi.loader;
this.utage = utage;
this.text = text;
this.audio = audio;
this.shaders = shaderscript;
//consts
this.resolutionScale = 1; //I created this thinking that I would need to handle changing offset when resolution changes. But lucikly I can just scale the parent container and it works without needing this.
this.baseFps = 60; //I am assuming that PIXI is going to stay as keeping 60fps = delta1.
this.bgLayerName = "背景"; //The label for the BG layer.
this.defaultCharPattern = 'すまし'; //The mission file doesn't always give a pattern for putting a character on the screen.
this.backCharTint = 0x808080;
this.titleWaitTime = 5;
this.currentCharacters = {};
this.layers = {};
this.currentCommand = undefined;
this.runEvent = false;
this.secondTicker = 1000;
this.waitTime = 0;
this.lerpTargets = [];
this.manualNext = false;
this.hasMoreText = false;
this.uiHidden = false;
this.center = {x: ((baseDimensions.width / 2) * this.resolutionScale), y: ((baseDimensions.height / 2) * this.resolutionScale) };
this.assetLoadPercent = 0;
this.audioLoadPercent = 0;
this.playingVoice = undefined;
}
playFile() {
let runningPromise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
this.preCheckFilesToGet()
.then((success) => {
this.pixi.app.ticker.add(this.onPixiTick, this);
}, (failure) => {
console.log(failure);
});
return runningPromise;
}
//Runs through the tsv file and loads any files it will need to play.
preCheckFilesToGet() {
return new Promise((resolve, reject) => {
let toLoadBgm = {};
let toLoadSe = {};
for(let i = 0; i < this.utage.currentPlayingFile.length; ++i) {
try {
let c = this.utage.currentPlayingFile[i];
if(c.comment) { continue; }
//They use this to set the sprite set for a charater but have an alternate name displayed
if(c.Arg2 && c.Arg2.toLowerCase().includes("<character=")) {
try {
let reg = /<Character=.*?>/i;
let match = c.Arg2.match(reg);
c.Arg2 = c.Arg2.replace(reg, "");
c.Character = match[0].match(/<Character=(.*?)>/i)[1];
} catch (error) {
console.log(error);
}
}
let command = c.Command ? c.Command.toLowerCase() : '';
switch(command) {
//BG images
case "bg":
if(this.utage.textureInfo[c.Arg1]) {
if(!this.loader.resources[`bg|${c.Arg1}`]) {
this.loader.add(`bg|${c.Arg1}`, this.utage.textureInfo[c.Arg1].FileName);
}
} else if(!this.utage.textureInfo[c.Arg1]) {
console.log(`Failed to get BG: ${c.Arg1}`);
}
break;
//Text
case "": {
//Character Text
let Arg2 = c.Arg2;
//because fuck me.
if(Arg2 === 'Off>') {
Arg2 = '<Off>'
}
let charToLoad = c.Character || c.Arg1;
if(charToLoad && this.utage.characterInfo[charToLoad] && !Arg2) {
Arg2 = this.defaultCharPattern;
}
//I know the nesting here isint pretty
//If the character at arg1|arg2 exists and arg2 is not <off>
if(this.utage.characterInfo[charToLoad] && this.utage.characterInfo[charToLoad][Arg2] && Arg2 && Arg2 != "<Off>") {
if(!this.loader.resources[`char|${charToLoad}|${Arg2}`]) {
this.loader.add(`char|${charToLoad}|${Arg2}`, this.utage.characterInfo[charToLoad][Arg2].FileName);
}
//If the character at arg1|arg2 isint here check at arg1|none. If not there put error in console.
} else if(charToLoad && Arg2 && Arg2 != "<Off>" &&
(!this.utage.characterInfo[charToLoad] || !this.utage.characterInfo[charToLoad][Arg2])) {
if(this.utage.characterInfo[charToLoad] && this.utage.characterInfo[charToLoad]['none']) {
if(!this.loader.resources[`char|${charToLoad}|none`]) {
this.loader.add(`char|${charToLoad}|none`, this.utage.characterInfo[charToLoad]['none'].FileName);
}
} else {
console.log(`Failed to get Character: ${charToLoad}|${Arg2}`);
}
}
//These voices arent in the Sound.tsv because fuck you
if(c.Voice) {
let filename = c.Voice;
if(filename.includes(',')) {
let s = filename.split(',');
filename = `${s[0].split('_').join('/')}/${s[1]}`;
}
filename = `${this.utage.rootDirectory}XDUData/Voice/${filename}.opus`;
if(!toLoadSe[c.Voice]) {
toLoadSe[c.Voice] = { Label: c.Voice, FileName: filename };
}
}
break;
}
case "bgm":
if(this.utage.soundInfo[c.Arg1]) {
if(!toLoadBgm[c.Arg1]) {
toLoadBgm[c.Arg1] = this.utage.soundInfo[c.Arg1];
}
} else {
console.log(`Failed to get BGM: ${c.Arg1}`);
}
break;
case "se":
if(this.utage.soundInfo[c.Arg1]) {
if(!toLoadSe[c.Arg1]) {
toLoadSe[c.Arg1] = this.utage.soundInfo[c.Arg1];
}
} else {
console.log(`Failed to get SE: ${c.Arg1}`);
}
break;
case "henshin01_bgmoff": {
let Arg2 = c.Arg2;
if(c.Arg1 && this.utage.characterInfo[c.Arg1] && !Arg2) {
Arg2 = this.defaultCharPattern;
}
if(this.utage.characterInfo[c.Arg1] && this.utage.characterInfo[c.Arg1][Arg2]) {
if(!this.loader.resources[`char|${c.Arg1}|${Arg2}`]) {
this.loader.add(`char|${c.Arg1}|${Arg2}`, this.utage.characterInfo[c.Arg1][Arg2].FileName);
}
}
if(this.utage.soundInfo["se_変身演出"]) {
if(!toLoadSe["se_変身演出"]) {
toLoadSe["se_変身演出"] = this.utage.soundInfo["se_変身演出"];
}
} else {
console.log(`Failed to get Henshin SE: se_変身演出`);
}
break;
}
case "henshin01": {
let Arg2 = c.Arg2;
if(c.Arg1 && this.utage.characterInfo[c.Arg1] && !Arg2) {
Arg2 = this.defaultCharPattern;
}
if(this.utage.characterInfo[c.Arg1] && this.utage.characterInfo[c.Arg1][Arg2]) {
if(!this.loader.resources[`char|${c.Arg1}|${Arg2}`]) {
this.loader.add(`char|${c.Arg1}|${Arg2}`, this.utage.characterInfo[c.Arg1][Arg2].FileName);
}
}
if(this.utage.soundInfo[c.Arg4]) {
if(!toLoadBgm[c.Arg4]) {
toLoadBgm[c.Arg4] = this.utage.soundInfo[c.Arg4];
}
} else {
console.log(`Failed to get BGM: ${c.Arg4}`);
}
if(this.utage.soundInfo["se_変身演出"]) {
if(!toLoadSe["se_変身演出"]) {
toLoadSe["se_変身演出"] = this.utage.soundInfo["se_変身演出"];
}
} else {
console.log(`Failed to get Henshin SE: se_変身演出`);
}
break;
}
case "somethingnew_appearance01":
case "unhappyseed_appearance01":
case "unhappyseed_appearance02":
case "arcanoise_appearance02":
case "arcanoise_appearance03":
case "darkaura01": {
switch(c.Command ? c.Command.toLowerCase() : '') {
case "somethingnew_appearance01":
if(this.utage.soundInfo['Se_サムシング・ニューの叫び声(アアア”ア”ア”)']) {
toLoadSe['Se_サムシング・ニューの叫び声(アアア”ア”ア”)'] = this.utage.soundInfo['Se_サムシング・ニューの叫び声(アアア”ア”ア”)'];
} else {
console.log(`Failed to get somethingnew_appearance01 SE: Se_サムシング・ニューの叫び声(アアア”ア”ア”)`);
}
break;
case "darkaura01":
case "unhappyseed_appearance02":
case "unhappyseed_appearance01":
if(this.utage.soundInfo['Se_不幸のオーラ(ヴォォオンン)']) {
toLoadSe['Se_不幸のオーラ(ヴォォオンン)'] = this.utage.soundInfo['Se_不幸のオーラ(ヴォォオンン)'];
} else {
console.log(`Failed to get unhappyseed_appearance SE: Se_不幸のオーラ(ヴォォオンン)`);
}
break;
}
let pat = this.defaultCharPattern;
if(c.Arg1) {
if(this.utage.characterInfo[c.Arg1] && this.utage.characterInfo[c.Arg1][pat]) {
if(!this.loader.resources[`char|${c.Arg1}|${pat}`]) {
this.loader.add(`char|${c.Arg1}|${pat}`, this.utage.characterInfo[c.Arg1][pat].FileName);
}
}
}
if(c.Arg2) {
if(this.utage.characterInfo[c.Arg2] && this.utage.characterInfo[c.Arg2][pat]) {
if(!this.loader.resources[`char|${c.Arg2}|${pat}`]) {
this.loader.add(`char|${c.Arg2}|${pat}`, this.utage.characterInfo[c.Arg2][pat].FileName);
}
}
}
if(c.Arg3) {
if(this.utage.characterInfo[c.Arg3] && this.utage.characterInfo[c.Arg3][pat]) {
if(!this.loader.resources[`char|${c.Arg3}|${pat}`]) {
this.loader.add(`char|${c.Arg3}|${pat}`, this.utage.characterInfo[c.Arg3][pat].FileName);
}
}
}
break;
}
case "scenetitle01":
//this isint in the texture file.
this.loader.add('bg|titlecard', `${this.utage.rootDirectory}XDUData/Sample/Texture/BG/bg_title.jpg`);
break;
case "scenetitlebridal":
this.loader.add('bg|titlecard', `${this.utage.rootDirectory}CustomData/Sample/Texture/BG/eventbridal.png`);
break;
case "scenetitle13":
this.loader.add('bg|titlecard', `${this.utage.rootDirectory}XDUData/Sample/Texture/BG/event0001.png`);
break;
case "loopeffect01": {
switch(c.Arg1) {
case "underwater01": {
if(!toLoadSe['se_斬撃音_単体']) {
toLoadSe['se_斬撃音_単体'] = this.utage.soundInfo['se_斬撃音_単体'];
}
if(!this.loader.resources['bg|underwater01']) {
this.loader.add('bg|underwater01', `${this.utage.rootDirectory}CustomData/Sample/Texture/BG/bg_underwater01.jpg`);
}
break;
}
}
break;
}
case "attachit02":
case "attachit03":
if(this.utage.soundInfo['se_打撃音']) {
toLoadSe['se_打撃音'] = this.utage.soundInfo['se_打撃音'];
} else {
console.log(`Failed to get attachit SE: se_打撃音`);
}
break;
case "attacshot02":
if(this.utage.soundInfo['se_銃撃単発']) {
toLoadSe['se_銃撃単発'] = this.utage.soundInfo['se_銃撃単発'];
} else {
console.log(`Failed to get attacshot SE: se_ガトリング音`);
}
break;
case "attacshot11":
case "attacshot12":
if(this.utage.soundInfo['se_ガトリング音']) {
toLoadSe['se_ガトリング音'] = this.utage.soundInfo['se_ガトリング音'];
} else {
console.log(`Failed to get attacshot SE: se_ガトリング音`);
}
break;
case "attacslash01":
case "attacslash02":
case "attacslash03":
case "attacslash04":
case "attacslash05":
if(this.utage.soundInfo['se_斬撃音']) {
toLoadSe['se_斬撃音'] = this.utage.soundInfo['se_斬撃音'];
} else {
console.log(`Failed to get attacslash SE: se_斬撃音`);
}
break;
case "attaclaser01":
if(this.utage.soundInfo['se_レーザーが放たれる音']) {
toLoadSe['se_レーザーが放たれる音'] = this.utage.soundInfo['se_レーザーが放たれる音'];
} else {
console.log(`Failed to get attaclaser SE: se_レーザーが放たれる音`);
}
break;
default:
break;
}
} catch (error) {
console.log(error);
continue;
}
}
let audioArray = [];
for(let s of Object.keys(toLoadBgm)) {
audioArray.push(toLoadBgm[s]);
}
for(let s of Object.keys(toLoadSe)) {
audioArray.push(toLoadSe[s]);
}
this.audio.loadSounds(audioArray, (percent) => {
this.onAudioProgress(percent);
});
//Manually load white bg for fading. Can be tinted to change color.
this.loader.add('bg|whiteFade', `${this.utage.rootDirectory}Images/white.png`);
this.loader
.on("progress", (loader, resource) => {
this.onPixiProgress(loader, resource);
})
.load(() => {
this.onPixiLoad(resolve, reject);
});
});
}
//Creates all the pixi containers for ther layers defined in layers.tsv
//also creates containers for fading
//note containers render in the order they are added, eg. the last added is always on top
buildLayerContainers() {
let layersToAdd = [];
for(let l of Object.keys(this.utage.layerInfo)) {
layersToAdd.push(this.utage.layerInfo[l]);
}
layersToAdd.push({LayerName: "bg|loopeffect", Type: "Bg", X: 0, Y: 0, Order: 1})
layersToAdd.sort(compare);
let parentContainer = new PIXI.Container();
parentContainer.position.set(this.center.x, this.center.y);
parentContainer.pivot.set(this.center.x, this.center.y);
this.pixi.app.stage.addChild(parentContainer);
this.layers["bg|mainparent"] = { container: parentContainer };
for(let l of layersToAdd) {
this.layers[l.LayerName] = { info: l };
let cont = new PIXI.Container();
this.layers[l.LayerName].container = cont;
parentContainer.addChild(cont);
//center the position of the container then offset it by the value in the layer info
let x = (((baseDimensions.width / 2) + Number(l.X)) * this.resolutionScale);
let y = (((baseDimensions.height / 2) - Number(l.Y)) * this.resolutionScale);
cont.position.set(x, y);
cont.visible = false;
}
let fadeBackCon = new PIXI.Container();
let fadeBackSp = new PIXI.Sprite(this.loader.resources["bg|whiteFade"].texture);
fadeBackSp.height = baseDimensions.height * this.resolutionScale;
fadeBackSp.width = baseDimensions.width * this.resolutionScale;
this.layers["bg|whiteFade"] = { info: undefined, sprite: fadeBackSp, container: fadeBackCon };
fadeBackSp.alpha = 0;
fadeBackCon.addChild(fadeBackSp);
this.pixi.app.stage.addChild(fadeBackCon);
function compare(a, b) {
return a.Order - b.Order;
}
}
buildShaders() {
this.shaders.buildShaders();
}
//Laoding progress functions
onPixiProgress(loader, resource) {
this.assetLoadPercent = loader.progress;
this.text.titleText(true, `Loading Assets... ${loader.progress.toFixed(0)}%`);
this.onLoadProgressUpdate();
}
onAudioProgress(percent) {
this.audioLoadPercent = percent;
this.onLoadProgressUpdate();
}
onLoadProgressUpdate() {
let totalProgress = (this.audioLoadPercent / 2) + (this.assetLoadPercent / 2);
this.text.titleText(true, `Loading Assets... ${totalProgress.toFixed(0)}%`);
}
onPixiLoad(resolve, reject) {
if(this.audioLoadPercent === 100) {
this.text.titleText(false, '');
setTimeout(() => {
this.runEvent = true;
this.buildLayerContainers();
this.buildShaders();
resolve();
}, 1000);
} else {
setTimeout(() => {
this.onPixiLoad(resolve, reject);
}, 100);
}
}
//Runs every frame. Delta is a float scaled from 1=60fps.
onPixiTick(delta) {
try {
if(!this.runEvent) { return; }
let deltaTime = (1000/this.baseFps)*delta;
this.secondTicker -= deltaTime;
if(isNaN(this.waitTime)) {
this.waitTime = 0;
console.log('WaitTime was NaN');
}
if(this.waitTime >= 0) {
this.waitTime -= deltaTime;
}
//Lerp items
this.loopHandleLerps(deltaTime);
//If wait time has expired and we arent waiting for user input end the current command
if(this.currentCommand && this.waitTime <= 0 && !this.manualNext) {
this.processEndCommand(delta);
}
//If we cleared the command get the next
if(!this.currentCommand) {
this.getNextCommand();
}
//If we have a new command set up the info for it
if(this.currentCommand && !this.manualNext && this.waitTime <= 0) {
this.processCommand(delta);
}
if(this.secondTicker < 0) {
this.secondTicker = 1000;
}
} catch (error) {
console.log(error);
}
}
//This loop is run every frame and handles all the animation liek tweens in the mission.
//interpolation is not just linear even if its called HandleLerps
loopHandleLerps(deltaTime) {
try {
//Loop through the lerp targets, modify the needed objects. If a object is at its 1 time state remove it from the list.
let toRemove = [];
for(let i = 0; i < this.lerpTargets.length; ++i) {
try {
let l = this.lerpTargets[i];
if(l.cancel) {
toRemove.push(i);
} else {
l.curTime += deltaTime;
if(l.curTime < 0) { continue; }
let inter = l.inter || "linear";
let pos = l.curTime / l.time;
if(l.cancel) {
pos = 1;
}
if(pos >= 1) {
pos = 1;
toRemove.push(i);
let postRes = postLerpAction(l)
if(postRes === "continue") {
continue;
} else if(postRes === "break") {
false;
}
}
switch(l.type) {
case "shake": {
if(pos === 1) {
if(l.object instanceof HTMLElement) {
l.object.style = "";
} else {
l.object.position.set(l.initV.x, l.initV.y);
}
} else {
let x = l.initV.x;
let y = l.initV.y;
if(l.finalV.x) {
x = Math.floor(Math.random() * (l.finalV.x * (1-pos)));
}
if(l.finalV.y) {
y = Math.floor(Math.random() * (l.finalV.y * (1-pos)));
}
if(l.object instanceof HTMLElement) {
l.object.style = `transform: translate(${x}px, ${y}px);`;
} else {
l.object.position.set(l.initV.x + x, l.initV.y + y);
}
}
break;
}
case "shader": {
try {
l.object.filters[0].uniforms.time = pos;
l.object.filters[0].apply();
} catch(error) { }
break;
}
case "tint": {
let lRgb = commonFunctions.hexToRgb(l.initV);
let fRgb = commonFunctions.hexToRgb(l.finalV);
let newR = commonFunctions.lerp(lRgb[0], fRgb[0], pos, inter);
let newG = commonFunctions.lerp(lRgb[1], fRgb[1], pos, inter);
let newB = commonFunctions.lerp(lRgb[2], fRgb[2], pos, inter);
let hexValue = commonFunctions.rgbToHex([newR, newG, newB]);
let newValue = commonFunctions.getColorFromName(hexValue).color;
l.object.tint = newValue;
break;
}
default: {
let newValue = commonFunctions.lerp(l.initV, l.finalV, pos, inter);
let split = l.type.split(".");
switch(split.length) {
case 1:
l.object[split[0]] = newValue;
break;
case 2:
l.object[split[0]][split[1]] = newValue;
break;
default:
continue;
}
break;
}
}
}
} catch(error) {
//If we got an exception during this it probably means the object doesnt exist anymore so just remove it.
toRemove.push(i);
postLerpAction(this.lerpTargets[i]);
}
}
for(let i = toRemove.length - 1; i > -1; --i) {
this.lerpTargets.splice(toRemove[i], 1);
}
} catch (error) {
console.log(error);
}
function postLerpAction(postLerp) {
try {
if(!postLerp || !postLerp.object || !postLerp.post) {
return '';
}
let split = postLerp.post.split('|');
switch(split[0].toLowerCase()) {
case "destroy":
postLerp.object.destroy({children: true});
return 'continue';
case "clearshader":
postLerp.object.filters = null;
postLerp.object.alpha = Number(split[1]);
return 'break';
}
} catch(error) { }
}
}
//Processes a line from the mission tsv
processCommand(delta) {
try {
let cur = this.currentCommand;
//No seriously this is a thing they did
if(cur.Arg2 === 'Off>') {
cur.Arg2 = '<off>';
}
if ((cur.Command || "").toLowerCase().includes('scenetitle')) {
cur.Command = 'scenetitle';
}
switch((cur.Command || "").toLowerCase()) {
case "scenetitle": {
this.waitTime = this.titleWaitTime * 1000;
try {
let container = this.layers[this.bgLayerName].container;
let sprite = new PIXI.Sprite(this.loader.resources[`bg|titlecard`].texture);
container.visible = true;
sprite.scale.set(1.30273438, 1.30273438);
sprite.anchor.set(0.5, 0.5);
sprite.alpha = 0;
container.addChild(sprite);
this.lerpTargets.push({type: 'alpha', object: sprite, curTime: 0, time: 300, finalV: 1, initV: 0});
this.lerpTargets.push({type: 'alpha', object: sprite, curTime: -(this.waitTime+500), time: 300, finalV: 0, initV: 1, post: "destroy"});
} catch (error) { }
let text = cur.English ? (this.utage.translations[cur.English] || cur.Text) : cur.Text;
this.text.titleText(true, text);
break;
}
case "divaeffect": {
this.waitTime = Number(cur.Arg5) * 1000;
let text = cur.English ? (this.utage.translations[cur.English] || cur.Text) : cur.Text;
this.text.divaText(true, text);
break;
}
//FadeTo
case "continue01":
case "fadeout": {
if(cur.Command.toLowerCase() === "continue01") {
cur.Arg1 = 'black';
cur.Arg6 = 1;
}
this.text.dialogText(false, "");
this.text.characterName(false, "");
this.waitTime = Number(cur.Arg6) * 1000;
if(!cur.Arg1)
cur.Arg1 = 'white';
let fadeColor = commonFunctions.getColorFromName(cur.Arg1);
this.layers["bg|whiteFade"].sprite.tint = fadeColor.color;
this.lerpTargets.push({type: 'alpha', object: this.layers["bg|whiteFade"].sprite, curTime: 0, time: this.waitTime, finalV: fadeColor.alpha, initV: 0});
break;
}
//FadeFrom
case "fadein": {
this.waitTime = Number(cur.Arg6) * 1000;
if(!cur.Arg1)
cur.Arg1 = 'white';
let fadeColor = commonFunctions.getColorFromName(cur.Arg1);
this.layers["bg|whiteFade"].sprite.tint = fadeColor.color;
this.lerpTargets.push({type: 'alpha', object: this.layers["bg|whiteFade"].sprite, curTime: 0, time: this.waitTime, finalV: 0, initV: fadeColor.alpha});
break;
}
case "divalefttorightblackfade": {
this.processDivaFadeHor(cur, false, false);
break;
}
case "divalefttorightclearfade": {
this.processDivaFadeHor(cur, true, false);
break;
}
case "divarighttoleftblackfade": {
this.processDivaFadeHor(cur, false, true);
break;
}
case "divarighttoleftclearfade": {
this.processDivaFadeHor(cur, true, true);
break;
}
case "divauptodownblackfade": {
this.processDivaFadeVert(cur, false, true);
break;
}
case "divauptodownclearfade": {
this.processDivaFadeVert(cur, true, true);
break;
}
case "divadowntoupblackfade": {
this.processDivaFadeVert(cur, false, false);
break;
}
case "divadowntoupclearfade": {
this.processDivaFadeVert(cur, true, false);
break;
}
case "divasepiacamera": { //103500311
let filter = this.shaders.shaders['sepia'];
filter.uniforms.factor = 0.5;
this.layers["bg|mainparent"].container.filters = [filter];
break;
}
case "divasepiacameraclear": {
this.layers["bg|mainparent"].container.filters = null;
break;
}
case "bg": {
let bgInfo = this.utage.textureInfo[cur.Arg1];
let container = this.layers[this.bgLayerName].container;
//If we have a fadetime we need to keep the old bg, fade its alpha out, then destroy it.
if(cur.Arg6 && container.children[0]) {
//I know im assuming a lot that there is already only one bg on the layer.
this.lerpTargets.push({type: 'alpha', object: container.children[0], curTime: 0, time: (Number(cur.Arg6) * 1000), finalV: 0, initV: 1, post: "destroy"});
} else {
//clear the sprite for the bg currently in use.
for(let i = 0; i < container.children.length; ++i) {
container.children[i].destroy({children: true});
}
}
container.visible = true;
let sprite = new PIXI.Sprite(this.loader.resources[`bg|${cur.Arg1}`].texture);
sprite.scale.set(Number(bgInfo.Scale), Number(bgInfo.Scale));
//center the bg
sprite.anchor.set(0.5, 0.5);
if(cur.Arg6) {
container.addChild(sprite);
sprite.alpha = 0;
this.lerpTargets.push({type: 'alpha', object: sprite, curTime: 0, time: (Number(cur.Arg6) * 1000), finalV: 1, initV: 0});
} else {
container.addChild(sprite);
}
break;
}
case "wait":
this.waitTime = Number(cur.Arg6) * 1000;
break;
case "waitinput": {
let time = Number(cur.Arg6);
if(time) {
this.waitTime = time * 1000;
} else {
this.manualNext = true;
}
break;
}
case "movecamera": {
let time = Number(cur.Arg4);
let scale = 1 + (1 - Number(cur.Arg3));
let cont = this.layers["bg|mainparent"].container;
let x = this.center.x + -(Number(cur.Arg1));
//y in xdu is flipped
let y = this.center.y - -(Number(cur.Arg2));
if(time) {
this.waitTime = time * 1000;
if(cont.scale.x !== scale) {
this.cancelLerpOfType('scale.x', cont);
this.lerpTargets.push({type: 'scale.x', object: cont, curTime: 0,
time: this.waitTime, finalV: scale, initV: cont.scale.x, inter: 'quadout' });
}
if(cont.scale.y !== scale) {
this.cancelLerpOfType('scale.y', cont);
this.lerpTargets.push({type: 'scale.y', object: cont, curTime: 0,
time: this.waitTime, finalV: scale, initV: cont.scale.y, inter: 'quadout' });
}
if(cont.position.x !== x) {
this.cancelLerpOfType('position.x', cont);
this.lerpTargets.push({type: 'position.x', object: cont, curTime: 0,
time: this.waitTime, finalV: x, initV: cont.position.x, inter: 'quadout' });
}
if(cont.position.y !== y) {
this.cancelLerpOfType('position.y', cont);
this.lerpTargets.push({type: 'position.y', object: cont, curTime: 0,
time: this.waitTime, finalV: y, initV: cont.position.y, inter: 'quadout' });
}
if(cur.Arg6 && cur.Arg6.toLowerCase() === "nowait") {
this.waitTime = 0;
}
} else {
this.cancelLerpOfType('scale.x', cont);
this.cancelLerpOfType('scale.y', cont);
this.cancelLerpOfType('position.x', cont);
this.cancelLerpOfType('position.y', cont);
cont.scale.set(scale, scale);
cont.position.set(x, y);
}
break;
}
case "characteroff": {
if(cur.Text) {
this.checkPutText(cur);
} else {
this.text.dialogText(false, "");
this.text.characterName(false, "");
}
for(let c of Object.keys(this.currentCharacters)) {
if(!this.currentCharacters[c]) { continue; }
let curChar = this.currentCharacters[c];
if(curChar.charName === cur.Arg1) {
let time = Number(cur.Arg6) * 1000;
if(!time) { time = 250; }
this.lerpTargets.push({type: 'alpha', object: curChar.sprite, name: cur.Arg1, curTime: 0, time: time, finalV: 0, initV: 1, post: "destroy" });
this.currentCharacters[c] = undefined;
break;
}
}
break;
}
case "tween":
this.processTween(delta, cur);
break;
case "bgm":
this.audio.stopSound('bgm');
if(!this.utage.soundInfo[cur.Arg1]) {
break;
}
this.audio.playSound(cur.Arg1, 'bgm');
break;
case "stopbgm":
this.audio.stopSound('bgm');
break;
case "se":
if(!this.utage.soundInfo[cur.Arg1]) {
break;
}
this.audio.playSound(cur.Arg1, 'se');
break;
case "shake": {
this.processShake(delta, cur);
break;
}
case "loopeffect01": {
let container = this.layers["bg|loopeffect"].container;
switch((cur.Arg1 || "").toLowerCase()) {
case "underwater01": {
this.audio.playSound("se_斬撃音_単体", "Se");
let sprite = new PIXI.Sprite(this.loader.resources['bg|underwater01'].texture);
sprite.scale.set(1.30273438, 1.30273438);
sprite.anchor.set(0.5, 0.5);
sprite.alpha = 0;
container.addChild(sprite);
this.lerpTargets.push({type: 'alpha', object: sprite, curTime: 0, time: 1000, finalV: 1, initV: 0});
}
}
container.visible = true;
break;
}
case "divaclearloopdecorate": {
let container = this.layers["bg|loopeffect"].container;
this.lerpTargets.push({type: 'alpha', object: container.children[0], curTime: 0, time: 1000, finalV: 0, initV: 1, post: "destroy"});
break;
}
default:
this.processCommandOther(delta);
break;
//custom effects
case "henshin01_bgmoff": //101000111
this.waitTime = 3850;
this.audio.stopSound('bgm');
this.checkPutCharacterScreen(cur, true);
this.audio.playSound('se_変身演出', 'Se');
break;
case "henshin01":
this.waitTime = 3850;
this.audio.stopSound('bgm');
if(this.utage.soundInfo[cur.Arg4]) {
this.audio.playSound(cur.Arg4, 'bgm');
}
this.audio.playSound('se_変身演出', 'Se');
cur.Arg4 = 0;
this.checkPutCharacterScreen(cur, true);
break;
case "attachit02": //103500221
this.waitTime = 850;
this.audio.playSound('se_打撃音', 'Se');
break;
case "attachit03": //312000112
this.waitTime = 850;
this.audio.playSound('se_打撃音', 'Se');
break;
case "attacshot02":
this.waitTime = 1500;
this.audio.playSound('se_銃撃単発', 'Se');
break;
case "attacshot11": //103500251
this.waitTime = 1680;
this.audio.playSound('se_ガトリング音', 'Se');
break;
case "attacshot12": //103500231
this.waitTime = 1680;
this.audio.playSound('se_ガトリング音', 'Se');
break;
case "attacslash01": //103500642
this.waitTime = 870;
this.audio.playSound('se_斬撃音', 'Se');
break;
case "attacslash02": //103500231
this.waitTime = 870;
this.audio.playSound('se_斬撃音', 'Se');
break;
case "attacslash03": //312000112
this.waitTime = 870;
this.audio.playSound('se_斬撃音', 'Se');
break;
case "attacslash04": //312000142
this.waitTime = 870;
this.audio.playSound('se_斬撃音', 'Se');
break;
case "attacslash05": //103500552
this.waitTime = 870;
this.audio.playSound('se_斬撃音', 'Se');
break;
case "attaclaser01": //312000222
this.waitTime = 2360;
this.audio.playSound('se_レーザーが放たれる音', 'Se');
break;
case "getitem01": //103400252
break;
case "skillmovie": //103500341
break;
case "unhappyseed_appearance01": { //312000112
this.audio.stopSound('Se_不幸のオーラ(ヴォォオンン)');
this.audio.playSound('Se_不幸のオーラ(ヴォォオンン)', 'Se');
this.waitTime = 2000;
let customCommand = { Command: "", Arg1: cur.Arg1, Arg2: this.defaultCharPattern, Arg3: 'キャラ中央', Arg6: .5 };
this.checkPutCharacterScreen(customCommand, false);
break;
}
case "unhappyseed_appearance02": //312000111
case "arcanoise_appearance02": { //103500341
let spawnTime = 0.5;
if(cur.Command.toLowerCase().includes("unhappyseed_appearance")) {
this.audio.stopSound('Se_不幸のオーラ(ヴォォオンン)');
this.audio.playSound('Se_不幸のオーラ(ヴォォオンン)', 'Se');
this.waitTime = 3000;
spawnTime = 2.5;
} else {
this.waitTime = 1000;
}
if(cur.Arg1 && cur.Arg2) {
let customCommand1 = { Command: "", Arg1: cur.Arg1, Arg2: this.defaultCharPattern, Arg3: 'キャラ右', Arg6: spawnTime };
this.checkPutCharacterScreen(customCommand1, false);
let customCommand2 = { Command: "", Arg1: cur.Arg2, Arg2: this.defaultCharPattern, Arg3: 'キャラ左', Arg6: spawnTime };
this.checkPutCharacterScreen(customCommand2, false);
}
break;
}
case "arcanoise_appearance03": { //103500521
if(cur.Arg1 && cur.Arg2 && cur.Arg3) {
this.waitTime = 1000;
let customCommand1 = { Command: "", Arg1: cur.Arg1, Arg2: this.defaultCharPattern, Arg3: 'キャラ右', Arg6: .5 };
this.checkPutCharacterScreen(customCommand1, false);
let customCommand2 = { Command: "", Arg1: cur.Arg2, Arg2: this.defaultCharPattern, Arg3: 'キャラ左', Arg6: .5 };
this.checkPutCharacterScreen(customCommand2, false);
let customCommand3 = { Command: "", Arg1: cur.Arg3, Arg2: this.defaultCharPattern, Arg3: 'キャラ中央', Arg6: .5 };
this.checkPutCharacterScreen(customCommand3, false);
}
break;
}
case "noise_disappearance01": //103500331
this.waitTime = Number(cur.Arg1) * 1000;
break;
case "noise_disappearance02": { //103500341
this.waitTime = Number(cur.Arg1) * 1000;
//let c1 = this.currentCharacters['キャラ右'];
//if(c1) {
// this.lerpTargets.push({type: 'alpha', object: c1.sprite, curTime: 0, time: 200, finalV: 0, initV: 1, post: "destroy" });
// this.currentCharacters['キャラ右'] = undefined;
//}
//let c2 = this.currentCharacters['キャラ左'];
//if(c2) {
// this.lerpTargets.push({type: 'alpha', object: c2.sprite, curTime: 0, time: 200, finalV: 0, initV: 1, post: "destroy" });
// this.currentCharacters['キャラ左'] = undefined;
//}
break;
}
case "noise_disappearance03": { //103500552
this.waitTime = Number(cur.Arg1) * 1000;
let c1 = this.currentCharacters['キャラ右'] || this.currentCharacters['キャラ右02'];
if(c1) {
this.lerpTargets.push({type: 'alpha', object: c1.sprite, curTime: (0 - (this.waitTime/2)), time: 200, finalV: 0, initV: 1, post: "destroy" });
this.currentCharacters['キャラ右'] = undefined;
}
let c2 = this.currentCharacters['キャラ左'] || this.currentCharacters['キャラ左02'];
if(c2) {
this.lerpTargets.push({type: 'alpha', object: c2.sprite, curTime: (0 - (this.waitTime/2)), time: 200, finalV: 0, initV: 1, post: "destroy" });
this.currentCharacters['キャラ左'] = undefined;
}
let c3 = this.currentCharacters['キャラ中央'];
if(c3) {
this.lerpTargets.push({type: 'alpha', object: c3.sprite, curTime: (0 - (this.waitTime/2)), time: 200, finalV: 0, initV: 1, post: "destroy" });
this.currentCharacters['キャラ中央'] = undefined;
}
break;
}
case "noise_disappearance11": //103500341
this.waitTime = Number(cur.Arg1) * 1000;
break;
case "enemy_disappearance01": //312000112
case "enemy_disappearance02": //312000111
case "enemy_disappearance03": //312000142
this.processTryRemoveChar(cur.Arg1);
if(cur.Arg2) {
this.processTryRemoveChar(cur.Arg2);
}
if(cur.Arg3) {
this.processTryRemoveChar(cur.Arg3);
}
break;
case "darkaura01": //312000111
this.audio.stopSound('Se_不幸のオーラ(ヴォォオンン)');
this.audio.playSound('Se_不幸のオーラ(ヴォォオンン)', 'Se');
this.waitTime = 2500;
break;
case "somethingnew_appearance01": { //312000111
this.audio.playSound('Se_サムシング・ニューの叫び声(アアア”ア”ア”)', 'Se');
let c = this.currentCharacters['キャラ中央'];
this.waitTime = 4000;
if(c) {
this.lerpTargets.push({type: 'alpha', object: c.sprite, curTime: 0, time: 3000, finalV: 0, initV: 1, post: "destroy" });
}
let customCommand = { Command: "", Arg1: cur.Arg1, Arg2: this.defaultCharPattern, Arg3: 'キャラ中央', Arg6: 3 };
this.checkPutCharacterScreen(customCommand, false, true);
break;
}
}
} catch(error) {
console.log(error);
}
}
processDivaFadeHor(command, clear, rtl) {
this.waitTime = Number(command.Arg6) * 1000;
let sprite = this.layers["bg|whiteFade"].sprite;
let filter = this.shaders.shaders[(rtl ? 'divarighttoleftfade' : 'divalefttorightfade')];
if(!command.Arg1) {
command.Arg1 = 'white';
}
let color = commonFunctions.getColorFromName(command.Arg1);
let rgbcolor = commonFunctions.hexToRgb(color.color);
sprite.tint = color.color;
sprite.alpha = color.alpha;
filter.uniforms.time = 0.0;
filter.uniforms.fadeincolor = (clear ? [0.0,0.0,0.0,0.0] : [rgbcolor[0],rgbcolor[1],rgbcolor[2],1.0]);
filter.uniforms.fadeoutcolor = (clear ? [rgbcolor[0],rgbcolor[1],rgbcolor[2],1.0] : [0.0,0.0,0.0,0.0]);
sprite.filters = [filter];
this.lerpTargets.push({type: 'shader', object: sprite, curTime: 0, time: this.waitTime, post: `clearshader|${(clear ? '0' : `${color.alpha}`)}`});
}
//utd is UpToDown
processDivaFadeVert(command, clear, utd) {
this.waitTime = Number(command.Arg6) * 1000;
let sprite = this.layers["bg|whiteFade"].sprite;
let filter = this.shaders.shaders[(utd ? 'divauptodownfade' : 'divadowntoupfade')];
if(!command.Arg1) {
command.Arg1 = 'white';
}
let color = commonFunctions.getColorFromName(command.Arg1);
let rgbcolor = commonFunctions.hexToRgb(color.color);
sprite.tint = color.color;
sprite.alpha = color.alpha;
filter.uniforms.time = 0.0;
filter.uniforms.fadeincolor = (clear ? [0.0,0.0,0.0,0.0] : [rgbcolor[0],rgbcolor[1],rgbcolor[2],1.0]);
filter.uniforms.fadeoutcolor = (clear ? [rgbcolor[0],rgbcolor[1],rgbcolor[2],1.0] : [0.0,0.0,0.0,0.0]);
sprite.filters = [filter];
this.lerpTargets.push({type: 'shader', object: sprite, curTime: 0, time: this.waitTime, post: `clearshader|${(clear ? '0' : `${color.alpha}`)}`});
}
//This should mostly be handling things like text
processCommandOther(delta) {
let cur = this.currentCommand;
//Character on screen
this.checkPutCharacterScreen(cur);
//Display text
this.checkPutText(cur);
}
//Checks if the current command is trying to put a new character on the screen or not.
//special is used if the command actually has a command param such as Henshin since normal text commands dont.
//ignoreCurrent is used for if you are remvoing a current character yourself and don't want this to add another fade out to it such as somethingnew_appearance01.
checkPutCharacterScreen(cur, special = false, ignoreCurrent = false,) {
let charToLoad = cur.Character || cur.Arg1;
if((!cur.Command || special) && charToLoad && this.utage.characterInfo[charToLoad]) {
let lay = undefined;
let curChar = undefined; //The character that is currently on screen with the same name as Arg1.
let prevChar = undefined; //The character that is already on the layer we are trying to put the new char on.
//First check if the character is already on screen
if(!ignoreCurrent) {
for(let c of Object.keys(this.currentCharacters)) {
if(!this.currentCharacters[c]) { continue; }
if(this.currentCharacters[c].charName === charToLoad) {
curChar = this.currentCharacters[c];
lay = this.currentCharacters[c].layer;
if(!cur.Arg3) {
cur.Arg3 = c;
}
}
}
}
//Sometimes they don't give a pattern so just assume the default.
if(!cur.Arg2 && !curChar) {
cur.Arg2 = this.defaultCharPattern;
//If the character was already on screen use that pattern
} else if (!cur.Arg2 && curChar) {
cur.Arg2 = curChar.character.Pattern;
}
let chr = this.utage.characterInfo[charToLoad][cur.Arg2];
if(!chr) {
//Non character sprites don't have a pattern and just use none as a key so if we don't find a character at arg1|arg2 look for this.
cur.Arg2 = 'none';
chr = this.utage.characterInfo[charToLoad][cur.Arg2];
}
//If we didn't find the character at all just abandon.
if(!chr) { return; }
//If the script gives us a layer get that layer and if there is a character on it already.
if(cur.Arg3) {
lay = this.layers[cur.Arg3];
prevChar = this.currentCharacters[cur.Arg3];
if(!lay) { return; }
//If they didn't give us a layer check for left and right positioning or try to go to default (middle)
} else if(!curChar && !lay) {
if(isCharOnLeft.apply(this)) {
lay = this.layers['キャラ右'];
} else if(isCharOnRight.apply(this)) {
lay = this.layers['キャラ左'];
} else {
lay = this.layers['キャラ中央'];
}
if(!lay) { return; }
cur.Arg3 = lay.info.LayerName;
prevChar = this.currentCharacters[cur.Arg3];
}
//If the character is already on screen on another layer move them to the new layer.
if(curChar && lay && curChar.layer.info.LayerName !== cur.Arg3) {
this.lerpTargets.push({type: 'alpha', object: curChar.sprite, curTime: 0, time: 200, finalV: 0, initV: 1, post: "destroy" });
this.currentCharacters[curChar.layer.info.LayerName] = undefined;
}
//If this character is already here and not changing patterns don't change anything.
else if(curChar && curChar.charName === charToLoad && curChar.character.Pattern === cur.Arg2) {
return;
}
if(!ignoreCurrent) {
//If the layer already has a different character on it remove it.
if(prevChar && (prevChar.charName !== charToLoad || prevChar.character.Pattern !== chr.Pattern)) {
this.lerpTargets.push({type: 'alpha', object: prevChar.sprite, curTime: 0, time: 200, finalV: 0, initV: 1, post: "destroy" });
this.currentCharacters[cur.Arg3] = undefined;
}
}
let sprite = new PIXI.Sprite(this.loader.resources[`char|${charToLoad}|${cur.Arg2}`].texture);
sprite.scale.set(Number(chr.Scale), Number(chr.Scale));
let anchor = commonFunctions.getAnchorFromCharPivot(chr.Pivot);
sprite.anchor.set(anchor.x, anchor.y);
sprite.alpha = 0;
let fadeTime = 200;
//If the character is already on screen put the new sprite in the same position as the old one.
if(curChar && curChar.layer.info.LayerName === cur.Arg3) {
sprite.position.x = curChar.sprite.position.x;
sprite.position.y = curChar.sprite.position.y;
//if the current character is doing a tween transfer the tween to the new one.
for(let l of this.lerpTargets) {
if(l.type.includes('position') && l.object === curChar.sprite) {
l.object = sprite;
break;
}
}
}
if(cur.Arg4) {
sprite.position.x = Number(cur.Arg4);
}
if(cur.Arg5) {
sprite.position.y = Number(cur.Arg5);
}
if(cur.Arg6) {
//Im halving this because their fades take too fucking long and looks bad.
fadeTime = (Number(cur.Arg6) * 1000) / 2;
}
this.currentCharacters[cur.Arg3] = { layer: lay, character: chr, charName: charToLoad, sprite: sprite };
if(fadeTime > 0) {
this.lerpTargets.push({type: 'alpha', object: sprite, curTime: 0, time: fadeTime, finalV: 1, initV: 0 });
} else {
sprite.alpha = 1;
}
lay.container.addChild(sprite);
lay.container.visible = true;
}
function isCharOnLeft() {
for(let l of Object.keys(this.layers)) {
let lay = this.layers[l].info;
if(!lay) { continue; }
if(lay.LayerName.includes('キャラ左')) {
if(this.currentCharacters[lay.LayerName]) {
return true;
}
}
}
return false;
}
function isCharOnRight() {
for(let l of Object.keys(this.layers)) {
let lay = this.layers[l].info;
if(!lay) { continue; }
if(lay.LayerName.includes('キャラ右')) {
if(this.currentCharacters[lay.LayerName]) {
return true;
}
}
}
return false;
}
}
processTryRemoveChar(character) {
let curChar = undefined;
for(let c of Object.keys(this.currentCharacters)) {
if(!this.currentCharacters[c]) { continue; }
if(this.currentCharacters[c].charName === character) {
curChar = this.currentCharacters[c];
}
}
if(!curChar) {
return;
}
this.lerpTargets.push({type: 'alpha', object: curChar.sprite, curTime: 0, time: 500, finalV: 0, initV: 1, post: "destroy" });
this.currentCharacters[curChar.layer.info.LayerName] = undefined;
}
//Checks if the current command is trying to put text on the screen.
checkPutText(cur) {
if(this.playingVoice) {
this.audio.stopSound(this.playingVoice);
}
if(!cur.Command && cur.Arg1 && cur.Text) {
//If its chracter off screen text
let text = cur.English ? (this.utage.translations[cur.English] || cur.Text) : cur.Text;
text = commonFunctions.convertUtageTextTags(text);
if(cur.Arg2 && cur.Arg2.toLowerCase() === "<off>") {
this.text.characterName(true, this.utage.charTranslations[cur.Arg1] || cur.Arg1);
this.text.dialogText(true, commonFunctions.convertUtageTextTags(text));
} else {
let found = false;
//Look for the character that is saying the text to get their name
//future note: This might be better to just look for the character in character info if this start failing.
for(let c of Object.keys(this.currentCharacters)) {
if(!this.currentCharacters[c]) { continue; }
if(this.currentCharacters[c].charName === cur.Arg1 || this.currentCharacters[c].charName === cur.Character) {
let nameToUse = this.currentCharacters[c].character.NameText;
//If cur.Character is set that means the line had a <character= included so we want to use the name from arg1 instead.
if(cur.Character) {
nameToUse = cur.Arg1;
}
this.text.characterName(true, this.utage.charTranslations[nameToUse] || nameToUse);
this.text.dialogText(true, text);
this.currentCharacters[c].sprite.tint = 0xFFFFFF;
found = true;
continue;
}
//while were here set other characters tint to background shade
if(this.currentCharacters[c].sprite) {
this.currentCharacters[c].sprite.tint = this.backCharTint;
}
}
//If we didnt find the character just dump the text anyways with Arg1 as the name
if(!found) {
this.text.characterName(true, this.utage.charTranslations[cur.Arg1] || cur.Arg1);
this.text.dialogText(true, text);
}
}
this.manualNext = true;
//Sometimes they don't give a Arg1 for the text.
} else if(!cur.Command && cur.Arg2.toLowerCase() === "<off>" && cur.Text) {
let text = cur.English ? (this.utage.translations[cur.English] || cur.Text) : cur.Text;
this.text.characterName(true, "");
this.text.dialogText(true, commonFunctions.convertUtageTextTags(text));
this.manualNext = true;
}
if(cur.Voice) {
this.playingVoice = cur.Voice;
this.audio.playSound(cur.Voice);
}
}
//Handle a tween command
processTween(delta, cur) {
this.text.dialogText(false, "");
this.text.characterName(false, "");
let curChar = undefined;
//Find the character for the tween.
for(let c of Object.keys(this.currentCharacters)) {
if(!this.currentCharacters[c]) { continue; }
if(this.currentCharacters[c].charName === cur.Arg1) {
curChar = this.currentCharacters[c];
this.currentCharacters[c].sprite.tint = 0xFFFFFF;
continue;
}
//while were here set other characters tint to background shade
if(this.currentCharacters[c].sprite) {
this.currentCharacters[c].sprite.tint = this.backCharTint;
}
}
if(!curChar) { return; }
switch(cur.Arg2.toLowerCase()) {
case "moveto": {
let props = commonFunctions.getPropertiesFromTweenCommand(cur.Arg3);
//moveto has a islocal value that im just assuming is true until I run into a case it actually isint.
//note that islocal is local to the layer's position not the characters current position so the final pos will be 0 + what the command says
if(!cur.Arg6 || cur.Arg6 !== "NoWait") {
this.waitTime = props.time + (props.delay || 0);
}
if(props.x != undefined) {
if(props.time) {
this.lerpTargets.push({type: 'position.x', object: curChar.sprite, curTime: 0 - (props.delay || 0), time: props.time,
finalV: props.x, initV: curChar.sprite.position.x, inter: 'quadout' });
} else {
curChar.sprite.position.x = props.x;
}
}
if(props.y != undefined) {
if(props.time) {
this.lerpTargets.push({type: 'position.y', object: curChar.sprite, curTime: 0 - (props.delay || 0), time: props.time,
finalV: props.y, initV: curChar.sprite.position.y, inter: 'quadout' });
} else {
curChar.sprite.position.y = props.y;
}
}
break;
}
case "punchposition": {
let props = commonFunctions.getPropertiesFromTweenCommand(cur.Arg3);
if(props.time == undefined) { props.time = 1000; }
if(!cur.Arg6 || cur.Arg6 !== "NoWait") {
this.waitTime = props.time + (props.delay || 0);
}
if(props.x != undefined) {
this.lerpTargets.push({type: 'position.x', object: curChar.sprite, curTime: 0 - (props.delay || 0), time: props.time,
finalV: curChar.sprite.position.x + props.x, initV: curChar.sprite.position.x, inter: 'punch' });
}
if(props.y != undefined) {
this.lerpTargets.push({type: 'position.y', object: curChar.sprite, curTime: 0 - (props.delay || 0), time: props.time,
finalV: curChar.sprite.position.y + props.y, initV: curChar.sprite.position.y, inter: 'punch' });
}
break;
}
case "scaleto": {
let props = commonFunctions.getPropertiesFromTweenCommand(cur.Arg3, false);
if(props.time == undefined) { props.time = 500; }
//cuz I don't care about their values that make no sense when everything else uses time.
if(props.speed) { props.time = props.speed * 1000; }
if(!cur.Arg6 || cur.Arg6 !== "NoWait") {
this.waitTime = props.time + (props.delay || 0);
}
if(props.x != undefined) {
this.lerpTargets.push({type: 'scale.x', object: curChar.sprite, curTime: 0 - (props.delay || 0), time: props.time,
finalV: curChar.sprite.scale.x * props.x, initV: curChar.sprite.scale.x });
}
if(props.y != undefined) {
this.lerpTargets.push({type: 'scale.y', object: curChar.sprite, curTime: 0 - (props.delay || 0), time: props.time,
finalV: curChar.sprite.scale.y * props.y, initV: curChar.sprite.scale.y });
}
break;
}
case "colorto": {
let props = commonFunctions.getPropertiesFromTweenCommand(cur.Arg3);
if(props.alpha != undefined) {
this.cancelLerpOfType('alpha', curChar.sprite);
if(props.time) {
this.lerpTargets.push({type: 'alpha', object: curChar.sprite, curTime: 0 - (props.delay || 0), time: props.time,
finalV: props.alpha, initV: curChar.sprite.alpha });
} else {
curChar.sprite.alpha = props.alpha;
}
}
if(props.color != undefined) {
this.cancelLerpOfType('tint', curChar.sprite);
let color = commonFunctions.getColorFromName(props.color);
if(props.time) {
this.lerpTargets.push({type: 'tint', object: curChar.sprite, curTime: 0 - (props.delay || 0), time: props.time,
finalV: color.color, initV: curChar.sprite.tint });
} else {
curChar.sprite.tint = color.color;
}
}
break;
}
}
}
processShake(delta, cur) {
let obj = cur.Arg1;
switch(obj.toLowerCase()) {
case "camera": {
let props = commonFunctions.getPropertiesFromTweenCommand(cur.Arg3);
if(!props.y) { props.y = 0; }
if(!props.x) { props.x = 0; }
if(!props.x && !props.y) {
props.x = 30;
props.y = 30;
}
if(!props.time) { props.time = 1000; }
//Im not waiting for these because utage seems to not.
//f(!cur.Arg6 || cur.Arg6 !== "NoWait") {
// this.waitTime = props.time + (props.delay || 0);
//
//If the screen is currently shaking the second shake will offset it so get the init
// position from that shake not the current position
let currentShake = undefined;
for(let l of this.lerpTargets) {
if(l.type.includes('shake') && l.object === this.pixi.app.stage) {
currentShake = l;
l.cancel = true;
break;
}
}
let stage = undefined;
if(currentShake) {
stage = currentShake.initV;
} else {
stage = this.pixi.app.stage.position;
}
this.lerpTargets.push({type: 'shake', object: this.pixi.app.stage, curTime: 0 - (props.delay || 0), time: props.time,
finalV: {x: props.x + stage.x, y: props.y + stage.y}, initV: {x: stage.x, y: stage.y} });
break;
}
case "messagewindow": {
let props = commonFunctions.getPropertiesFromTweenCommand(cur.Arg3);
if(!props.y) { props.y = 0; }
if(!props.x) { props.x = 0; }
if(!props.x && !props.y) {
props.x = 30;
props.y = 30;
}
if(!props.time) { props.time = 1000; }
//if(!cur.Arg6 || cur.Arg6 !== "NoWait") {
// this.waitTime = props.time + (props.delay || 0);
//}
this.lerpTargets.push({type: 'shake', object: document.getElementById('dialog-box'), curTime: 0 - (props.delay || 0), time: props.time,
finalV: {x: props.x, y: props.y}, initV: {x: 0, y: 0} });
break;
}
default: {
let props = commonFunctions.getPropertiesFromTweenCommand(cur.Arg3);
if(!props.y) { props.y = 0; }
if(!props.x) { props.x = 0; }
if(!props.x && !props.y) {
props.x = 30;
props.y = 30;
}
if(!props.time) { props.time = 1000; }
let curChar = undefined;
//Find the character.
for(let c of Object.keys(this.currentCharacters)) {
if(!this.currentCharacters[c]) { continue; }
if(this.currentCharacters[c].charName === cur.Arg1) {
curChar = this.currentCharacters[c];
continue;
}
}
if(!curChar) { return; }
//The sprite's position should be added to the final x and y when setting the shake position.
this.lerpTargets.push({type: 'shake', object: curChar.sprite, curTime: 0 - (props.delay || 0), time: props.time,
finalV: {x: props.x, y: props.y}, initV: {x: curChar.sprite.position.x, y: curChar.sprite.position.y} });
}
}
}
cancelLerpOfType(type, object) {
for(let l of this.lerpTargets) {
if(l.type.includes(type) && l.object === object) {
l.cancel = true;
}
}
}
processEndCommand(delta) {
let cur = this.currentCommand;
switch((cur.Command || "").toLowerCase()) {
case "scenetitle":
this.text.titleText(false, '');
break;
case "divaeffect":
this.text.divaText(false, '');
break;
}
this.currentCommand = undefined;
}
onMainClick(event) {
if(!this.runEvent) {
return
}
event.preventDefault();
event.stopPropagation();
if(this.manualNext && !this.uiHidden) {
if (!this.text.scrollingText) {
this.manualNext = false;
this.waitTime = 0;
} else if(this.text.scrollingText) {
this.text.showDialogFullText();
}
} else if(this.uiHidden) {
this.text.hideUi(true);
this.uiHidden = false;
}
}
hideUiClicked(event) {
if(!this.runEvent) {
return;
}
event.preventDefault();
event.stopPropagation();
this.uiHidden = !this.uiHidden;
this.text.hideUi(!this.uiHidden);
}
onEndFile() {
this.resolve();
}
getNextCommand() {
let command = this.utage.currentPlayingFile.pop();
if(!this.utage.currentPlayingFile || this.utage.currentPlayingFile.length === 0) {
this.onEndFile();
return;
}
if(!command || command.comment || (!command.Command && !command.Arg1 && !command.Arg2 && !command.Arg3 && !command.Arg4 && !command.Arg5 && !command.Arg6)) {
this.getNextCommand();
return;
}
this.currentCommand = command;
}
updateResolution(res) {
let newScale = res.height / baseDimensions.height;
//Set the scale by the pixel ratio so pixi makes the stage the proper size
this.pixi.app.stage.scale.set(newScale * window.devicePixelRatio, newScale * window.devicePixelRatio);
//Sizes the canvas/pixi's renderer to the actual render resolution
this.pixi.app.renderer.resize(res.width * window.devicePixelRatio, res.height * window.devicePixelRatio);
//Css size overwrites the display size of the canvas
this.pixi.app.view.style.width = res.width + "px";
this.pixi.app.view.style.height = res.height + "px";
//Transform the text container to be the right scale, browser handles all dpi stuff for html elements itself
document.getElementById('text-container').style.cssText = `transform: scale(${newScale})`;
}
resetAll() {
return new Promise((resolve, reject) => {
try {
this.pixi.app.ticker.remove(this.onPixiTick, this);
this.utage.currentPlayingFile.length = 0;
this.currentCharacters = {};
this.layers = {};
this.currentCommand = undefined;
this.runEvent = false;
this.secondTicker = 1000;
this.waitTime = 0;
this.lerpTargets = [];
this.manualNext = false;
this.hasMoreText = false;
this.audioLoadPercent = 0;
this.assetLoadPercent = 0;
this.playingVoice = undefined;
this.text.resetAll();
this.audio.resetAll();
this.utage.resetTranslations();
this.pixi.app.stage.children.forEach(function(child) { child.destroy({children: true, texture: true, baseTexture: true}); });
for(let tex of Object.keys(PIXI.utils.TextureCache)) {
if(PIXI.utils.TextureCache[tex]) {
PIXI.utils.TextureCache[tex].destroy(true);
}
}
for(let tex of Object.keys(PIXI.utils.BaseTextureCache)) {
if(PIXI.utils.BaseTextureCache[tex]) {
PIXI.utils.BaseTextureCache[tex].destroy(true);
}
}
this.loader.reset();
resolve();
} catch (error) {
reject(error);
}
});
}
}
export { Player };