UNPKG

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

1,455 lines (1,424 loc) 1.1 MB
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HeroProps = exports.Hero = exports.HERO_ARCHETYPES = void 0; exports.mergeArchetypes = mergeArchetypes; exports.mergeArchetypesWeighted = mergeArchetypesWeighted; /** * @description * Hero based classes * Core of RPG type of game. */ const HERO_ARCHETYPES = exports.HERO_ARCHETYPES = { Warrior: { hpMult: 1.2, manaMult: 0.8, attackMult: 1.1, armorMult: 1.2, moveSpeed: 1.0, attackSpeed: 1.0, hpRegenMult: 1.2, manaRegenMult: 0.8 }, Tank: { hpMult: 1.6, manaMult: 0.6, attackMult: 0.9, armorMult: 1.5, moveSpeed: 0.9, attackSpeed: 0.8, hpRegenMult: 1.4, manaRegenMult: 0.7 }, Assassin: { hpMult: 0.9, manaMult: 0.9, attackMult: 1.5, armorMult: 0.8, moveSpeed: 1.3, attackSpeed: 1.4, hpRegenMult: 0.9, manaRegenMult: 0.9 }, Mage: { hpMult: 0.8, manaMult: 1.5, attackMult: 0.9, armorMult: 0.7, moveSpeed: 1.0, attackSpeed: 0.9, hpRegenMult: 0.8, manaRegenMult: 1.5 }, Support: { hpMult: 1.0, manaMult: 1.2, attackMult: 0.8, armorMult: 1.0, moveSpeed: 1.0, attackSpeed: 1.0, hpRegenMult: 1.2, manaRegenMult: 1.2 }, Ranger: { hpMult: 1.0, manaMult: 1.0, attackMult: 1.2, armorMult: 0.9, moveSpeed: 1.2, attackSpeed: 1.2, hpRegenMult: 1.0, manaRegenMult: 1.0 }, Summoner: { hpMult: 0.9, manaMult: 1.4, attackMult: 0.8, armorMult: 0.9, moveSpeed: 1.0, attackSpeed: 0.9, hpRegenMult: 1.0, manaRegenMult: 1.4 }, Necromancer: { hpMult: 0.9, manaMult: 1.4, attackMult: 0.9, armorMult: 0.8, moveSpeed: 1.0, attackSpeed: 0.9, hpRegenMult: 0.9, manaRegenMult: 1.4 }, Engineer: { hpMult: 1.1, manaMult: 1.0, attackMult: 1.0, armorMult: 1.1, moveSpeed: 1.0, attackSpeed: 1.0, hpRegenMult: 1.0, manaRegenMult: 1.0 }, // special for creeps creep: { hpMult: 0.6, manaMult: 1, attackMult: 1, armorMult: 1, moveSpeed: 0.3, attackSpeed: 0.5, hpRegenMult: 1, manaRegenMult: 1 } }; class HeroProps { constructor(name) { this.name = name; this.levels = [{ level: 1, xp: 100, hp: 500, mana: 300, attack: 40, armor: 5, moveSpeed: 1.0, attackSpeed: 1.0, hpRegen: 2.0, mpRegen: 1.0, abilityPoints: 1 }, { level: 2, xp: 200, hp: 570, mana: 345, attack: 46, armor: 5.5, moveSpeed: 1.05, attackSpeed: 1.05, hpRegen: 2.25, mpRegen: 1.15, abilityPoints: 2 }, { level: 3, xp: 350, hp: 645, mana: 395, attack: 52, armor: 6, moveSpeed: 1.10, attackSpeed: 1.10, hpRegen: 2.52, mpRegen: 1.31, abilityPoints: 3 }, { level: 4, xp: 500, hp: 725, mana: 450, attack: 58, armor: 6.5, moveSpeed: 1.15, attackSpeed: 1.16, hpRegen: 2.81, mpRegen: 1.49, abilityPoints: 4 }, { level: 5, xp: 700, hp: 810, mana: 510, attack: 65, armor: 7, moveSpeed: 1.20, attackSpeed: 1.23, hpRegen: 3.13, mpRegen: 1.68, abilityPoints: 5 }, { level: 6, xp: 900, hp: 900, mana: 575, attack: 72, armor: 7.5, moveSpeed: 1.25, attackSpeed: 1.31, hpRegen: 3.48, mpRegen: 1.88, abilityPoints: 6 }, { level: 7, xp: 1150, hp: 995, mana: 645, attack: 80, armor: 8, moveSpeed: 1.30, attackSpeed: 1.40, hpRegen: 3.85, mpRegen: 2.10, abilityPoints: 7 }, { level: 8, xp: 1400, hp: 1095, mana: 720, attack: 88, armor: 8.5, moveSpeed: 1.35, attackSpeed: 1.50, hpRegen: 4.25, mpRegen: 2.33, abilityPoints: 8 }, { level: 9, xp: 1700, hp: 1200, mana: 800, attack: 97, armor: 9, moveSpeed: 1.40, attackSpeed: 1.61, hpRegen: 4.68, mpRegen: 2.58, abilityPoints: 9 }, { level: 10, xp: null, hp: 1310, mana: 885, attack: 107, armor: 9.5, moveSpeed: 1.45, attackSpeed: 1.73, hpRegen: 5.13, mpRegen: 2.84, abilityPoints: 10 }]; this.currentLevel = 1; this.currentXP = 0; this.gold = 200; this.baseXP = 100; this.baseGold = 200; // --- Multipliers this.xpMultiplier = { stronger: 0.1, weaker: 0.2 }; this.goldMultiplier = 50; // --- Maximum level difference for XP this.maxLevelDiffForXP = 3; this.abilities = [{ name: "Spell 1", level: 1, maxLevel: 4 }, { name: "Spell 2", level: 0, maxLevel: 4 }, { name: "Spell 3", level: 0, maxLevel: 4 }, { name: "Ultimate", level: 0, maxLevel: 1 }]; this.invertoryBonus = { hp: 1, mana: 1, attack: 1, armor: 1, moveSpeed: 1, attackSpeed: 1, hpRegen: 1, mpRegen: 1 }; this.updateStats(); } updateStats() { const lvlData = this.levels[this.currentLevel - 1]; if (!lvlData) return; // console.log('updateStats: armor ', this.invertoryBonus.armor) Object.assign(this, { hp: lvlData.hp * this.invertoryBonus.hp, mana: lvlData.mana * this.invertoryBonus.mana, attack: lvlData.attack * this.invertoryBonus.attack, armor: lvlData.armor * this.invertoryBonus.armor, moveSpeed: lvlData.moveSpeed * this.invertoryBonus.moveSpeed, attackSpeed: lvlData.attackSpeed * this.invertoryBonus.attackSpeed, hpRegen: lvlData.hpRegen * this.invertoryBonus.hpRegen, mpRegen: lvlData.mpRegen * this.invertoryBonus.mpRegen, abilityPoints: lvlData.abilityPoints }); dispatchEvent(new CustomEvent('stats-localhero', { detail: { gold: this.gold, currentLevel: this.currentLevel, xp: this.currentXP, hp: this.hp, mana: this.mana, attack: this.attack, armor: this.armor, moveSpeed: this.moveSpeed, attackSpeed: this.attackSpeed, hpRegen: this.hpRegen, mpRegen: this.mpRegen } })); } // --- Kill enemy: only enemyLevel argument killEnemy(enemyLevel) { if (enemyLevel < 1) enemyLevel = 1; const levelDiff = this.currentLevel - enemyLevel; // --- XP calculation with cap for weak enemies let earnedXP = 0; if (levelDiff < this.maxLevelDiffForXP) { if (enemyLevel >= this.currentLevel) { earnedXP = this.baseXP * (1 + this.xpMultiplier.stronger * (enemyLevel - this.currentLevel)); } else { earnedXP = this.baseXP * (1 - this.xpMultiplier.weaker * (this.currentLevel - enemyLevel)); } earnedXP = Math.round(Math.max(0, earnedXP)); } // --- Gold reward const goldReward = this.baseGold + enemyLevel * this.goldMultiplier; this.currentXP += earnedXP; this.gold += goldReward; // for creep any way - rule if they kill hero // maybe some smlall reward... checkLevelUp console.log(`${this.name} killed Lv${enemyLevel} enemy: +${earnedXP} XP, +${goldReward} gold`); this.checkLevelUp(); } // --- Automatic level-up checkLevelUp() { while (this.currentLevel < 10) { const nextLevelXP = this.levels[this.currentLevel - 1].xp; if (this.currentXP >= nextLevelXP) { this.currentLevel++; console.log(`${this.name} leveled up! Now level ${this.currentLevel}`); this.updateStats(); this.currentXP -= nextLevelXP; } else break; } // emit for hud // dispatchEvent(new CustomEvent('stats-localhero', { // detail: { // gold: this.gold, // currentLevel: this.currentLevel, // xp: this.currentXP, // hp: this.hp, // mana: this.mana, // attack: this.attack, // armor: this.armor, // moveSpeed: this.moveSpeed, // attackSpeed: this.attackSpeed, // hpRegen: this.hpRegen, // mpRegen: this.mpRegen, // } // })) } // --- Upgrade abilities upgradeAbility(spellIndex) { const spell = this.abilities[spellIndex]; if (!spell) return false; if (spell.level < spell.maxLevel && this.abilityPoints > 0) { spell.level++; this.abilityPoints--; console.log(`${this.name} upgraded ${spell.name} to level ${spell.level}`); return true; } return false; } // --- Get / Set stats getStat(statName) { return this[statName] ?? null; } setStat(statName, value) { if (this.hasOwnProperty(statName)) { this[statName] = value; return true; } return false; } // --- Debug print debugPrint() { console.table({ level: this.currentLevel, xp: this.currentXP, gold: this.gold, hp: this.hp, mana: this.mana, attack: this.attack, armor: this.armor, moveSpeed: this.moveSpeed, attackSpeed: this.attackSpeed, hpRegen: this.hpRegen, mpRegen: this.mpRegen, abilityPoints: this.abilityPoints, abilities: this.abilities.map(a => `${a.name} (Lv ${a.level})`).join(", ") }); } showUpgradeableAbilities() { if (this.abilityPoints <= 0) { console.log(`${this.name} has no ability points to spend.`); return []; } const upgradeable = this.abilities.map((spell, index) => ({ ...spell, index })).filter(spell => spell.level < spell.maxLevel); if (upgradeable.length === 0) { console.log(`${this.name} has no spells left to upgrade.`); return []; } console.log(`${this.name} has ${this.abilityPoints} ability point(s) available.`); console.log("Upgradeable spells:"); upgradeable.forEach(spell => { console.log(` [${spell.index}] ${spell.name} (Lv ${spell.level}/${spell.maxLevel})`); }); return upgradeable; } // --- Upgrade a spell by name (optional convenience) upgradeAbilityByName(spellName) { const spellIndex = this.abilities.findIndex(s => s.name === spellName); if (spellIndex === -1) return false; return this.upgradeAbility(spellIndex); } // attack - direction always local -> enemy (remote) calcDamage(attacker, defender, abilityMultiplier = 1.0, critChance = 1, critMult = 1) { // Use attack from your current scaled stats const baseAttack = attacker.attack; // Optional: magic abilities could use mana or another stat later const base = baseAttack * abilityMultiplier; // Critical hit roll - not for now const crit = Math.random() < critChance ? critMult : 1.0; // Damage reduced by armor const damage = Math.max(0, base * crit - defender.armor); // Apply damage defender.hp = Math.max(0, defender.hp - damage); // --- Sync energy bar (0 → 1) const progress = Math.max(0, Math.min(1, defender.hp / this.getHPMax())); dispatchEvent(new CustomEvent(`onDamage-${defender.name}`, { detail: { progress: progress, attacker: attacker.name, defenderLevel: defender.currentLevel, defender: defender.name, hp: defender.hp, damage: damage } })); return { damage, crit: crit > 1.0 }; } } exports.HeroProps = HeroProps; class Hero extends HeroProps { constructor(name, archetypes = ["Warrior"]) { super(name); // limit to 2 mix this.archetypes = archetypes.slice(0, 2); this.applyArchetypeStats(); } applyArchetypeStats() { if (!this.archetypes || this.archetypes.length === 0) return; let typeData; if (this.archetypes.length === 2) { typeData = mergeArchetypes(this.archetypes[0], this.archetypes[1]); } else { typeData = HERO_ARCHETYPES[this.archetypes[0]]; } if (!typeData) return; this.hp *= typeData.hpMult; this.mana *= typeData.manaMult; this.attack *= typeData.attackMult; this.armor *= typeData.armorMult; this.moveSpeed *= typeData.moveSpeed; this.attackSpeed *= typeData.attackSpeed; this.hpRegen *= typeData.hpRegenMult; this.mpRegen *= typeData.manaRegenMult; this._mergedArchetype = typeData._mergedFrom || this.archetypes; } getHPMax() { let typeData; if (this.archetypes.length === 2) { typeData = mergeArchetypes(this.archetypes[0], this.archetypes[1]); } else { typeData = HERO_ARCHETYPES[this.archetypes[0]]; } this.baseHp = this.levels[this.currentLevel - 1].hp; return this.baseHp; // * typeData.hpMult; ??? } // Override updateStats to include archetype scaling updateStats() { super.updateStats(); this.applyArchetypeStats(); } } exports.Hero = Hero; function mergeArchetypes(typeA, typeB) { if (!HERO_ARCHETYPES[typeA] || !HERO_ARCHETYPES[typeB]) { console.warn(`Invalid archetype(s): ${typeA}, ${typeB}`); return HERO_ARCHETYPES[typeA] || HERO_ARCHETYPES[typeB]; } const a = HERO_ARCHETYPES[typeA]; const b = HERO_ARCHETYPES[typeB]; const merged = {}; // Average their multipliers (or tweak with weights if needed) for (const key in a) { if (typeof a[key] === "number" && typeof b[key] === "number") { merged[key] = (a[key] + b[key]) / 2; } } merged._mergedFrom = [typeA, typeB]; return merged; } // not used now function mergeArchetypesWeighted(typeA, typeB, weightA = 0.7) { const a = HERO_ARCHETYPES[typeA]; const b = HERO_ARCHETYPES[typeB]; const wB = 1 - weightA; const merged = {}; for (const key in a) if (typeof a[key] === "number" && typeof b[key] === "number") merged[key] = a[key] * weightA + b[key] * wB; merged._mergedFrom = [typeA, typeB]; return merged; } },{}],2:[function(require,module,exports){ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ROCK_RANK = exports.RCSAccount = void 0; var _utils = require("../../../src/engine/utils.js"); /** * @description This is clone from hang3d. * @author Nikola Lukic * @email zlatnaspirala@gmail.com * @website https://maximumroulette.com */ class RCSAccount { email = null; token = null; constructor(apiDomain) { this.apiDomain = apiDomain; this.visitor(); addEventListener('F12', e => { console.log(`%c[Debbuger] ${e.detail}`, REDLOG); localStorage.removeItem("visitor"); this.visitor(e.detail); }); // this.leaderboardBtn = document.createElement('div') // this.leaderboardBtn.id = 'leaderboard'; // this.leaderboardBtn.innerHTML = ` // <button id="leaderboardBtn" class="btn">Leaderboard</button> // `; // document.body.appendChild(this.leaderboardBtn); // this.leaderboardBtn = document.getElementById('leaderboardBtn'); // this.leaderboardBtn.addEventListener("click", this.getLeaderboard) } createDOM = hideLoginForm => { if (typeof hideLoginForm === 'undefined') hideLoginForm = false; var parent = document.createElement('div'); this.parent = parent; //parent.classList.add('') parent.id = 'myAccountLoginForm'; if (hideLoginForm == true) parent.style.display = 'none'; var logo = document.createElement('img'); logo.id = 'logologin'; logo.setAttribute('alt', 'Login'); logo.style = 'width: 100px;border-radius: 10px;padding: 6px;'; logo.src = './res/icons/512.png'; var title = document.createElement('div'); title.style.display = 'flex'; title.innerHTML = ` <div style='width:100%; margin: 5px 5px;'> <h2 style='margin: 5px 5px;'>Rocket GamePlay Login Form</h2> Maximumroulette.com</div> `; title.appendChild(logo); var content = document.createElement('div'); content.style.display = 'flex'; content.style.flexDirection = 'column'; content.style.background = 'transparent'; var emailLabel = document.createElement('label'); emailLabel.id = 'emailLabel'; emailLabel.innerHTML = `Email:`; emailLabel.setAttribute('for', 'arg-email'); var email = document.createElement('input'); // email.classList.add('myInput') email.id = 'arg-email'; var passLabel = document.createElement('label'); passLabel.id = 'passLabel'; passLabel.innerHTML = `Passw:`; passLabel.setAttribute('for', 'arg-pass'); var pass = document.createElement('input'); pass.id = 'arg-pass'; // pass.classList.add('myInput') var loginBtn = document.createElement('button'); loginBtn.id = 'loginRCSBtn'; loginBtn.innerHTML = `LOGIN`; loginBtn.classList.add('btn'); loginBtn.classList.add('btnMargin'); loginBtn.addEventListener('click', this.login); var gotoRegisterMyAccount = document.createElement('button'); gotoRegisterMyAccount.id = 'registerBtn'; gotoRegisterMyAccount.classList.add(`btn`); gotoRegisterMyAccount.classList.add(`btnMargin`); gotoRegisterMyAccount.innerHTML = `REGISTER`; gotoRegisterMyAccount.addEventListener('click', this.register); var hideLoginMyAccount = document.createElement('button'); hideLoginMyAccount.classList.add(`btn`); hideLoginMyAccount.classList.add(`btnMargin`); hideLoginMyAccount.innerHTML = `NO LOGIN -> INSTANT PLAY`; hideLoginMyAccount.addEventListener('click', () => { (0, _utils.byId)('myAccountLoginForm').remove(); }); if ((0, _utils.isMobile)() == false) { var descText = document.createElement('div'); descText.id = 'descText'; descText.style = 'font-size:smaller;'; descText.innerHTML = `<span style="width:45%" >Welcome to rocketCraftingServer platform, enjoy in 'Forest Of Hollow Blood' lets magic begin.</span>`; // <span style="width:45%;" >Add Url params '?video=false&audio=false' to disable streaming</span> } parent.appendChild(title); parent.appendChild(content); content.appendChild(emailLabel); content.appendChild(email); content.appendChild(passLabel); content.appendChild(pass); content.appendChild(loginBtn); content.appendChild(gotoRegisterMyAccount); content.appendChild(hideLoginMyAccount); // content.appendChild(logo) if ((0, _utils.isMobile)() == false) content.appendChild(descText); document.body.appendChild(parent); }; createLeaderboardDOM = data => { if ((0, _utils.byId)('leaderboard') != null) { (0, _utils.byId)('leaderboard').style.display = 'block'; return; } // console.log('TEST MOBILE +++') var parent = document.createElement('div'); parent.style = ``; parent.classList.add('leaderboard'); // if(isMobile() == true) { // parent.style = ` // position: absolute; // border-radius: 4px; // top: 10%; // left: 0%; // width: 95%; // padding: 10px;`; // } parent.id = 'leaderboard'; var title = document.createElement('div'); title.innerHTML = `<h3>Top 10 leaderboard [RocketCraftingServer]</h3>`; parent.appendChild(title); var tableLabel = document.createElement('div'); tableLabel.style.display = 'flex'; tableLabel.style.flexDirection = 'row'; var nicklabel = document.createElement('div'); nicklabel.innerText = 'Nickname'; nicklabel.style.width = '100%'; var pointslabel = document.createElement('div'); pointslabel.innerText = 'Points'; pointslabel.style.width = '100%'; tableLabel.appendChild(nicklabel); tableLabel.appendChild(pointslabel); parent.appendChild(tableLabel); var parentForTable = document.createElement('div'); parentForTable.style.height = '70vh'; parentForTable.style.overflow = 'scroll'; parentForTable.style.overflowX = 'hidden'; data.forEach((element, index) => { var table = document.createElement('div'); table.style.display = 'flex'; table.style.flexDirection = 'row'; table.style.justifyContent = 'center'; table.style.alignItems = 'center'; table.style.boxShadow = 'none'; var nick = document.createElement('div'); nick.innerText = element.nickname; nick.style.width = '100%'; if (index == 0) { nick.style.boxShadow = '0px 7px 2px -1px #ffe100'; } else if (index == 1) { nick.style.boxShadow = '0px 7px 2px -1px white'; } else if (index == 2) { nick.style.boxShadow = '0px 7px 2px -1px #a01010'; } else { nick.style.boxShadow = '0px 7px 2px -1px #757471'; } var points = document.createElement('div'); points.innerText = element.points; points.style.width = '100%'; if (index == 0) { points.style.boxShadow = '0px 7px 2px -1px #ffe100'; } else if (index == 1) { points.style.boxShadow = '0px 7px 2px -1px white'; } else if (index == 2) { points.style.boxShadow = '0px 7px 2px -1px #a01010'; } else { points.style.boxShadow = '0px 7px 2px -1px #757471'; } // var medal = document.createElement('img'); // medal.id = 'medal'; // logo.src = './assets/icons/icon96.png'; table.appendChild(nick); table.appendChild(points); table.innerHTML += ROCK_RANK.getRankMedalImg(ROCK_RANK.getRank(element.points)); parentForTable.appendChild(table); }); parent.appendChild(parentForTable); var hideBtn = document.createElement('button'); hideBtn.classList = 'btn'; hideBtn.style.marginTop = '7px'; hideBtn.innerText = 'HIDE'; hideBtn.addEventListener('click', () => { parent.style.display = 'none'; }); parent.appendChild(hideBtn); document.body.appendChild(parent); }; register = () => { this.register_procedure(this); }; async register_procedure() { let route = this.apiDomain || location.origin; (0, _utils.byId)('loginRCSBtn').disabled = true; (0, _utils.byId)('registerBtn').disabled = true; let args = { emailField: (0, _utils.byId)('arg-email') != null ? (0, _utils.byId)('arg-email').value : null, passwordField: (0, _utils.byId)('arg-pass') != null ? (0, _utils.byId)('arg-pass').value : null }; if (args.emailField == null || args.passwordField == null) { _utils.mb.show('Please fill up email and passw for login or register.'); } fetch(route + '/rocket/register', { method: 'POST', headers: _utils.jsonHeaders, body: JSON.stringify(args) }).then(d => { return d.json(); }).then(r => { _utils.mb.error(`${r.message}`); if (r.message == "Check email for conmfirmation key.") { this.email = (0, _utils.byId)('arg-email').value; sessionStorage.setItem('email', (0, _utils.byId)('arg-email').value); (0, _utils.byId)('emailLabel').remove(); (0, _utils.byId)('loginRCSBtn').remove(); (0, _utils.byId)('arg-email').remove(); (0, _utils.byId)("passLabel").innerHTML = 'ENTER CONFIRMATION CODE'; (0, _utils.byId)('arg-pass').value = ""; (0, _utils.byId)('registerBtn').removeEventListener('click', this.register); (0, _utils.byId)('registerBtn').disabled = false; (0, _utils.byId)('registerBtn').innerHTML = 'CONFIRM CODE FROM EMAIL'; (0, _utils.byId)('registerBtn').id = 'CC'; (0, _utils.byId)('CC').addEventListener('click', () => { this.confirmation(); }); sessionStorage.setItem('RocketAcountRegister', 'Check email for conmfirmation key.'); } else { setTimeout(() => { _utils.mb.show("Next Register/Login call try in 5 secounds..."); this.preventDBLOG = false; this.preventDBREG = false; (0, _utils.byId)('loginRCSBtn').disabled = false; (0, _utils.byId)('registerBtn').disabled = false; }, 5000); } }).catch(err => { console.log('[My Account Error]', err); _utils.mb.show("Next Register call try in 5 secounds..."); setTimeout(() => { this.preventDBLOG = false; this.preventDBREG = false; (0, _utils.byId)('loginRCSBtn').disabled = false; (0, _utils.byId)('registerBtn').disabled = false; }, 5000); return; }); } confirmation = async () => { let route = this.apiDomain; const args = { emailField: this.email, tokenField: (0, _utils.byId)('arg-pass').value }; fetch(route + '/rocket/confirmation', { method: 'POST', headers: _utils.jsonHeaders, body: JSON.stringify(args) }).then(d => { return d.json(); }).then(r => { if (r.message == "Wrong confirmation code.") {} else if (r.message == "Confirmation done.") { alert(r.message); this.parent.innerHTML = ''; // ---- this.createDOM(); } _utils.mb.error(`${r.message}`); }); }; gameStarted = async () => { let route = this.apiDomain || location.origin; let args = { username: 'guest' }; fetch(route + '/rocket/fohbstart', { method: 'POST', headers: _utils.jsonHeaders, body: JSON.stringify(args) }).then(d => { return d.json(); }).then(r => { console.log(r.message); _utils.mb.show(`${r.message}`); if (r.message == "User logged") {} }).catch(err => { console.log('[RCS Error]', err); return; }); }; login = async () => { let route = this.apiDomain || location.origin; (0, _utils.byId)('loginRCSBtn').disabled = true; (0, _utils.byId)('registerBtn').disabled = true; let args = { emailField: (0, _utils.byId)('arg-email') != null ? (0, _utils.byId)('arg-email').value : null, passwordField: (0, _utils.byId)('arg-pass') != null ? (0, _utils.byId)('arg-pass').value : null }; fetch(route + '/rocket/login', { method: 'POST', headers: _utils.jsonHeaders, body: JSON.stringify(args) }).then(d => { return d.json(); }).then(r => { console.log(r.message); _utils.mb.show(`${r.message}`); if (r.message == "User logged") { this.email = (0, _utils.byId)('arg-email').value; (0, _utils.byId)('myAccountLoginForm').style.display = 'none'; sessionStorage.setItem('RocketAcount', JSON.stringify(r.flag)); } }).catch(err => { console.log('[My Account Error]', err); _utils.mb.show("Next Login call try in 5 secounds..."); setTimeout(() => { this.preventDBLOG = false; this.preventDBREG = false; (0, _utils.byId)('registerBtn').disabled = false; (0, _utils.byId)('loginRCSBtn').disabled = false; }, 5000); return; }); }; async visitor(isRegular) { if (typeof isRegular === 'undefined') isRegular = 'Yes'; if (localStorage.getItem("visitor") == 'welcome') return; let route = this.apiDomain; let args = { email: (0, _utils.byId)('arg-email') != null ? (0, _utils.byId)('arg-email').value : 'no-email', userAgent: navigator.userAgent.toString(), fromUrl: location.href.toString(), isRegular: isRegular }; fetch(route + '/rocket/visitors', { method: 'POST', headers: _utils.jsonHeaders, body: JSON.stringify(args) }).then(d => { return d.json(); }).then(() => { localStorage.setItem("visitor", "welcome"); }).catch(err => { console.log('ERR', err); }); } getLeaderboard = async e => { e.preventDefault(); (0, _utils.byId)('netHeaderTitle').click(); // this.leaderboardBtn.disabled = true; fetch(this.apiDomain + '/rocket/public-leaderboard', { method: 'POST', headers: _utils.jsonHeaders, body: JSON.stringify({}) }).then(d => { return d.json(); }).then(r => { _utils.mb.error(`${r.message}`); if (r.message == "You got leaderboard data.") { this.leaderboardData = r.leaderboard; this.createLeaderboardDOM(r.leaderboard); } // setTimeout(() => {this.leaderboardBtn.disabled = false}, 5000) }).catch(err => { console.log('[Leaderboard Error]', err); _utils.mb.show("Next call try in 5 secounds..."); // setTimeout(() => {this.leaderboardBtn.disabled = false}, 5000) return; }); }; getLeaderboardFor3dContext = async e => { // e.preventDefault(); fetch(this.apiDomain + '/rocket/public-leaderboard', { method: 'POST', headers: _utils.jsonHeaders, body: JSON.stringify({}) }).then(d => { return d.json(); }).then(r => { _utils.mb.error(`${r.message}`); if (r.message == "You got leaderboard data.") { this.leaderboardData = r.leaderboard; console.log('PREPARE FOR 3d context', this.leaderboardData); } setTimeout(() => { this.leaderboardBtn.disabled = false; }, 5000); }).catch(err => { console.log('[Leaderboard Error]', err); _utils.mb.show("Next call try in 5 secounds..."); setTimeout(() => { this.leaderboardBtn.disabled = false; }, 5000); return; }); }; async points10() { let route = this.apiDomain; if (sessionStorage.getItem('RocketAcount') != null && JSON.parse(sessionStorage.getItem('RocketAcount')).token) { console.log("NO ACCOUNT USER", sessionStorage.getItem('RocketAcount')); return; } let args = { email: (0, _utils.byId)('arg-email') != null ? (0, _utils.byId)('arg-email').value : 'no-email', userAgent: navigator.userAgent.toString(), fromUrl: location.href.toString(), token: JSON.parse(sessionStorage.getItem('RocketAcount')).token, mapName: 'FOHB' }; fetch(route + '/rocket/point-plus10', { method: 'POST', headers: _utils.jsonHeaders, body: JSON.stringify(args) }).then(d => { return d.json(); }).then(() => { localStorage.setItem("visitor", "welcome"); }).catch(err => { console.log('ERR', err); }); } async dead() { let route = this.apiDomain; if (sessionStorage.getItem('RocketAcount') != null && JSON.parse(sessionStorage.getItem('RocketAcount')).token) { console.log("NO ACCOUNT USER", sessionStorage.getItem('RocketAcount')); return; } let args = { email: (0, _utils.byId)('arg-email') != null ? (0, _utils.byId)('arg-email').value : 'no-email', userAgent: navigator.userAgent.toString(), fromUrl: location.href.toString(), token: JSON.parse(sessionStorage.getItem('RocketAcount')).token, mapName: 'FOHB' }; fetch(route + '/rocket/point-plus10', { method: 'POST', headers: _utils.jsonHeaders, body: JSON.stringify(args) }).then(d => { return d.json(); }).then(() => { localStorage.setItem("visitor", "welcome"); }).catch(err => { console.log('ERR', err); }); } } exports.RCSAccount = RCSAccount; var ROCK_RANK = exports.ROCK_RANK = { getRank: points => { points = parseInt(points); if (points < 1001) { return "junior"; } else if (points < 2000) { return "senior"; } else if (points < 3000) { return "captain"; } else if (points < 5000) { return "general"; } else { return "ultimate-killer"; } }, getRankMedalImg: rank => { if (rank == 'junior') { return `<img style="height: 60px" src="./res/icons/medals/1.png" />`; } else if (points == 'senior') { return `<img style="height: 60px" src="./res/icons/medals/2.png" />`; } else if (points == 'captain') { return `<img style="height: 60px" src="./res/icons/medals/3.png" />`; } else if (points == 'general') { return `<img style="height: 60px" src="./res/icons/medals/4.png" />`; } else { return `<img style="height: 60px" src="./res/icons/medals/5.png" />`; } } }; },{"../../../src/engine/utils.js":47}],3:[function(require,module,exports){ "use strict"; var _webgpuGltf = require("../../../src/engine/loaders/webgpu-gltf.js"); var _net = require("../../../src/engine/networking/net.js"); var _utils = require("../../../src/engine/utils.js"); var _world = _interopRequireDefault(require("../../../src/world.js")); var _hero = require("./hero.js"); var _animatedCursor = require("../../../src/engine/plugin/animated-cursor/animated-cursor.js"); var _matrixStream = require("../../../src/engine/networking/matrix-stream.js"); var _rocketCraftingAccount = require("./rocket-crafting-account.js"); var _enBackup = require("../../../public/res/multilang/en-backup.js"); var _tts = require("./tts.js"); var _editor = require("../../../src/tools/editor/editor.js"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } /** * @name forestOfHollowBloodStartSceen * * @licence * Creative Commons Attribution 4.0 International (CC BY 4.0) * You are free to share and adapt this project, provided that you give appropriate credit. * Attribution requirement: * Include the following notice (with working link) in any distributed version or about page: * * "Forest Of Hollow Blood — an RPG example made with MatrixEngineWGPU (https://github.com/zlatnaspirala/matrix-engine-wgpu)" * @Note * “Character and animation assets from Mixamo, * used under Adobe’s royalty‑free license. * Redistribution of raw assets is not permitted.” * * @Note * This is startup main instance for menu screen and for the game. * All @zlatnaspirala software use networking based * on openvidu/kurento media server(webRTC). * Node.js used for middleware. * Server Events API also used for helping in creation of * matching/waiting list players or get status of public channel * (game-play channel). * * @note * Only last non selected hero player will get * first free hero in selection action next/back. * For now. Next better varian can be timer solution. * * @Backend Session account stuff. * RocketCraftingServer platform used. **/ _utils.LS.clear(); _utils.SS.clear(); let forestOfHollowBloodStartSceen = new _world.default({ dontUsePhysics: true, // useEditor: true, useSingleRenderPass: true, canvasSize: 'fullscreen', // {w: window.visualViewport.width, h: window.visualViewport.height } mainCameraParams: { type: 'WASD', responseCoef: 1000 }, clearColor: { r: 0, b: 0.1, g: 0.1, a: 1 } }, forestOfHollowBloodStartSceen => { if ('serviceWorker' in navigator) { if (location.hostname.indexOf('localhost') == -1) { navigator.serviceWorker.register('cache.js'); } else { // RCSAccount navigator.serviceWorker.getRegistrations().then(function (registrations) { for (let registration of registrations) { registration.unregister(); } }); } } else { console.warn('Matrix Engine WGPU : No support for web workers in this browser.'); } forestOfHollowBloodStartSceen.tts = new _tts.MatrixTTS(); forestOfHollowBloodStartSceen.account = new _rocketCraftingAccount.RCSAccount("https://maximumroulette.com"); forestOfHollowBloodStartSceen.account.createDOM(); forestOfHollowBloodStartSceen.FS = new _utils.FullscreenManager(); forestOfHollowBloodStartSceen.gamePlayStatus = null; // in future replace with server event solution forestOfHollowBloodStartSceen.gamePlayStatusTimer = null; forestOfHollowBloodStartSceen.heroByBody = []; forestOfHollowBloodStartSceen.selectedHero = 0; forestOfHollowBloodStartSceen.lock = false; // Audios forestOfHollowBloodStartSceen.matrixSounds.createAudio('music2', 'res/audios/rpg/music.mp3', 1); forestOfHollowBloodStartSceen.matrixSounds.createAudio('music', 'res/audios/rpg/wizard-rider.mp3', 1); forestOfHollowBloodStartSceen.matrixSounds.createAudio('click1', 'res/audios/click1.mp3', 1); app.matrixSounds.audios.click1.volume = 0.2; forestOfHollowBloodStartSceen.matrixSounds.createAudio('hover', 'res/audios/kenney/mp3/click3.mp3', 2); forestOfHollowBloodStartSceen.matrixSounds.createAudio('feel', 'res/audios/rpg/feel.mp3', 2); let heros = null; function checkUsername() { if (JSON.parse(_utils.SS.get('RocketAcount')) != null && typeof JSON.parse(_utils.SS.get('RocketAcount')).nickname !== 'undefined') { return JSON.parse(_utils.SS.get('RocketAcount')).nickname; } else { if (app.net.session !== null) { return app.net.session.connection.connectionId; } else { return 'nosession'; } } } // Networking forestOfHollowBloodStartSceen.net = new _net.MatrixStream({ active: true, domain: 'maximumroulette.com', port: 2020, sessionName: 'forestOfHollowBlood-free-for-all-start', resolution: '160x240', isDataOnly: true }); function 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; } function checkHeroStatus() { const indices = []; document.querySelectorAll('[data-hero-index]').forEach(elem => { const index = parseInt(elem.getAttribute('data-hero-index')); indices.push(index); }); // check if any value appears more than once const hasDuplicate = indices.some((val, i) => indices.indexOf(val) !== i); return hasDuplicate; } function determinateTeam() { console.log('check remote conn.app.net.session.remoteConnections.size..', app.net.session.remoteConnections.size); if (app.net.session.remoteConnections.size == 0) { // Rule - even -> south team odd -> north team return "south"; } else { if ((0, _utils.isOdd)(app.net.session.remoteConnections.size) == true) { return "north"; } else { return "south"; } } } function determinateSelection() { if (app.net.session.connection != null) { // console.log("Test team data moment", byId(`waiting-${app.net.session.connection.connectionId}`).getAttribute('data-hero-team')) let testDom = (0, _utils.byId)(`waiting-${app.net.session.connection.connectionId}`).getAttribute('data-hero-team'); if (typeof testDom != 'string') { console.log('Potencial error not handled....'); } app.net.sendOnlyData({ type: "selectHeroIndex", selectHeroIndex: app.selectedHero, team: testDom }); } // fix for local if ((0, _utils.byId)(`waithero-img-${app.net.session.connection.connectionId}`)) { let heroImage = (0, _utils.byId)(`waithero-img-${app.net.session.connection.connectionId}`); heroImage.src = `./res/textures/rpg/hero-image/${handleHeroImage(app.selectedHero)}.png`; heroImage.setAttribute('data-hero-index', app.selectedHero); } else { let heroImage = document.createElement('img'); heroImage.setAttribute('data-hero-index', app.selectedHero); heroImage.id = `waithero-img-${app.net.session.connection.connectionId}`; heroImage.width = '64'; heroImage.height = '64'; heroImage.src = `./res/textures/rpg/hero-image/${handleHeroImage(app.selectedHero)}.png`; (0, _utils.byId)(`waiting-${app.net.session.connection.connectionId}`).appendChild(heroImage); } // Only last non selected hero player will get // first free hero in selection action next/back. // For now. if (checkHeroStatus() == true) { console.log("hero used keep graphics no send"); return; } if (isAllSelected() == true) { forestOfHollowBloodStartSceen.gotoGamePlay(); } } forestOfHollowBloodStartSceen.determinateSelection = determinateSelection; function isAllSelected() { let sumParty = document.querySelectorAll('[id*="waiting-"]'); let testSelection = document.querySelectorAll('[id*="waithero-img-"]'); console.info(testSelection, ' testSelection vs Number of players:', sumParty); if (sumParty.length == forestOfHollowBloodStartSceen.MINIMUM_PLAYERS) { // good all are still here if (testSelection.length == forestOfHollowBloodStartSceen.MINIMUM_PLAYERS) { // good all selected hero !PLAY! return true; } else { _utils.mb.error(`No selection hero for all players...`); return false; } } else { _utils.mb.error(`No enough players...`); return false; } } forestOfHollowBloodStartSceen.gotoGamePlay = preventEmit => { setTimeout(() => { // check again ! good all selected hero !PLAY! // console.log('...', byId(`waiting-${app.net.session.connection.connectionId}`)); _utils.LS.set('player', { mesh: heros[app.selectedHero].meshName, hero: heros[app.selectedHero].name, path: heros[app.selectedHero].path, archetypes: [heros[app.selectedHero].type], team: (0, _utils.byId)(`waiting-${app.net.session.connection.connectionId}`).getAttribute('data-hero-team'), data: Date.now(), numOfPlayers: forestOfHollowBloodStartSceen.MINIMUM_PLAYERS, useCameraOrAudio: true }); _utils.SS.set('player', { mesh: heros[app.selectedHero].meshName, hero: heros[app.selectedHero].name, path: heros[app.selectedHero].path, archetypes: [heros[app.selectedHero].type], team: (0, _utils.byId)(`waiting-${app.net.session.connection.connectionId}`).getAttribute('data-hero-team'), data: Date.now(), numOfPlayers: forestOfHollowBloodStartSceen.MINIMUM_PLAYERS, useCameraOrAudio: true }); if (typeof preventEmit === 'undefined') forestOfHollowBloodStartSceen.net.sendOnlyData({ type: 'start' }); location.assign('rpg-game.html'); }, 1000); }; if ('connection' in navigator && navigator.connection) { navigator.connection.onchange = e => { console.info('Network state changed...', e); if (e.target.downlink < 0.4) { (0, _utils.byId)('loader').style.display = 'block'; (0, _utils.byId)('loader').style.fontSize = '150%'; (0, _utils.byId)('loader').innerHTML = `NO INTERNET CONNECTIONS`; setTimeout(() => { location.href = 'https://maximumroulette.com'; }, 3000); } }; } addEventListener('check-gameplay-channel', e => { let info = e.detail; if (info.status != 'false' && typeof info.status !== "undefined") { // console.log('check-gameplay-channel status:', info.status) (0, _utils.byId)("onlineUsers").innerHTML = `GamePlay:Free`; forestOfHollowBloodStartSceen.gamePlayStatus = "free"; (0, _utils.byId)('startBtnText').innerHTML = app.label.get.play; (0, _utils.byId)("startBtnText").style.color = 'rgba(0, 0, 0, 0)'; clearInterval(forestOfHollowBloodStartSceen.gamePlayStatusTimer); forestOfHollowBloodStartSceen.gamePlayStatusTimer = null; } else { // console.log('check-gameplay-channel status:', info.status) if (typeof info.status != "undefined" && info.status == "false") { // no internet (0, _utils.byId)('loader').style.display = 'block'; alert("This is modal window, No internet connection... Please try "); } else { info = JSON.parse(e.detail); if (info.connections && info.connections.numberOfElements == 0) { (0, _utils.byId)("onlineUsers").innerHTML = `GamePlay:Free`; forestOfHollowBloodStartSceen.gamePlayStatus = "free"; (0, _utils.byId)('startBtnText').innerHTML = app.label.get.play; (0, _utils.byId)("startBtnText").style.color = 'rgba(0, 0, 0, 0)'; clearInterval(forestOfHollowBloodStartSceen.gamePlayStatusTimer); forestOfHollowBloodStartSceen.gamePlayStatusTimer = null; return; } (0, _utils.byId)("onlineUsers").innerHTML = `${app.label.get.alreadyingame}:${info.connections.numberOfElements}`; forestOfHollowBloodStartSceen.gamePlayStatus = "used"; (0, _utils.byId)('startBtnText').innerHTML = `${app.label.get.gameplaychannel}:${app.label.get.used}`; (0, _utils.byId)("startBtnText").style.color = 'rgb(255 53 53)'; forestOfHollowBloodStartSceen.gamePlayStatusTimer = setTimeout(() => { app.net.fetchInfo('forestOfHollowBlood-free-for-all'); }, 30000); } } }); forestOfHollowBloodStartSceen.MINIMUM_PLAYERS = location.hostname.indexOf('localhost') != -1 ? 2 : 4; forestOfHollowBloodStartSceen.setWaitingList = () => { // access net doms who comes with broadcaster2.html const waitingForOthersDOM = document.createElement("div"); waitingForOthersDOM.id = "waitingForOthersDOM"; Object.assign(waitingForOthersDOM.style, { flexFlow: 'wrap', width: "100%", height: "35%", backgroundColor: "rgba(60, 60, 60, 1)", display: "flex", alignItems: "center", justifyContent: "space-around", color: "white", fontFamily: "'Orbitron', sans-serif", zIndex: "1", fontSize: '20px', padding: "10px", boxSizing: "border-box" }); (0, _utils.byId)('session-header').appendChild(waitingForOthersDOM); const onlineUsers = document.createElement("div"); onlineUsers.id = "onlineUsers"; Object.assign(onlineUsers.style, { flexFlow: 'wrap', width: "100%", height: "35%", backgroundColor: "rgba(60, 60, 60, 1)", display: "flex", alignItems: "center", justifyContent: "space-around", color: "white", fontFamily: "'Orbitron', sans-serif", zIndex: "1", fontSize: '20px', padding: "10px", boxSizing: "border-box" }); (0, _utils.byId)('netHeader').appendChild(onlineUsers); // app.net.fetchInfo('forestOfHollowBlood-free-for-all'); }; if (document.querySelector('.form-group')) document.querySelector('.form-group').style.display = 'none'; // keep simple all networking code on top level // all job will be done with no account for now. addEventListener('net-ready', () => { (0, _utils.byId)('matrix-net').style.opacity = '0.75'; document.querySelector('.form-group').style.display = 'none'; (0, _utils.byId)("caller-title").innerHTML = `forestOfHollowBlood`; (0, _utils.byId)("sessionName").disabled = true; forestOfHollowBloodStartSceen.setWaitingList(); // check game-play channel setTimeout(() => { app.net.fetchInfo('forestOfHollowBlood-free-for-all'); app.sendmsg = m => { if (typeof m != 'string') return; if (m.length > 120) return; let username = checkUsername(); if (username != 'nosession') app.net.sendOnlyData({ type: "chat", msg: m, username: username }); }; }, 1500); }); addEventListener('connectionDestroyed', e => { (0, _utils.byId)(`waiting-${e.detail.connectionId}`).remove(); }); addEventListener("onConnectionCreated", e => { console.log('newconn : created', e.detail); let newPlayer = document.createElement('div'); if (app.net.session.connection.connectionId == e.detail.connection.connectionId) { console.log('newconn : created [LOCAL] determinate team'); document.title = app.net.session.connection.connectionId; let team = determinateTeam(); newPlayer.setAttribute('data-hero-team', team); newPlayer.innerHTML = `<div id="${e.detail.connection.connectionId}-title" >Player:${e.detail.connection.connectionId} Team:${team}</div>`; setTimeout(() => { //---------- test if (app.net.session.connection != null) { console.log("Test team data moment", (0, _utils.byId)(`waiting-${app.net.session.connection.connectionId}`).getAttribute('data-hero-team')); let testDom = (0, _utils.byId)(`waiting-${app.net.session.connection.connectionId}`).getAttribute('data-hero-team'); if (typeof testDom != 'string') { console.low('Potencial error not handled....'); } app.net.sendOnlyData({ type: "selectHeroIndex", selectHeroIndex: app.selectedHero, team: testDom }); app.net.sendOnlyData({ type: "team-notify", team: team }); } }, 2000); } else { newPlayer.innerHTML = `<div id="${e.detail.connection.connectionId}-title" >Player:${e.detail.connection.connectionId}</div>`; } newPlayer.id = `waiting-${e.detail.connection.connectionId}`; (0, _utils.byId)('waitingForOthersDOM').appendChild(newPlayer); let testParty = document.querySelectorAll('[id*="waiting-"]'); console.info('Test number of players:', testParty); if (testParty.length == forestOfHollowBloodStartSceen.MINIMUM_PLAYERS) { // when all choose hero goto play _utils.mb.success(`Consensus is reached. Party${forestOfHollowBloodStartSceen.MINIMUM_PLAYERS} When all player select hero gameplay starts. `); } else if (testParty.length < forestOfHollowBloodStartSceen.MINIMUM_PLAYERS) { _utils.mb.success(`Player ${e.detail.connection.connectionId} joined part