/* * Author: Tim Plate * Project: Advanced Roleplay Environment * Copyright (c) 2022 Tim Plate Solutions */ var selectedBodyPart = "HEAD"; var lastActionCategory = ""; var bodyPartInformation = {}; var bodyPartsBleeding = []; var tourniquets = []; var infusions = []; var lastLogs = []; var bloodVolume = 0; var localizationData = {}; var activePain = 0; var lastActionClick = 0; var soundsPlaying = []; function closeMenu() { $(".nearbyPlayersContainer").hide(); $(".menuContainer").fadeOut("fast"); fetch(`https://${GetParentResourceName()}/NUIEventCloseMenu`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', } }); } $(document).keydown(function (e) { switch (e.keyCode) { case 27: // ESC closeMenu(); break; } }); $(".closeButton").click(closeMenu); function getDebugValue(debugId) { return $(`.debugValue[data-id='${debugId}'] .value`).text(); } function updateDebugValue(debugId, debugValue) { if (typeof debugValue == 'number') debugValue = debugValue.toFixed(2); if (typeof debugValue == 'string') { if (debugValue == "OK") $(`.debugValue[data-id='${debugId}'] .value`).css("color", "green"); else if (debugValue == "FATAL") $(`.debugValue[data-id='${debugId}'] .value`).css("color", "red"); else if (debugValue == "HEMORRAGE I" || debugValue == "HEMORRAGE II" || debugValue == "HEMORRAGE III" || debugValue == "HEMORRAGE IV") $(`.debugValue[data-id='${debugId}'] .value`).css("color", "#ffa800"); else $(`.debugValue[data-id='${debugId}'] .value`).css("color", "#ffa800"); } $(`.debugValue[data-id='${debugId}'] .value`).text(debugValue); } const getTime = (time) => { const d = new Date(time * 1000); const dd = [d.getHours(), d.getMinutes(), d.getSeconds()].map((a) => (a < 10 ? '0' + a : a)); return dd.join(':'); }; window.addEventListener('message', (event) => { switch (event.data.payload) { case "createDebugValue": if (typeof event.data.payloadData.value == 'number') event.data.payloadData.value = event.data.payloadData.value.toFixed(2); $(".debugContainer").append(`
${event.data.payloadData.id}
${event.data.payloadData.value}
`); break; case "deleteDebugValue": $(`.debugValue[data-id='${event.data.payloadData.id}']`).remove(); break; case "updateDebugValue": updateDebugValue(event.data.payloadData.id, event.data.payloadData.value); break; case "createDebugSection": $(".debugContainer").append(`
${event.data.payloadData.name}
`); break; case "deleteDebugSection": $(`.debugSection[data-id='${event.data.payloadData.id}']`).remove(); break; case "toggleDispatchInfo": event.data.payloadData.value ? $(".contact_medic_box").fadeIn("fast") : $(".contact_medic_box").fadeOut("fast"); if (event.data.payloadData.key) $(".contact_medic_key").text(event.data.payloadData.key); break; case "pushAvailableActions": _addActions(JSON.parse(event.data.payloadData.actions), event.data.payloadData.bodyPart, event.data.payloadData.category); break; case "updateInternalPainEffect": updateInternalPainEffect(event.data.payloadData.value); break; case "updateInternalBleedingEffect": updateInternalBleedingEffect(event.data.payloadData.value); break; case "updateHeartbeatSound": updateHeartbeatSound(event.data.payloadData.value); break; case "showManualRespawnText": if (event.data.payloadData.value) $(".manual_respawn_box").fadeIn("fast"); else $(".manual_respawn_box").fadeOut("fast"); if (event.data.payloadData.key) $(".manual_respawn_key").text(event.data.payloadData.key); break; case "stateMenu": $(".nearbyPlayersContainer").hide(); if (event.data.payloadData.value) { $(".menuContainer").fadeIn("fast"); $(".menuName").text(event.data.payloadData.title); onClickBodyPart("HEAD"); } else { $(".menuContainer").fadeOut("fast"); } break; case "updateLogs": $("#logList").html(""); var logs = JSON.parse(event.data.payloadData.logs); if (!logs) return; logs.sort(function (a, b) { return b.timestamp - a.timestamp; }); lastLogs = logs; logs.forEach(function (log) { $("#logList").append(`
  • ${log.message}
    ${getTime(log.timestamp)}
  • `); }); break; case "loadBodyPartInformation": stateLoadingIndicator(true); loadBodyPartInformation(event.data.payloadData.value, event.data.payloadData.tourniquets, event.data.payloadData.bleedings, event.data.payloadData.infusions, event.data.payloadData.pain, event.data.payloadData.bloodVolume, event.data.payloadData.triageSelection, event.data.payloadData.unconscious); break; case "updateECGValues": updateHeartRate(Math.round(event.data.payloadData.heartRate)); updateBloodPressure(event.data.payloadData.highBP, event.data.payloadData.lowBP); break; case "toggleECGMenu": $(".ecg").css("display", event.data.payloadData.value ? "block" : "none"); break; case "forcedUpdateBodyPartInformation": _showInjuries(selectedBodyPart, _getBodyPartInjuries(selectedBodyPart)); _getAvailableActionsForCategory(lastActionCategory, selectedBodyPart); break; case "toggleTriageSelection": event.data.payloadData ? $(".triageSelection").show() : $(".triageSelection").hide(); case "toggleUnconsciousScreen": if (event.data.payloadData.value) { $(".remaining_time").hide(); $(".remaining_time").text(`00:00 ${_translateText("MINUTES_REMAINING")}`); } event.data.payloadData.value ? $(".unconsciousScreen").fadeIn("fast") : $(".unconsciousScreen").fadeOut("fast"); break; case "toggleAnesthesiaScreen": event.data.payloadData.value ? $(".anesthesiaScreen").fadeIn("fast") : $(".anesthesiaScreen").fadeOut("fast"); break; case "updateUnconsciousTime": const minutes = Math.floor(event.data.payloadData.seconds / 60); $(".remaining_time").text(`${minutes.toString().padStart(2, "0")}:${(event.data.payloadData.seconds % 60).toString().padStart(2, "0")} ${_translateText("MINUTES_REMAINING")}`); $(".unconsciousScreen .progressbar").css("width", event.data.payloadData.seconds / event.data.payloadData.max_seconds * 100 + "%"); if ($(".remaining_time").css("display") == "none") $(".remaining_time").fadeIn("fast"); break; case "play3dSound": play3dSound(event.data.payloadData.id, event.data.payloadData.coords, event.data.payloadData.distance, event.data.payloadData.name); break; case "stop3dSound": stop3dSound(event.data.payloadData.id); break; } }); function stateLoadingIndicator(state) { state ? $(".loadingIndicator").fadeIn("fast") : $(".loadingIndicator").fadeOut("fast"); } $(".bodyPart").click(function () { onClickBodyPart($(this).attr("data-bodyPart")); }); $(".actionCategory i").click(function () { onClickAction(this); }); function onClickBodyPart(bodyPartIdentifier) { if (Date.now() - lastActionClick < 250) return; selectedBodyPart = bodyPartIdentifier; lastActionClick = Date.now(); $(".actionCategory i").removeClass("active"); var localizedName = _translateText(bodyPartIdentifier); $(".bodyPartTitle").text(localizedName); var bodyPartCategory = _getBodyPartCategory(bodyPartIdentifier); _rethinkAvailableActions(bodyPartCategory, false); _showInjuries(bodyPartIdentifier, _getBodyPartInjuries(bodyPartIdentifier)); } function onClickAction(htmlElement) { if ($(htmlElement).hasClass("disabled")) return; $(".actionCategory i").removeClass("active"); var actionIdentifier = $(htmlElement).attr("data-actionIdentifier"); $(htmlElement).addClass("active"); lastActionCategory = actionIdentifier; _getAvailableActionsForCategory(actionIdentifier, selectedBodyPart); } function loadBodyPartInformation(bodyPartInjuries, tourniquetsData, bleedings, infusionsData, pain, bloodV, triageSelection, unconscious) { bodyPartInformation = JSON.parse(bodyPartInjuries); tourniquets = JSON.parse(tourniquetsData); bodyPartsBleeding = JSON.parse(bleedings); infusions = JSON.parse(infusionsData); bloodVolume = bloodV; activePain = pain; $('.bodyPart').each(function () { $(this).prop("src", $(this).attr("orig-src")); }); for (const [key, value] of Object.entries(bodyPartInformation)) { var oldLevel = "none"; value.forEach(injury => { var bodyPart = _getBodyPartByIdentifier(key); _updateBodyPartLevel(bodyPart, oldLevel, injury.level, injury.needSewing); oldLevel = injury.level; }); } $(".bodyPart[data-tourniquet]").hide(); tourniquets.forEach((location) => { const bodyPart = _getTourniquetBodyPartByIdentifier(location); $(bodyPart).show(); }); $("#triageSelect").val(triageSelection); $("#triageSelect").attr("data-value", triageSelection); unconscious ? $("#black_triage").show() : $("#black_triage").hide(); stateLoadingIndicator(false); } var activePainInterval = undefined; var _painEffectSwitch = false; function updateInternalPainEffect(painLevel) { if (painLevel > 0 && !activePainInterval) { activePainInterval = setInterval(() => { if (_painEffectSwitch) { $(".painEffect").css("background", `radial-gradient(circle, rgba(0,0,0,0) 0%, rgba(255,255,255,0.75) ${Math.max(500 / painLevel, 200)}%)`) $(".painEffect").fadeOut("fast"); } else { $(".painEffect").css("background", `radial-gradient(circle, rgba(0,0,0,0) 0%, rgba(255,255,255,0.75) ${Math.max(500 / painLevel, 200)}%)`) $(".painEffect").fadeIn("fast"); } _painEffectSwitch = !_painEffectSwitch; }, Math.max(500 / painLevel, 250)); } else { if (!activePainInterval) return; clearInterval(activePainInterval); activePainInterval = undefined; } if (painLevel <= 0 && !activePainInterval && $(".painEffect").css("display") == "block") $(".painEffect").fadeOut("fast"); } function updateInternalBleedingEffect(bleeding) { $(`.bleedingEffect`).css("background", `radial-gradient(circle, rgba(0,0,0,0) 0%, rgba(255,0,0,0.75) 1000%`) bleeding ? $(".bleedingEffect").fadeIn("fast") : $(".bleedingEffect").fadeOut("fast"); } var currentHeartbeatSound; function updateHeartbeatSound(type) { if (type == "none") { if (!currentHeartbeatSound) return; currentHeartbeatSound.audio.pause(); currentHeartbeatSound = undefined; return; } if (!currentHeartbeatSound) { currentHeartbeatSound = { type: type, audio: new Audio(`assets/sounds/heartrate_${type}.wav`) } currentHeartbeatSound.audio.loop = true; currentHeartbeatSound.audio.volume = 0.4; currentHeartbeatSound.audio.play(); return; } if (currentHeartbeatSound.type == type) return; currentHeartbeatSound.audio.pause(); currentHeartbeatSound.audio = new Audio(`assets/sounds/heartrate_${type}.wav`); currentHeartbeatSound.type = type; currentHeartbeatSound.audio.play(); } function _getBodyPartInjuries(bodyPart) { return bodyPartInformation[bodyPart]; } function _getBodyPartCategory(bodyPartIdentifier) { if (bodyPartIdentifier.includes("HEAD")) return "HEAD"; if (bodyPartIdentifier.includes("ARM")) return "ARM"; if (bodyPartIdentifier.includes("TORSO")) return "TORSO"; if (bodyPartIdentifier.includes("LEG")) return "LEG"; return "UNKNOWN"; } function _forceSelectActionCategory(actionCategoryHtmlElement) { onClickAction(actionCategoryHtmlElement); } function _capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } function _showInjuries(bodyPart, injuries) { const bloodLossLevel = returnBloodLossLevel(bloodVolume); const infusionsAtBodyPart = _getInfusionsAtBodyPart(bodyPart); const tourniquetAppliedText = tourniquets.includes(bodyPart) ? `
  • ${_translateText("TOURNIQUET_APPLIED")}
  • ` : ""; const bleedingText = bodyPartsBleeding.includes(bodyPart) ? `
  • ${_translateText("ACTIVE_BLEEDING")}

  • ` : "
    "; const painText = activePain > 0.0 ? `
  • ${_translateText("ACTIVE_PAIN")}
  • ` : ""; const bloodVolumeText = bloodLossLevel ? `
  • ${bloodLossLevel}
  • ` : ""; $(".bodyInjuries").html(""); $(".bodyInjuries").append(tourniquetAppliedText); $(".bodyInjuries").append(painText); $(".bodyInjuries").append(bloodVolumeText); $(".bodyInjuries").append(bleedingText); for (i = 0; i < infusionsAtBodyPart.length; i++) { var infusion = infusionsAtBodyPart[i]; $(".bodyInjuries").append(`
  • ${_translateText("ACTIVE_INFUSION")} ${_translateText("INFUSION_" + infusion.name.toUpperCase())} (~${Math.round(infusion.remainingVolume / 5) * 5}ml ${_translateText("VOLUME_LEFT")})
  • ${i == infusionsAtBodyPart.length - 1 ? "
    " : ""}`); } if (!injuries || injuries.length == 0) { $(".bodyInjuries").append(`
  • ${_translateText("NO_INJURIES_AT_THIS_BODY_PART")}
  • `); return; } injuries.forEach(injury => { const injuryNormal = `
  • 1x ${_translateText(injury.level.toUpperCase())} ${_translateText(injury.key.toUpperCase())}
  • `; const injuryNeedSewing = `
  • 1x ${_capitalizeFirstLetter(_translateText(injury.key.toUpperCase()))} (${_translateText("NEED_SEWING")})
  • `; if(injury.needSewing) $(".bodyInjuries").append(injuryNeedSewing); else $(".bodyInjuries").append(injuryNormal); }); } function _rethinkAvailableActions(bodyPartCategory, isSelf) { var allowedActions = []; switch (bodyPartCategory) { case "HEAD": allowedActions = ["diagnoses", "bandages"]; break; case "ARM": allowedActions = ["diagnoses", "bandages", "syringes"]; if (!isSelf) allowedActions.push("infusions"); break; case "TORSO": allowedActions = ["bandages"]; if (!isSelf) allowedActions.push("cpr"); break; case "LEG": allowedActions = ["diagnoses", "bandages", "syringes"]; break; } if (!isSelf) allowedActions.push("carry"); $(".actionCategory i").addClass("disabled"); allowedActions.forEach(action => { $(`.actionCategory i[data-actionIdentifier=${action}]`).removeClass("disabled"); }); _forceSelectActionCategory($(`.actionCategory i[data-actionIdentifier=${allowedActions[0]}]`)); } function _addActions(actions, bodyPart, category) { var availableActions = []; actions.forEach((data) => { availableActions.push(_returnAction(data, bodyPart)); }); $(".buttonContainer").html(""); $(".buttonContainer").hide(); availableActions.forEach(action => { $(".buttonContainer").append(`
    ${_translateText(action.translation_key)}
    `); }); $(".buttonContainer").show(); $(".actionButton").click(function () { if (typeof $(this).attr("data-payload") === 'undefined') return; var actionData = JSON.parse($(this).attr("data-payload")); fetch(`https://${GetParentResourceName()}/NUIEventTriggerAction`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: JSON.stringify({ name: actionData.name, bodyPart: bodyPart }) }); }); } function _getAvailableActionsForCategory(actionCategoryIdentifier, bodyPart) { fetch(`https://${GetParentResourceName()}/NUIEventGetAvailableActions`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: JSON.stringify({ category: actionCategoryIdentifier, bodyPart: bodyPart }) }); } function _returnAction(translationKey, payload) { return { translation_key: translationKey, action_data: { name: translationKey, payload: payload } } } function _getNearbyPlayers() { fetch(`https://${GetParentResourceName()}/NUIEventGetNearbyPlayers`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: JSON.stringify({}), }).then(response => response.json()).then(data => { _updateNearbyPlayers(data); }); } function _updateNearbyPlayers(players) { $(".nearbyPlayers").html(""); players.forEach(player => { $(".nearbyPlayers").append(`
    ${player.name} (${player.distance.toFixed(2)}m)
    `); }); $(".nearbyPlayersContainer").fadeIn("fast"); $(".nearbyPlayer").click(function () { var playerId = $(this).attr("data-id"); _loadPlayerMenu(playerId); }); } $(".playersButton").click(function () { _getNearbyPlayers(); }); $(".nearbyPlayersContainer>.containerClose").click(function () { $(".nearbyPlayersContainer").fadeOut("fast"); }); function _loadPlayerMenu(id) { fetch(`https://${GetParentResourceName()}/NUIEventGetPlayerMenu`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: JSON.stringify({ id: id }) }); } function _updateBodyPartLevel(element, oldLevel, level, needSewing) { var currentImage = $(element).prop("src"); var newLevel = "n"; var newLevelRank = _getInjuryLevelRank(level); var oldLevelRank = _getInjuryLevelRank(oldLevel); switch (level) { case "minor": newLevel = "l" break; case "medium": newLevel = "m" break; case "large": newLevel = "h" break; case "fatal": newLevel = "s" break; } if (oldLevelRank > newLevelRank) return; if (needSewing) newLevel = "s"; var newImageSrc = currentImage.split(";")[0] + ";" + newLevel + ".png"; $(element).prop("src", newImageSrc); } $("img").attr("draggable", "false"); function _getInjuryLevelRank(level) { switch (level) { case "minor": return 1; case "medium": return 2; case "large": return 3; case "fatal": return 4; default: return 0; } } function _getInfusionsAtBodyPart(bodyPart) { return infusions.filter(x => x.bodyPart == bodyPart && x.remainingVolume > 0); } function _getBodyPartByIdentifier(identifier) { return $(`.bodyPart[data-bodyPart='${identifier}']`).not("[data-tourniquet]"); } function _getTourniquetBodyPartByIdentifier(identifier) { return $(`.bodyPart[data-tourniquet][data-bodyPart='${identifier}']`); } function returnBloodLossLevel(bloodVolume) { if (bloodVolume < 3000) return _translateText("LOST_A_FATAL_AMOUNT_OF_BLOOD"); else if (bloodVolume < 3600) return _translateText("LOST_A_LARGE_AMOUNT_OF_BLOOD"); else if (bloodVolume < 4200) return _translateText("LOST_A_LOT_OF_BLOOD"); else if (bloodVolume < 5400) return _translateText("LOST_SOME_BLOOD"); return false; } // ToDo finish returnPainLevel function returnPainLevel(painLevel) { if (painLevel > 5.0) return _translateText("HAS_A_VERY_HIGH_PAIN_LEVEL"); else if (bloodVolume < 3600) return _translateText("LOST_A_LARGE_AMOUNT_OF_BLOOD"); else if (bloodVolume < 4200) return _translateText("LOST_A_LOT_OF_BLOOD"); else if (bloodVolume < 5400) return _translateText("LOST_SOME_BLOOD"); return false; } function play3dSound(soundId, coords, distance, soundName) { var sound = new Howl({ src: ['assets/sounds/' + soundName + '.wav'], volume: 1.0, id: soundId, onend: function () { delete soundsPlaying[soundId]; }, }); sound.pos(coords.x, coords.y, coords.z); sound.orientation(coords.x, coords.y, coords.z); sound.pannerAttr({ panningModel: 'equalpower', refDistance: 1, rolloffFactor: 5, distanceModel: 'linear' }, soundId); sound.play(); soundsPlaying[soundId] = { sound: sound, coords: coords, distance: distance, id: soundId }; } function stop3dSound(soundId) { if (typeof soundsPlaying[soundId] === 'undefined') return; var sound = soundsPlaying[soundId]; sound.sound.stop(); delete soundsPlaying[soundId]; } setInterval(function () { if (Object.keys(soundsPlaying).length > 0) { fetch(`https://${GetParentResourceName()}/NUIEventUpdatePlayerPosition`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: JSON.stringify({ }) }).then(res => res.json()).then(data => { data = JSON.parse(data); Howler.pos(data.coords.x, data.coords.y, data.coords.z); Howler.orientation(data.rotation.x, data.rotation.y, data.rotation.z, 0, 0, 0); for (var sound in soundsPlaying) { sound = soundsPlaying[sound]; Howler.mute(calculateDistance(data.coords, sound.coords) > sound.distance, sound.id); } }); } }, 100); String.format = function (format) { var args = Array.prototype.slice.call(arguments, 1); return format.replace(/{(\d+)}/g, function (match, number) { return typeof args[number] != 'undefined' ? args[number] : match ; }); }; function _translateText(/**/) { var args = arguments; if (localizationData[args[0]] != undefined) return String.format(localizationData[args[0]], args[1]) else return "Not translated: " + args[0] } function calculateDistance(p1, p2) { var a = p2.x - p1.x; var b = p2.y - p1.y; var c = p2.z - p1.z; return Math.sqrt(a * a + b * b + c * c); } $("#logCopy").click(function () { let text = ""; lastLogs.forEach(function (log) { text += `[${getTime(log.timestamp)}] ${log.message}\n`; }); /* Clipboard Workaround since navigator.[...] isn't allolwed */ const el = document.createElement('textarea'); el.value = text; document.body.appendChild(el); el.select(); document.execCommand('copy'); document.body.removeChild(el); }); var oldValue = $('#triageSelect').val(); $('#triageSelect').on('change', function () { const triageSelection = this.value; fetch(`https://${GetParentResourceName()}/NUIEventUpdateTriage`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', }, body: JSON.stringify({ triageSelection: triageSelection }) }).then(res => res.json()).then(data => { if (!data) { $(this).val(oldValue); return; } oldValue = triageSelection; $(this).attr("data-value", triageSelection); }); }); $(document).ready(() => { fetch(`https://${GetParentResourceName()}/NUIEventLoadLocalizationData`, { method: 'POST', headers: { 'Content-Type': 'application/json; charset=UTF-8', } }).then(response => response.json()).then(data => { fetch(`../script/languages/${data}.json`) .then(response => response.json()) .then(data => { localizationData = data.messages; $("div").each(function () { if ($(this).text().startsWith("NUI_")) $(this).text(_translateText($(this).text().split("NUI_")[1])); }); $("span").each(function () { if ($(this).text().startsWith("NUI_")) $(this).text(_translateText($(this).text().split("NUI_")[1])); }); $("option").each(function () { if ($(this).text().startsWith("NUI_")) $(this).text(_translateText($(this).text().split("NUI_")[1])); }); }); }); });