matrix-engine-wgpu
Version:
Networking implemented - based on kurento openvidu server. fix arcball camera,instanced draws added also effect pipeline blend with instancing option.Normalmap added, Fixed shadows casting vs camera/video texture, webGPU powered pwa application. Crazy fas
469 lines (441 loc) • 21.3 kB
JavaScript
import MatrixEngineWGPU from "../../../src/world.js";
import {Controller} from "./controller.js";
import {HUD} from "./hud.js";
import {MEMapLoader} from "./map-loader.js";
import {Character} from "./character-base.js";
import {EnemiesManager} from "./enemies-manager.js";
import {CollisionSystem} from "../../../src/engine/collision-sub-system.js";
import {LS, mb, SS, urlQuery} from "../../../src/engine/utils.js";
import {MatrixStream} from "../../../src/engine/networking/net.js";
import {byId} from "../../../src/engine/networking/matrix-stream.js";
import {startUpPositions} from "./static.js";
import {MatrixTTS} from "./tts.js";
import {Marketplace} from "./marketplace.js";
import {Inventory} from "./invertoryManager.js";
import {RCSAccount} from "./rocket-crafting-account.js";
import {en} from "../../../public/res/multilang/en-backup.js";
/**
* @description
* This is main root dep file.
* All start from here.
* @Note
* “Character and animation assets from Mixamo,
* used under Adobe’s royalty‑free license.
* Redistribution of raw assets is not permitted.”
**/
if(!SS.has('player') || !LS.has('player')) {
location.href = 'https://maximumroulette.com';
}
let forestOfHollowBlood = new MatrixEngineWGPU({
dontUsePhysics: true,
useSingleRenderPass: true,
canvasSize: 'fullscreen',
mainCameraParams: {
type: 'RPG',
responseCoef: 1000
},
clearColor: {r: 0, b: 0.122, g: 0.122, a: 1}
}, () => {
forestOfHollowBlood.account = new RCSAccount("https://maximumroulette.com");
forestOfHollowBlood.account.createDOM(true);
forestOfHollowBlood.tts = new MatrixTTS();
forestOfHollowBlood.player = {
username: "guest"
};
// Audios
forestOfHollowBlood.matrixSounds.createAudio('music', 'res/audios/rpg/music.mp3', 1);
forestOfHollowBlood.matrixSounds.createAudio('music2', 'res/audios/rpg/wizard-rider.mp3', 1)
forestOfHollowBlood.matrixSounds.createAudio('win1', 'res/audios/rpg/feel.mp3', 2);
forestOfHollowBlood.handleHeroImage = (selectHeroIndex) => {
// func exist in case of changinf hero names...
let name = 'no-name';
if(selectHeroIndex == 0) {
name = 'mariasword';
} else if(selectHeroIndex == 1) {
name = 'slayzer';
} else if(selectHeroIndex == 2) {
name = 'steelborn';
} else if(selectHeroIndex == 3) {
name = 'warrok';
} else if(selectHeroIndex == 4) {
name = 'skeletonz';
} else if(selectHeroIndex == 5) {
name = 'erika';
} else if(selectHeroIndex == 6) {
name = 'arissa';
}
return name;
}
// addEventListener('AmmoReady', async () => {})
forestOfHollowBlood.player.data = SS.get('player');
// ASYNC FOR very small json become big buggy - pragmatic
if(typeof app.label == 'undefined' || typeof app.label.get == 'undefined' || typeof app.label.get.mariasword == 'undefined') {
if(typeof app.label == 'undefined') app.label = {get: {}};
app.label.get = en;
}
forestOfHollowBlood.net = new MatrixStream({
active: true,
domain: 'maximumroulette.com',
port: 2020,
sessionName: 'forestOfHollowBlood-free-for-all',
resolution: '160x240',
isDataOnly: forestOfHollowBlood.player.data.useCameraOrAudio, //(urlQuery.camera || urlQuery.audio ? false : true),
customData: forestOfHollowBlood.player.data
});
forestOfHollowBlood.net.virtualEmiter = null;
forestOfHollowBlood.player.remoteByTeam = {
south: [],
north: []
};
app.matrixSounds.audios.music.loop = true;
addEventListener('net-ready', () => {
byId('join-btn').click();
forestOfHollowBlood.loadEnemyCreeps();
byId('buttonLeaveSession').addEventListener('click', () => {location.assign("rpg-menu.html")});
});
forestOfHollowBlood.loadEnemyCreeps = () => {
if(forestOfHollowBlood.player.data.team == 'south') {
forestOfHollowBlood.player.data.enemyTeam = 'north';
forestOfHollowBlood.enemies = new EnemiesManager(forestOfHollowBlood, 'north');
} else {
forestOfHollowBlood.player.data.enemyTeam = 'south';
forestOfHollowBlood.enemies = new EnemiesManager(forestOfHollowBlood, 'south');
}
}
addEventListener('connectionDestroyed', (e) => {
console.log('connectionDestroyed - end of game.');
/**
* @note
* For now actual is most simple way
* Destroy game session if any player disconnected.
* Later : after adding DB backend account session
* add negative BAN flag for players who leave gameplay.
*/
if(byId('remote-' + e.detail.connectionId)) {
byId('remote-' + e.detail.connectionId).remove();
// byId('waiting-' + e.detail.connectionId).remove();
mb.error(`Player ${e.detail.connectionId} disconnected...`);
let getPlayer = JSON.parse(e.detail.event.connection.data);
let disPlayer = forestOfHollowBlood.getSceneObjectByName(getPlayer.mesh);
mb.error(`Player ${e.detail.connectionId} disconnected..${disPlayer}.`);
// back to base for now
disPlayer.position.setPosition(
startUpPositions[getPlayer.team][0],
startUpPositions[getPlayer.team][1],
startUpPositions[getPlayer.team][2]);
}
setTimeout(() => {
// app.net.closeSession();
app.net.buttonLeaveSession.click();
location.assign("rpg-menu.html");
}, 4000);
});
addEventListener("onConnectionCreated", (e) => {
const remoteCons = Array.from(e.detail.connection.session.remoteConnections.entries());
if(remoteCons.length == 4) {
if(location.hostname.indexOf('localhost') == -1) app.account.gameStarted();
}
const isLocal = e.detail.connection.connectionId == app.net.session.connection.connectionId;
if(e.detail.connection.session.remoteConnections.size == 0) {
if(forestOfHollowBlood.net.virtualEmiter == null && isLocal) {
forestOfHollowBlood.net.virtualEmiter = e.detail.connection.connectionId;
document.title = "VE " + app.net.session.connection.connectionId;
}
}
else {
// If present same team than emitter is active ...
let isSameTeamAlready = false;
for(var x = 0;x < remoteCons.length;x++) {
let currentRemoteConn = JSON.parse(remoteCons[x][1].data);
if(forestOfHollowBlood.player.data.team == currentRemoteConn.team) {
isSameTeamAlready = true;
if(forestOfHollowBlood.player.remoteByTeam[forestOfHollowBlood.player.data.team].indexOf(remoteCons[x][1]) == -1) {
forestOfHollowBlood.player.remoteByTeam[forestOfHollowBlood.player.data.team].push(remoteCons[x][1]);
}
} else {
if(forestOfHollowBlood.player.remoteByTeam[currentRemoteConn.team].indexOf(remoteCons[x][1]) == -1) {
forestOfHollowBlood.player.remoteByTeam[currentRemoteConn.team].push(remoteCons[x][1]);
}
}
}
if(isSameTeamAlready == false && isLocal == true) {
forestOfHollowBlood.net.virtualEmiter = e.detail.connection.connectionId;
document.title = "VE " + app.net.session.connection.connectionId;
}
}
if(e.detail.connection.connectionId == app.net.session.connection.connectionId) {
let newPlayer = document.createElement('div');
newPlayer.innerHTML = `Local Player: ${e.detail.connection.connectionId}`;
newPlayer.id = `local-${e.detail.connection.connectionId}`;
byId('matrix-net').appendChild(newPlayer);
// document.title = forestOfHollowBlood.label.get.titleBan;
// document.title = app.net.session.connection.connectionId;
} else {
let newPlayer = document.createElement('div');
newPlayer.innerHTML = `remote Player: ${e.detail.connection.connectionId}`;
newPlayer.id = `remote-${e.detail.connection.connectionId}`;
byId('matrix-net').appendChild(newPlayer);
let d = JSON.parse(e.detail.connection.data)
if(d.team == app.player.data.team) {
let testIfExistAlready = app.mainRenderBundle.filter(obj =>
obj.name && obj.name.includes(d.mesh));
if(testIfExistAlready.length == 0) {
app.localHero.loadFriendlyHero(d);
}
} else {
let testIfExistAlready = app.mainRenderBundle.filter(obj =>
obj.name && obj.name.includes(d.mesh));
if(testIfExistAlready.length > 0) {
console.log('[new enemy hero already exist do nothing]', d);
} else {
app.enemies.loadEnemyHero(d);
}
}
}
});
addEventListener('self-msg-data', (e) => {
let d = JSON.parse(e.detail.data);
console.log('<data-receive self>', d);
if(d.type == "damage") {
let IsEnemyHeroObj = forestOfHollowBlood.enemies.enemies.find((enemy) => enemy.name === d.defenderName);
let IsEnemyCreepObj = forestOfHollowBlood.enemies.creeps.find((creep) => creep.name === d.defenderName);
if(IsEnemyHeroObj) {
// console.log('<data-receive damage for IsEnemyHeroObj >', IsEnemyHeroObj);
const progress = Math.max(0, Math.min(1, d.hp / IsEnemyHeroObj.getHPMax()));
IsEnemyHeroObj.heroe_bodies[0].effects.energyBar.setProgress(progress);
IsEnemyHeroObj.hp = d.hp;
if(progress == 0) {
if(app.localHero.name == d.attackerName) {
console.log('<data-receive damage KILL by local >', d.attackerName);
app.localHero.killEnemy(1);
}
}
}
} else if(d.type == "damage-creep") {
if(app.player.data.team == d.defenderTeam) {
console.log('<data-receive damage local creep but from self :', d.defenderTeam);
// can be both team
let getCreepByIndex = parseInt(d.defenderName[d.defenderName.length - 1]);
app.localHero.friendlyLocal.creeps[getCreepByIndex]
.heroe_bodies[0].effects.energyBar.setProgress(d.progress);
if(d.progress <= 0.09) {
app.localHero.friendlyLocal.creeps[getCreepByIndex].creepFocusAttackOn = null;
app.localHero.friendlyLocal.creeps[getCreepByIndex].setDead();
setTimeout(() => {
app.localHero.friendlyLocal.creeps[getCreepByIndex].setStartUpPosition();
app.localHero.friendlyLocal.creeps[getCreepByIndex].gotoFinal = false;
app.localHero.friendlyLocal.creeps[getCreepByIndex].heroe_bodies[0].effects.energyBar.setProgress(1);
app.localHero.friendlyLocal.creeps[getCreepByIndex].hp = 300;
setTimeout(() => {
dispatchEvent(new CustomEvent('navigate-friendly_creeps', {detail: 'test'}))
}, 100);
}, 500)
}
} else {
console.log('<data-receive damage enemy creep but from self :', d.defenderTeam);
let getCreepByIndex = parseInt(d.defenderName[d.defenderName.length - 1]);
app.enemies.creeps[getCreepByIndex]
.heroe_bodies[0].effects.energyBar.setProgress(d.progress);
app.enemies.creeps[getCreepByIndex].creepFocusAttackOn = null;
if(d.progress <= 0.09) {
app.enemies.creeps[getCreepByIndex].setDead();
setTimeout(() => {
app.enemies.creeps[getCreepByIndex].setStartUpPosition();
app.enemies.creeps[getCreepByIndex].gotoFinal = false;
app.enemies.creeps[getCreepByIndex].heroe_bodies[0].effects.energyBar.setProgress(1);
app.enemies.creeps[getCreepByIndex].hp = 300;
}, 700);
}
}
} else if (d.type == "damage-tron") {
if(app.player.data.team == d.defenderTeam) {
app.tron.effects.energyBar.setProgress(d.progress);
if(d.progress == 0) {
app.tron.globalAmbient = [2, 1, 1];
mb.show(`☠️☠️☠️ ${app.player.data.enemyTeam} ☠️☠️☠️`);
mb.show(`☠️ Enemy wins ☠️ ${app.player.data.enemyTeam} `);
setTimeout(() => {
location.assign("rpg-menu.html");
}, 15000);
}
} else {
app.enemytron.effects.energyBar.setProgress(d.progress);
if(d.progress == 0) {
app.tron.globalAmbient = [2, 1, 1];
mb.show(`🏆🏆🏆 Your team wins ! 🏆🏆🏆 ${app.player.data.team} 🏆🏆🏆`);
app.localHero.setSalute();
setTimeout(() => {
location.assign("rpg-menu.html",);
}, 15000);
}
}
}
});
addEventListener('only-data-receive', (e) => {
let d = JSON.parse(e.detail.data);
// if(e.detail.from.connectionId == app.net.session.connection.connectionId) {
// console.log('<data-receive damage for local hero !!!>', d)
// }
if(d.type == "damage") {
// console.log('<data-receive damage for >', d.defenderName);
let IsEnemyHeroObj = forestOfHollowBlood.enemies.enemies.find((enemy) => enemy.name === d.defenderName);
let IsEnemyCreepObj = forestOfHollowBlood.enemies.creeps.find((creep) => creep.name === d.defenderName);
// new
let IsFriendlyHeroObj = forestOfHollowBlood.localHero.friendlyLocal.heroes.find((fhero) => fhero.name === d.defenderName);
if(IsFriendlyHeroObj) {
// console.log('<data-receive damage for IsFriendlyHeroObj >', IsFriendlyHeroObj);
const progress = Math.max(0, Math.min(1, d.hp / IsFriendlyHeroObj.getHPMax()));
IsFriendlyHeroObj.heroe_bodies[0].effects.energyBar.setProgress(progress);
console.log('<data-receive damage IsFriendlyHeroObj progress >', progress);
} else if(IsEnemyHeroObj) {
// console.log('<data-receive damage for IsEnemyHeroObj >', IsEnemyHeroObj);
const progress = Math.max(0, Math.min(1, d.hp / IsEnemyHeroObj.getHPMax()));
IsEnemyHeroObj.heroe_bodies[0].effects.energyBar.setProgress(progress);
console.log('<data-receive damage IsEnemyHeroObj progress >', progress);
if(progress == 0) {
if(app.localHero.name == d.attackerName) {
console.log('<data-receive damage KILL by local >', d.attackerName);
app.localHero.killEnemy(1);
}
}
//..
} else if(IsEnemyCreepObj) {
// maybe never ?
// console.log('<data-receive damage for IsEnemyCreepObj >', IsEnemyCreepObj);
const progress = Math.max(0, Math.min(1, d.hp / IsEnemyCreepObj.getHPMax()));
IsEnemyCreepObj.heroe_bodies[0].effects.energyBar.setProgress(progress);
} else if(app.localHero.name == d.defenderName) {
// console.log('<data-receive damage for LOCAL HERO >');
const progress = Math.max(0, Math.min(1, d.hp / app.localHero.getHPMax()));
app.localHero.heroe_bodies[0].effects.energyBar.setProgress(progress);
app.localHero.hp = d.hp;
if(d.hp == 0 || progress == 0) {
// local hero dead
app.localHero.setDead();
setTimeout(() => {
app.localHero.heroe_bodies[0].position.setPosition(
startUpPositions[forestOfHollowBlood.player.data.team][0],
startUpPositions[forestOfHollowBlood.player.data.team][1],
startUpPositions[forestOfHollowBlood.player.data.team][2]
);
app.localHero.heroe_bodies[0].effects.energyBar.setProgress(1);
let newhp = app.localHero.getHPMax();
app.localHero.hp = newhp;
app.net.sendOnlyData({
type: "hero-update",
heroName: d.defenderName,
hp: newhp,
progress: 1
});
}, 600);
}
}
} else if(d.type == "damage-creep") {
if(app.player.data.team == d.defenderTeam) {
// console.log('<data-receive damage local creep team:', d.defenderTeam);
let getCreepByIndex = parseInt(d.defenderName[d.defenderName.length - 1]);
app.localHero.friendlyLocal.creeps[getCreepByIndex]
.heroe_bodies[0].effects.energyBar.setProgress(d.progress);
if(d.progress <= 0.09) {
app.localHero.friendlyLocal.creeps[getCreepByIndex].creepFocusAttackOn = null;
app.localHero.friendlyLocal.creeps[getCreepByIndex].setDead();
setTimeout(() => {
app.localHero.friendlyLocal.creeps[getCreepByIndex].setStartUpPosition();
app.localHero.friendlyLocal.creeps[getCreepByIndex].gotoFinal = false;
app.localHero.friendlyLocal.creeps[getCreepByIndex].heroe_bodies[0].effects.energyBar.setProgress(1);
app.localHero.friendlyLocal.creeps[getCreepByIndex].hp = 300;
setTimeout(() => {
dispatchEvent(new CustomEvent('navigate-friendly_creeps', {detail: 'test'}))
}, 200);
}, 500)
}
} else {
let getCreepByIndex = parseInt(d.defenderName[d.defenderName.length - 1]);
app.enemies.creeps[getCreepByIndex]
.heroe_bodies[0].effects.energyBar.setProgress(d.progress);
app.enemies.creeps[getCreepByIndex].creepFocusAttackOn = null;
if(d.progress <= 0.09) {
app.enemies.creeps[getCreepByIndex].setDead();
setTimeout(() => {
app.enemies.creeps[getCreepByIndex].setStartUpPosition();
app.enemies.creeps[getCreepByIndex].gotoFinal = false;
app.enemies.creeps[getCreepByIndex].heroe_bodies[0].effects.energyBar.setProgress(1);
app.enemies.creeps[getCreepByIndex].hp = 300;
}, 700);
}
}
} else if(d.type == "damage-tron") {
if(app.player.data.team == d.defenderTeam) {
app.tron.effects.energyBar.setProgress(d.progress);
if(d.progress == 0) {
app.tron.globalAmbient = [2, 1, 1];
mb.show(`☠️☠️☠️ ${app.player.data.enemyTeam} ☠️☠️☠️`);
mb.show(`☠️ Enemy wins ☠️ ${app.player.data.enemyTeam} `);
setTimeout(() => {
location.assign("rpg-menu.html");
}, 15000);
}
} else {
app.enemytron.effects.energyBar.setProgress(d.progress);
if(d.progress == 0) {
app.tron.globalAmbient = [2, 1, 1];
mb.show(`🏆🏆🏆 Your team wins ! 🏆🏆🏆 ${app.player.data.team} 🏆🏆🏆`);
app.localHero.setSalute();
setTimeout(() => {
location.assign("rpg-menu.html",);
}, 15000);
}
}
} else if(d.type == "hero-update") {
let IsEnemyHeroObj = forestOfHollowBlood.enemies.enemies.find((enemy) => enemy.name === d.heroName);
if(IsEnemyHeroObj) {
console.log(d.hp, ' d.hp ->... IsEnemyHeroObj ... ', IsEnemyHeroObj.hp);
IsEnemyHeroObj.hp = d.hp;
IsEnemyHeroObj.heroe_bodies[0].effects.energyBar.setProgress(1);
return;
}
let IsEnemyCreepObj = forestOfHollowBlood.enemies.creeps.find((creep) => creep.name === d.heroName);
if(IsEnemyCreepObj) {
console.log('... IsEnemyCreepObj test for now ... ')
IsEnemyCreepObj.hp = d.hp;
IsEnemyCreepObj.heroe_bodies[0].effects.energyBar.setProgress(1);
return;
}
let IsFriendlyHeroObj = forestOfHollowBlood.localHero.friendlyLocal.heroes.find((fhero) => fhero.name === d.heroName);
if(IsFriendlyHeroObj) {
// console.log(d.hp, ' d.hp -> ... IsFriendlyHeroObj ... ', IsFriendlyHeroObj.hp)
IsFriendlyHeroObj.hp = d.hp;
IsFriendlyHeroObj.heroe_bodies[0].effects.energyBar.setProgress(1);
return;
}
}
})
addEventListener('local-hero-bodies-ready', () => {
app.cameras.RPG.position[1] = 130;
app.cameras.RPG.movementSpeed = 100;
app.cameras.RPG.followMe = forestOfHollowBlood.localHero.heroe_bodies[0].position;
app.cameras.RPG.mousRollInAction = true;
app.tts.speakHero(app.player.data.hero.toLowerCase(), 'hello');
});
forestOfHollowBlood.RPG = new Controller(forestOfHollowBlood);
forestOfHollowBlood.mapLoader = new MEMapLoader(forestOfHollowBlood, "./res/meshes/nav-mesh/navmesh.json");
forestOfHollowBlood.localHero = new Character(
forestOfHollowBlood, forestOfHollowBlood.player.data.path,
forestOfHollowBlood.player.data.hero, [forestOfHollowBlood.player.data.archetypes]);
forestOfHollowBlood.localHero.inventory = new Inventory(forestOfHollowBlood.localHero);
forestOfHollowBlood.marketPlace = new Marketplace(forestOfHollowBlood.localHero);
forestOfHollowBlood.marketPlace.mb = mb;
forestOfHollowBlood.marketPlace.label = forestOfHollowBlood.label;
forestOfHollowBlood.localHero.inventory.loadAllRules(forestOfHollowBlood.marketPlace._generateItems());
forestOfHollowBlood.HUD = new HUD(forestOfHollowBlood.localHero);
forestOfHollowBlood.collisionSystem = new CollisionSystem(forestOfHollowBlood);
app.matrixSounds.play('music');
app.matrixSounds.audios.music.onended = () => {
app.matrixSounds.play('music2');
};
app.matrixSounds.audios.music2.onended = () => {
app.matrixSounds.play('music');
};
forestOfHollowBlood.addLight();
})
window.app = forestOfHollowBlood;