diff --git a/resources/[ERS]/SmartFiresLite/.fxap b/resources/[ERS]/SmartFiresLite/.fxap
index bd5b9a68e..0099fe9f0 100644
Binary files a/resources/[ERS]/SmartFiresLite/.fxap and b/resources/[ERS]/SmartFiresLite/.fxap differ
diff --git a/resources/[ERS]/SmartFiresLite/cl_smartfires.lua b/resources/[ERS]/SmartFiresLite/cl_smartfires.lua
index a4c6e88a3..7ab530c01 100644
Binary files a/resources/[ERS]/SmartFiresLite/cl_smartfires.lua and b/resources/[ERS]/SmartFiresLite/cl_smartfires.lua differ
diff --git a/resources/[ERS]/SmartFiresLite/sv_smartfires.lua b/resources/[ERS]/SmartFiresLite/sv_smartfires.lua
index 628b96de7..32f82356e 100644
Binary files a/resources/[ERS]/SmartFiresLite/sv_smartfires.lua and b/resources/[ERS]/SmartFiresLite/sv_smartfires.lua differ
diff --git a/resources/[ERS]/SmartHoseLite/.fxap b/resources/[ERS]/SmartHoseLite/.fxap
index affe7d6db..8e056a434 100644
Binary files a/resources/[ERS]/SmartHoseLite/.fxap and b/resources/[ERS]/SmartHoseLite/.fxap differ
diff --git a/resources/[ERS]/SmartHoseLite/cl_hose.lua b/resources/[ERS]/SmartHoseLite/cl_hose.lua
index 77300d92e..974a4887f 100644
Binary files a/resources/[ERS]/SmartHoseLite/cl_hose.lua and b/resources/[ERS]/SmartHoseLite/cl_hose.lua differ
diff --git a/resources/[ERS]/SmartHoseLite/sv_exports.lua b/resources/[ERS]/SmartHoseLite/sv_exports.lua
index 741f8947d..13271aff7 100644
Binary files a/resources/[ERS]/SmartHoseLite/sv_exports.lua and b/resources/[ERS]/SmartHoseLite/sv_exports.lua differ
diff --git a/resources/[ERS]/SmartHoseLite/sv_hose.lua b/resources/[ERS]/SmartHoseLite/sv_hose.lua
index 37c475191..5f041fdb6 100644
Binary files a/resources/[ERS]/SmartHoseLite/sv_hose.lua and b/resources/[ERS]/SmartHoseLite/sv_hose.lua differ
diff --git a/resources/[ERS]/ebu_flatbeds_ers/.fxap b/resources/[ERS]/ebu_flatbeds_ers/.fxap
index c3dce4805..120c11e6f 100644
Binary files a/resources/[ERS]/ebu_flatbeds_ers/.fxap and b/resources/[ERS]/ebu_flatbeds_ers/.fxap differ
diff --git a/resources/[ERS]/ebu_flatbeds_ers/client/client.lua b/resources/[ERS]/ebu_flatbeds_ers/client/client.lua
index f4b4e5fc0..e82b25be4 100644
Binary files a/resources/[ERS]/ebu_flatbeds_ers/client/client.lua and b/resources/[ERS]/ebu_flatbeds_ers/client/client.lua differ
diff --git a/resources/[ERS]/ebu_flatbeds_ers/config.lua b/resources/[ERS]/ebu_flatbeds_ers/config.lua
index ef0701638..1280acaf5 100644
--- a/resources/[ERS]/ebu_flatbeds_ers/config.lua
+++ b/resources/[ERS]/ebu_flatbeds_ers/config.lua
@@ -81,6 +81,10 @@ Config.flatbed = "flatbed" -- Base game flatbed
Config.flatbedm2 = "flatbedm2" -- https://www.gta5-mods.com/vehicles/freightliner-m2-crew-cab-flatbed-add-on-script-beta
Config.lgc19flatbed = "lgc19flatbed" -- https://forum.cfx.re/t/2019-peterbilt-flatbed/4783413
Config.biftowmfd2 = "biftowmfd2" -- https://mfd.tebex.io/package/6281210
+Config.Gtow = "Gtow" -- https://www.gta5-mods.com/vehicles/peterbilt-337-tuning-by-mfd-fivem
+Config.flatbedm2 = "flatbedm2" -- https://www.gta5-mods.com/vehicles/freightliner-m2-crew-cab-flatbed-add-on-script-beta
+Config.lgc19flatbed = "lgc19flatbed" -- https://forum.cfx.re/t/2019-peterbilt-flatbed/4783413
+Config.biftowmfd2 = "biftowmfd2" -- https://mfd.tebex.io/package/6281210
Config.Gtow = "Gtow"
Config.towy = "towy"
Config.rollback1 = "rollback1"
@@ -101,11 +105,6 @@ Config.gtrailer = "gtrailer"
Config.dotgooseneck = "dotgooseneck"
Config.firef350 = "firef350"
Config.Terrain20tahoe = "Terrain20tahoe"
-
-
-
-
-
-- List any vehicle models here you do not want to be able to be towed
Config.BlacklistedVehs = {
diff --git a/resources/[ERS]/ebu_flatbeds_ers/server/server.lua b/resources/[ERS]/ebu_flatbeds_ers/server/server.lua
index eeec6d14c..9c3557834 100644
Binary files a/resources/[ERS]/ebu_flatbeds_ers/server/server.lua and b/resources/[ERS]/ebu_flatbeds_ers/server/server.lua differ
diff --git a/resources/[ERS]/night_ers/.fxap b/resources/[ERS]/night_ers/.fxap
index 67747aac4..ba4447520 100644
Binary files a/resources/[ERS]/night_ers/.fxap and b/resources/[ERS]/night_ers/.fxap differ
diff --git a/resources/[ERS]/night_ers/NUI/main.js b/resources/[ERS]/night_ers/NUI/main.js
index 38438cfd6..ad40cddc2 100644
--- a/resources/[ERS]/night_ers/NUI/main.js
+++ b/resources/[ERS]/night_ers/NUI/main.js
@@ -1,49 +1,2812 @@
-let resourceName=null,language=null,commands=null,firstnames={},lastnames={},isDisplayingPreCalloutInterface=!1,isDisplayingCalloutInterface=!1,isDragMode=!1,isDragging=!1,dragElement=null,dragOffset={x:0,y:0},isLayoutMode=!1,layoutElements={},visibleLayoutElements=new Set,originalStyles=new Map,layoutBoundaries={left:20,top:20,right:20,bottom:20},snapGrid={enabled:!0,size:10},collisionDetection={enabled:!0},dimensionValidation={enabled:!0};window.addEventListener("message",e=>{let t=e.data;if("playSound"==t.transactionType&&PlayNUISound(t.transactionFolder,t.transactionFile,t.transactionVolume),"init"==t.type&&("undefined"!=typeof $?(resourceName=t.resourcename,language=t.language,commands=t.commands,firstnames=t.firstnames,lastnames=t.lastnames,$.post(`https://${resourceName}/initComplete`,JSON.stringify({complete:!0}))):console.log("Script is still loading...")),"togglecalloutinfo"==t.type&&toggleCalloutInfo(),"toggleUILayoutMode"==t.type&&(console.log("Received toggleUILayoutMode message from Lua"),toggleUILayoutMode()),"emergencyUICleanup"==t.type&&(console.log("Received emergencyUICleanup message from Lua"),emergencyUICleanup()),"ersselectionmenu"==t.type){var a,n,i,o=t.display;ToggleERSSelectionMenu(o,t.permissions)}if("radialmenu"==t.type){var o=t.display;ToggleRadialMenu(o)}if("pursuitradialmenu"==t.type){var o=t.display;TogglePursuitRadialMenu(o)}if("pursuitmode"==t.type){var s,l,o=t.display,r=t.displayhotkeyhint;TogglePursuitModeUI(o,r,t.zoomhotkey,t.backuphotkey)}if("dualsteeringmode"==t.type){var o=t.display;ToggleDualSteeringMode(o)}if("hotkeyhintprompt"==t.type){var o=t.display,c=t.hotkeys,d=t.time;ToggleHotKeyHintPrompt(o,c,language,d)}if("dispatchmessage"==t.type){var u,o=t.display,c=t.hotkeys,g=t.messagetype,p=t.calloutdata,b=t.message,d=t.time;AddMessageToDispatchQueue(o,c,g,p,d,b,t.toggle)}if("calloutprompt"==t.type){var m,o=t.display,p=t.calloutdata,c=t.hotkeys,d=t.time;ToggleCalloutDisplayPrompt(o,c,language,d,p,t.serviceType)}if("calloutpreinterface"==t.type){var o=t.display,c=t.hotkeys,p=t.calloutdata;TogglePreCalloutInterface(o,c,p)}if("calloutinterface"==t.type){var h,y,o=t.display,c=t.hotkeys,f=t.pedCount,v=t.vehicleCount,x=t.objectCount,w=t.propCount,k=t.fireCount,C=t.smokeCount,p=t.calloutdata,I=t.pedOriginalCount,T=t.vehOriginalCount,S=t.objOriginalCount,P=t.propOriginalCount;ToggleCalloutInterface(o,c,f,v,x,w,k,C,p,I,T,S,P,t.fireOriginalCount,t.smokeOriginalCount)}if("unifiedprompt"==t.type){var o=t.display,E=t.promptType,R=t.hotkey,b=t.message,d=t.time;DisplayUnifiedPrompt(o,E,R,b,d)}if("interactionmodule"==t.type){var N,A,o=t.display;DisplayPedInteractionModule(o,t.ispeddeadordying,t.service)}if("idcardprompt"==t.type){var M,_,o=t.display;DisplayPedIdCard(o,t.pedpersonaldata,t.showdatabasecheck)}if("inventoryprompt"==t.type){var q,o=t.display;DisplayPedInventory(o,t.itemlist)}if("vehicleinfoprompt"==t.type){var D,O,o=t.display;DisplayVehicleInfo(o,t.vehicleData,t.DisableTaxAndMOT)}if("togglequestionsmodule"==t.type){var L,o=t.display;ToggleQuestionsModule(o,t.questions)}if("updateanswers"==t.type){var B,o=t.display;UpdateAnswers(o,t.answers)}if("addorremovewaypoint"==t.type&&AddWaypoint(t.addwaypoint),"updatewaypoint"==t.type){var U,j,F,G=t.scaleX;UpdateWaypointPosition(G,t.scaleY,t.distanceText)}if("areyousure"==t.type){var o=t.display;DisplayAreYouSurePopup(o)}if("gearmenu"==t.type){var W,o=t.display;DisplayGearMenu(o,t.locationdata)}if("updatezonechange"==t.type){UpdateZoneChangeUI(t.lastZone,t.currentZone)}"updatewaypoints"===t.type&&updateWaypoints(t)});var unifiedPromptTimeout,idCardTimeout,dbCheckTimeout,vehicleInfoTimeout,vehicleDbCheckTimeout,inventoryTimeout,zoneChangeTimeout=null;let zoneChangeQueue=[],isAnimating=!1;function UpdateZoneChangeUI(e,t){zoneChangeQueue.push({oldZone:e,newZone:t}),isAnimating||processNextZoneChange()}function processNextZoneChange(){if(0===zoneChangeQueue.length){isAnimating=!1;return}isAnimating=!0;let{oldZone:e,newZone:t}=zoneChangeQueue.shift(),a=document.getElementById("zone-change-ui");if(!a){processNextZoneChange();return}let n=a.querySelector(".old-zone"),i=a.querySelector(".new-zone");if(!n||!i){processNextZoneChange();return}zoneChangeTimeout&&clearTimeout(zoneChangeTimeout),a.classList.remove("show"),i.classList.remove("show"),n.textContent=e||"N/A",i.textContent=t||"N/A",a.offsetWidth,a.classList.add("show"),i.classList.add("show"),zoneChangeTimeout=setTimeout(()=>{a.classList.remove("show"),i.classList.remove("show"),setTimeout(processNextZoneChange,500)},4e3)}let escapeEventListenerAdded=!1;function DisplayAreYouSurePopup(e){if(e){$("#are-you-sure-modal").remove();let t=`
${language.CompletingCalloutCannotBeUndone}
`;$("body").append(t),$("#are-you-sure-modal").modal({backdrop:"static",keyboard:!1}),$("#are-you-sure-modal").modal("show"),$("#are-you-sure-modal").on("shown.bs.modal",function(){escapeEventListenerAdded||($(document).on("keydown",function(e){"Escape"===e.key&&($("#are-you-sure-modal").modal("hide"),handleNoClick(),e.stopPropagation())}),escapeEventListenerAdded=!0)}).on("hidden.bs.modal",function(){$(document).off("keydown"),escapeEventListenerAdded=!1})}else $("#are-you-sure-modal").remove()}function handleNoClick(){$.post(`https://${resourceName}/AreYouSure`,JSON.stringify({areyousure:!1})),$("#are-you-sure-modal").modal("hide")}function handleYesClick(){$.post(`https://${resourceName}/AreYouSure`,JSON.stringify({areyousure:!0})),$("#are-you-sure-modal").modal("hide")}function DisplayPedInteractionModule(e,t,a){if(e){if(DisplayUnifiedPrompt(!1),t)$("#ped-interaction-module").empty(),$("#ped-interaction-module").show(),$("#ped-interaction-module").append(` ${language.InteractionOptions} ${[{id:"identify",label:language.IdentifyDeadPed,explanation:language.IdentifyDeadPedExplanation},{id:"drag",label:language.DragPed,explanation:language.DragPedExplanation},{id:"massivebleeding",label:language.CheckMassiveBleeding,explanation:language.CheckMassiveExplanation},{id:"airway",label:language.CheckAirway,explanation:language.CheckAirwayExplanation},{id:"breathing",label:language.CheckBreathing,explanation:language.CheckBreathingExplanation},{id:"circulation",label:language.CheckCirculation,explanation:language.CheckCirculationExplanation},{id:"hypothermia",label:language.CheckHypothermia,explanation:language.CheckHypothermiaExplanation},{id:"cpr",label:language.PerformCPR,explanation:language.PerformCPRExplanation},{id:"putonstretcher",label:language.PutOnStretcher,explanation:language.PutOnStretcherExplanation},{id:"takeoffstretcher",label:language.TakeOffStretcher,explanation:language.TakeOffStretcherExplanation},{id:"bodybag",label:language.PutInBodyBag,explanation:language.PutInBodyBagExplanation}].map(e=>`
${e.label}
${e.explanation}
`).join("")}
${language.ExitInteraction}
`),applySavedPosition("ped-interaction-module"),["identify","drag","massivebleeding","airway","breathing","circulation","hypothermia","cpr","putonstretcher","takeoffstretcher","bodybag","exit"].forEach(e=>addSoundOnHoverEventListener(`p-btn-${e}`));else{$("#ped-interaction-module").empty(),$("#ped-interaction-module").show();var n={police:[{id:"greet",label:language.Greet,explanation:language.GreetExplanation},{id:"id",label:language.AskForId,explanation:language.AskForIdExplanation},{id:"questioning",label:language.Question,explanation:language.QuestionExplanation},{id:"breathalyze",label:language.Breathalyze,explanation:language.BreathalyzeExplanation},{id:"drugtest",label:language.DrugTest,explanation:language.DrugTestExplanation},{id:"warn",label:language.Warn,explanation:language.WarnExplanation},{id:"fine",label:language.Fine,explanation:language.FineExplanation},{id:"grab",label:language.Grab,explanation:language.GrabExplanation},{id:"getout",label:language.GetOut,explanation:language.GetOutExplanation},{id:"follow",label:language.AskToFollow,explanation:language.AskToFollowExplanation},{id:"wait",label:language.AskToWait,explanation:language.AskToWaitExplanation},{id:"handsup",label:language.HandsUp,explanation:language.HandsUpExplanation},{id:"search",label:language.Search,explanation:language.SearchExplanation},{id:"cuff",label:language.Cuff,explanation:language.CuffExplanation},{id:"putinvehicle",label:language.PutInVehicle,explanation:language.PutInVehicleExplanation}],ambulance:[{id:"greet",label:language.Greet,explanation:language.GreetExplanation},{id:"questioning",label:language.Question,explanation:language.QuestionExplanation},{id:"breathalyze",label:language.Breathalyze,explanation:language.BreathalyzeExplanation},{id:"drugtest",label:language.DrugTest,explanation:language.DrugTestExplanation},{id:"getout",label:language.GetOut,explanation:language.GetOutExplanation},{id:"follow",label:language.AskToFollow,explanation:language.AskToFollowExplanation},{id:"wait",label:language.AskToWait,explanation:language.AskToWaitExplanation},{id:"putinvehicle",label:language.PutInVehicle,explanation:language.PutInVehicleExplanation}],fire:[{id:"greet",label:language.Greet,explanation:language.GreetExplanation},{id:"questioning",label:language.Question,explanation:language.QuestionExplanation},{id:"breathalyze",label:language.Breathalyze,explanation:language.BreathalyzeExplanation},{id:"drugtest",label:language.DrugTest,explanation:language.DrugTestExplanation},{id:"getout",label:language.GetOut,explanation:language.GetOutExplanation},{id:"follow",label:language.AskToFollow,explanation:language.AskToFollowExplanation},{id:"wait",label:language.AskToWait,explanation:language.AskToWaitExplanation},{id:"putinvehicle",label:language.PutInVehicle,explanation:language.PutInVehicleExplanation}],tow:[{id:"greet",label:language.Greet,explanation:language.GreetExplanation},{id:"questioning",label:language.Question,explanation:language.QuestionExplanation},{id:"getout",label:language.GetOut,explanation:language.GetOutExplanation},{id:"follow",label:language.AskToFollow,explanation:language.AskToFollowExplanation},{id:"wait",label:language.AskToWait,explanation:language.AskToWaitExplanation},{id:"putinvehicle",label:language.PutInVehicle,explanation:language.PutInVehicleExplanation}]};$("#ped-interaction-module").append(` ${language.InteractionOptions} ${(n[a]||n.police).map(e=>`
${e.label}
${e.explanation}
`).join("")}
${language.StopInteraction} ${language.ExitInteraction} ${"police"===a?`${language.SendToCustody} `:""}
`);let i=n[a]||n.police,o=[...i.map(e=>e.id),"end","exit",..."police"===a?["custody"]:[]];o.forEach(e=>addSoundOnHoverEventListener(`p-btn-${e}`)),applySavedPosition("ped-interaction-module")}}else $("#ped-interaction-module").hide()}function PedInteraction(e){$.post(`https://${resourceName}/pedInteraction`,JSON.stringify({interactionType:e}))}function addSoundOnHoverEventListener(e){document.getElementById(e).addEventListener("mouseenter",function(){PlayNUISound("generic-sounds","rollover",.5)})}function DisplayUnifiedPrompt(e,t,a,n,i){if(e){$("#unified-prompt").show(),applySavedPosition("unified-prompt"),clearTimeout(unifiedPromptTimeout);let o={pedinteraction:{icon:"\uD83D\uDCAC",color:"text-warning",text:language.ToInteract,timeout:1500},injuredped:{icon:"\uD83E\uDE7A",color:"text-danger",text:language.ToPerformCPR,timeout:1500},impound:{icon:"\uD83D\uDE97",color:"text-danger",text:language.ToInteractImpound,timeout:1500},gear:{icon:"\uD83D\uDE97",color:"text-danger",text:language.ToInteractGear,timeout:1500},stretcher:{icon:"\uD83C\uDFE5",color:"text-info",text:language.ToUseStretcherVeh,timeout:1500},objectcleanup:{icon:"\uD83E\uDDF9",color:"text-success",text:language.ToCleanupObject,timeout:1500},spikesvehicle:{icon:"\uD83D\uDCA2",color:"text-warning",text:n||"",timeout:i||1500}},s=o[t]||o.pedinteraction;$("#unified-prompt").html(`
- ${s.icon} ${language.Press} ${a} ${s.text}
- `),unifiedPromptTimeout=setTimeout(function(){$("#unified-prompt").hide()},s.timeout)}else $("#unified-prompt").hide()}function DisplayPedIdCard(e,t,a){if(e){$("#vehicle-info").hide(),$("#ped-interaction-inventory").hide(),clearTimeout(idCardTimeout),clearTimeout(dbCheckTimeout),$("#ped-interaction-id-card").empty();var n=t.ProfilePicture||"N/A",i=t.FirstName||"N/A",o=t.LastName||"N/A",s=t.DOB||"N/A",l=t.Gender||"N/A",r=t.Email||"N/A",c=t.PhoneNumber||"N/A",d=t.Country||"N/A",u=t.State||"N/A",g=t.City||"N/A",p=t.PostalCode||"N/A",b=t.Address||"N/A";t.AddressType;var m=t.Nationality||"N/A",h=t.FlagsOrMarkers,y=t.License_Car||"N/A",f=t.License_Car_Colour||"text-light",v=t.License_Car_Icon||"fas fa-car",x=t.License_Bike||"N/A",w=t.License_Bike_Colour||"text-light",k=t.License_Bike_Icon||"fas fa-bicycle",C=t.License_Boat||"N/A",I=t.License_Boat_Colour||"text-light",T=t.License_Boat_Icon||"fas fa-ship",S=t.License_Truck||"N/A",P=t.License_Truck_Colour||"text-light",E=t.License_Truck_Icon||"fas fa-truck",R=t.License_Pilot||"N/A",N=t.License_Pilot_Colour||"text-light",A=t.License_Pilot_Icon||"fas fa-plane";$("#ped-interaction-id-card").append(` ${language.FullName} ${i} ${o} ${language.DOB} ${s} `),$("#ped-interaction-id-card").show(),applySavedPosition("ped-interaction-id-card"),a&&($("#ped-interaction-id-card").append(` Loading...
${language.RunningDatabaseCheck}
`),dbCheckTimeout=setTimeout(function(){$("#initial-check").remove(),$("#database-check").html(` ${i} ${o}
(${s})
${language.Nationality} ${m} ${language.Gender} ${l} ${language.Address} ${b}, ${g}, ${u} ${p} (${d}) ${language.Email} ${r} | ${language.PhoneNumber} ${c} ${language.Licenses} ${y} ${x} ${C} ${S} ${R}
${h&&Object.keys(h).some(e=>h[e])?`
${language.FlagsOrMarkers} ${h.armed_and_dangerous?` ${language.armed_and_dangerous} `:""} ${h.assault?` ${language.assault} `:""} ${h.burglary?` ${language.burglary} `:""} ${h.drug_related?` ${language.drug_related} `:""} ${h.gang_affiliation?` ${language.gang_affiliation} `:""} ${h.homicide?` ${language.homicide} `:""} ${h.kidnapping?` ${language.kidnapping} `:""} ${h.mental_health_issues?` ${language.mental_health_issues} `:""} ${h.sex_offense?` ${language.sex_offense} `:""} ${h.terrorism?` ${language.terrorism} `:""} ${h.theft?` ${language.theft} `:""} ${h.traffic_violation?` ${language.traffic_violation} `:""} ${h.wanted_person?` ${language.wanted_person} `:""} ${h.other?` ${language.other} `:""} ${h.active_warrant?` ${language.active_warrant} `:""}
`:""}
`)},3e3)),idCardTimeout=setTimeout(function(){$("#ped-interaction-id-card").hide()},15e3)}else $("#ped-interaction-id-card").hide()}function DisplayVehicleInfo(e,t,a){if(e){$("#ped-interaction-id-card").hide(),$("#ped-interaction-inventory").hide(),clearTimeout(vehicleInfoTimeout),clearTimeout(vehicleDbCheckTimeout),$("#vehicle-info").empty();var n={vehNetId:t.vehNetId,license_plate:t.license_plate,model:t.model,model_hash:t.model_hash,vehicle_class:t.vehicle_class,vehicle_class_from_name:t.vehicle_class_from_name,color:t.color,color_secondary:t.color_secondary,build_year:t.build_year,tax:t.tax,mot:t.mot,insurance:t.insurance,stolen:t.stolen,bolo:t.bolo,bolo_description:t.bolo_description,owner_name:t.owner_name};$("#vehicle-info").append(` ${language.VehiclePlate} ${n.license_plate} ${language.VehicleModel} ${n.model} `),$("#vehicle-info").append(` Loading...
${language.RunningDatabaseCheck}
`),$("#vehicle-info").show(),applySavedPosition("vehicle-info"),vehicleDbCheckTimeout=setTimeout(function(){$("#vehicle-info-database-check").html(` `)},5e3),vehicleInfoTimeout=setTimeout(function(){$("#vehicle-info").hide()},15e3)}else $("#vehicle-info").hide()}function DisplayPedInventory(e,t){e?($("#ped-interaction-id-card").hide(),$("#vehicle-info").hide(),clearTimeout(inventoryTimeout),$("#ped-interaction-inventory").empty(),$("#ped-interaction-inventory").append(` ${language.Inventory} `),$.each(t,function(e,t){var a,n=` ${t.name} `;$("#inventory-table-body").append(n)}),$("#ped-interaction-inventory").show(),applySavedPosition("ped-interaction-inventory"),inventoryTimeout=setTimeout(function(){$("#ped-interaction-inventory").hide()},15e3)):$("#ped-interaction-inventory").hide()}var listener=!1;function setupButton(e,t,a){let n=document.getElementById(e);$(`#${e}`).html(` `),n.addEventListener("mouseenter",()=>PlayNUISound("generic-sounds","rollover",.5)),n.addEventListener("mouseover",()=>{document.getElementById("instruction-text").style.display="block",$("#instruction-text").html(`${a}
`)}),n.addEventListener("mouseout",()=>document.getElementById("instruction-text").style.display="none")}function setupRadialMenu(){if(!listener){listener=!0;let e=[{id:"middle-x",iconClass:"",instruction:language.ExitRadialMenuInstruction,hoverColour:"",function:"ToggleRadialMenu(false)"},{id:"police-transport",iconClass:language.RequestPoliceTransportIcon,instruction:language.RequestPoliceTransportInstruction,hoverColour:"#0057b9",function:"RequestOrCancelPoliceTransport()"},{id:"ambulance-request",iconClass:language.RequestAmbulanceIcon,instruction:language.RequestAmbulanceInstruction,hoverColour:"#ff0000",function:"RequestOrCancelAmbulance()"},{id:"tow-request",iconClass:language.RequestTowIcon,instruction:language.RequestTowInstruction,hoverColour:"#782323",function:"RequestOrCancelTow()"},{id:"taxi-request",iconClass:language.RequestTaxiIcon,instruction:language.RequestTaxiInstruction,hoverColour:"#FF9A18",function:"RequestOrCancelTaxi()"},{id:"road-service-request",iconClass:language.RequestRoadServiceIcon,instruction:language.RequestRoadServiceInstruction,hoverColour:"#ffbf00",function:"RequestOrCancelRoadService()"},{id:"coroner-request",iconClass:language.RequestCoronerIcon,instruction:language.RequestCoronerInstruction,hoverColour:"#3D3D3D",function:"RequestOrCancelCoroner()"},{id:"animal-rescue-request",iconClass:language.RequestAnimalRescueIcon,instruction:language.RequestAnimalRescueInstruction,hoverColour:"#9bde00",function:"RequestOrCancelAnimalRescue()"},{id:"mechanic-request",iconClass:language.RequestMechanicIcon,instruction:language.RequestMechanicInstruction,hoverColour:"#3D3D3D",function:"RequestOrCancelMechanic()"},{id:"fire-request",iconClass:language.RequestFireIcon,instruction:language.RequestFireInstruction,hoverColour:"#3D3D3D",function:"RequestOrCancelFire()"}];$("#circle-menu").html(`
-
+// Global values
+let resourceName = null;
+let language = null;
+let commands = null;
+let firstnames = {};
+let lastnames = {};
+
+let isDisplayingPreCalloutInterface = false;
+let isDisplayingCalloutInterface = false;
+
+// Drag functionality variables
+let isDragMode = false;
+let isDragging = false;
+let dragElement = null;
+let dragOffset = { x: 0, y: 0 };
+
+// UI Layout Mode variables
+let isLayoutMode = false;
+let layoutElements = {};
+let visibleLayoutElements = new Set();
+
+// Enhanced layout system variables
+let originalStyles = new Map(); // Store original styles before layout mode
+let layoutBoundaries = { left: 20, top: 20, right: 20, bottom: 20 }; // Screen boundaries
+let snapGrid = { enabled: true, size: 10 }; // Grid snapping
+let collisionDetection = { enabled: true }; // Element collision detection
+let dimensionValidation = { enabled: true }; // Validate element dimensions for real vs placeholder content
+
+window.addEventListener('message', (event) => {
+ const data = event.data;
+
+ if (data.transactionType == "playSound") {
+ PlayNUISound(data.transactionFolder, data.transactionFile, data.transactionVolume)
+ }
+
+ if (data.type == "init") {
+ if (typeof $ !== 'undefined') {
+ resourceName = data.resourcename
+ language = data.language
+ commands = data.commands
+ firstnames = data.firstnames
+ lastnames = data.lastnames
+
+ $.post(`https://${resourceName}/initComplete`, JSON.stringify({
+ complete: true
+ }));
+ } else {
+ console.log("Script is still loading...")
+ };
+ }
+
+ if (data.type == "togglecalloutinfo") {
+ toggleCalloutInfo();
+ }
+
+ if (data.type == "toggleUILayoutMode") {
+ console.log("Received toggleUILayoutMode message from Lua");
+ toggleUILayoutMode();
+ }
+
+ if (data.type == "emergencyUICleanup") {
+ console.log("Received emergencyUICleanup message from Lua");
+ clearModuleData();
+ emergencyUICleanup();
+ }
+
+ if (data.type == "clearInformationModulesOnShiftEnd") {
+ clearModuleData();
+ }
+
+ if (data.type == "ersselectionmenu") {
+ var display = data.display
+ var permissions = data.permissions
+ ToggleERSSelectionMenu(display, permissions)
+ }
+
+ if (data.type == "radialmenu") {
+ var display = data.display
+ ToggleRadialMenu(display)
+ }
+
+ if (data.type == "pursuitradialmenu") {
+ var display = data.display
+ TogglePursuitRadialMenu(display)
+ }
+
+ if (data.type == "pursuitmode") {
+ var display = data.display
+ var displayhotkeyhint = data.displayhotkeyhint
+ var zoomhotkey = data.zoomhotkey
+ var backuphotkey = data.backuphotkey
+ TogglePursuitModeUI(display, displayhotkeyhint, zoomhotkey, backuphotkey)
+ }
+
+ if (data.type == "dualsteeringmode") {
+ var display = data.display
+ ToggleDualSteeringMode(display)
+ }
+
+ if (data.type == "hotkeyhintprompt") {
+ var display = data.display
+ var hotkeys = data.hotkeys
+ var time = data.time
+ ToggleHotKeyHintPrompt(display, hotkeys, language, time)
+ }
+
+ if (data.type == "dispatchmessage") {
+ var display = data.display
+ var hotkeys = data.hotkeys
+ var messagetype = data.messagetype
+ var calloutdata = data.calloutdata
+ var message = data.message
+ var time = data.time
+ var toggle = data.toggle
+ AddMessageToDispatchQueue(display, hotkeys, messagetype, calloutdata, time, message, toggle)
+ }
+
+ if (data.type == "calloutprompt") {
+ var display = data.display
+ var calloutdata = data.calloutdata
+ var hotkeys = data.hotkeys
+ var time = data.time
+ var serviceType = data.serviceType
+ ToggleCalloutDisplayPrompt(display, hotkeys, language, time, calloutdata, serviceType)
+ }
+
+ if (data.type == "calloutpreinterface") {
+ var display = data.display
+ var hotkeys = data.hotkeys
+ var calloutdata = data.calloutdata
+ TogglePreCalloutInterface(display, hotkeys, calloutdata)
+ }
+
+ if (data.type == "calloutinterface") {
+ var display = data.display
+ var hotkeys = data.hotkeys
+ var pedCount = data.pedCount
+ var vehicleCount = data.vehicleCount
+ var objectCount = data.objectCount
+ var propCount = data.propCount
+ var fireCount = data.fireCount
+ var smokeCount = data.smokeCount
+
+ var calloutdata = data.calloutdata
+
+ var pedOriginalCount = data.pedOriginalCount
+ var vehOriginalCount = data.vehOriginalCount
+ var objOriginalCount = data.objOriginalCount
+ var propOriginalCount = data.propOriginalCount
+ var fireOriginalCount = data.fireOriginalCount
+ var smokeOriginalCount = data.smokeOriginalCount
+
+ ToggleCalloutInterface(display, hotkeys,pedCount, vehicleCount, objectCount, propCount, fireCount, smokeCount, calloutdata, pedOriginalCount, vehOriginalCount, objOriginalCount, propOriginalCount, fireOriginalCount, smokeOriginalCount)
+ }
+
+ if (data.type == "unifiedprompt") {
+ var display = data.display
+ var promptType = data.promptType
+ var hotkey = data.hotkey
+ var message = data.message
+ var time = data.time
+ DisplayUnifiedPrompt(display, promptType, hotkey, message, time)
+ }
+
+ if (data.type == "interactionmodule") {
+ var display = data.display
+ var isDead = data.ispeddeadordying
+ var service = data.service
+ DisplayPedInteractionModule(display, isDead, service)
+ }
+
+ if (data.type == "idcardprompt") {
+ var display = data.display
+ var personaldata = data.pedpersonaldata
+ var showdatabasecheck = data.showdatabasecheck
+ DisplayPedIdCard(display, personaldata, showdatabasecheck)
+ }
+
+ if (data.type == "inventoryprompt") {
+ var display = data.display
+ var itemList = data.itemlist
+ DisplayPedInventory(display, itemList)
+ }
+
+ if(data.type == "vehicleinfoprompt") {
+ var display = data.display
+ var vehicleData = data.vehicleData
+ var DisableTaxAndMOT = data.DisableTaxAndMOT
+ DisplayVehicleInfo(display, vehicleData, DisableTaxAndMOT)
+ }
+
+ if (data.type == "redisplaylastmodule") {
+ redisplayLastModule()
+ }
+
+ if (data.type == "togglequestionsmodule") {
+ var display = data.display
+ var questions = data.questions
+ ToggleQuestionsModule(display, questions)
+ }
+
+ if (data.type == "updateanswers") {
+ var display = data.display
+ var answers = data.answers
+ UpdateAnswers(display, answers)
+ }
+
+ if (data.type == "addorremovewaypoint") {
+ var addOrRemove = data.addwaypoint
+ AddWaypoint(addOrRemove);
+ }
+
+ if (data.type == "updatewaypoint") {
+ var scaleX = data.scaleX
+ var scaleY = data.scaleY
+ var distanceText = data.distanceText
+ UpdateWaypointPosition(scaleX, scaleY, distanceText);
+ }
+
+ if (data.type == "areyousure") {
+ var display = data.display
+ DisplayAreYouSurePopup(display)
+ }
+
+ if (data.type == "gearmenu") {
+ var display = data.display
+ var locationData = data.locationdata
+ DisplayGearMenu(display, locationData)
+ }
+
+ if (data.type == "updatezonechange") {
+ var lastZone = data.lastZone
+ var currentZone = data.currentZone
+ UpdateZoneChangeUI(lastZone, currentZone)
+ }
+
+ if (data.type === 'updatewaypoints') {
+ updateWaypoints(data);
+ }
+});
+
+// FUNCTIONS
+
+var zoneChangeTimeout = null;
+let zoneChangeQueue = [];
+let isAnimating = false;
+
+function UpdateZoneChangeUI(oldZone, newZone) {
+ zoneChangeQueue.push({ oldZone, newZone });
+ if (!isAnimating) {
+ processNextZoneChange();
+ }
+}
+
+function processNextZoneChange() {
+ if (zoneChangeQueue.length === 0) {
+ isAnimating = false;
+ return;
+ }
+
+ isAnimating = true;
+ const { oldZone, newZone } = zoneChangeQueue.shift();
+
+ const zoneChangeUI = document.getElementById('zone-change-ui');
+ if (!zoneChangeUI) {
+ processNextZoneChange();
+ return;
+ }
+
+ const oldZoneElement = zoneChangeUI.querySelector('.old-zone');
+ const newZoneElement = zoneChangeUI.querySelector('.new-zone');
+
+ if (!oldZoneElement || !newZoneElement) {
+ processNextZoneChange();
+ return;
+ }
+
+ // Clear any existing timeout
+ if (zoneChangeTimeout) {
+ clearTimeout(zoneChangeTimeout);
+ }
+
+ // Reset the animation state
+ zoneChangeUI.classList.remove('show');
+ newZoneElement.classList.remove('show');
+
+ // Set the new zone values
+ oldZoneElement.textContent = oldZone ? oldZone : "N/A";
+ newZoneElement.textContent = newZone ? newZone : "N/A";
+
+ // Trigger reflow to ensure the animation restarts
+ void zoneChangeUI.offsetWidth;
+
+ // Start the new animation
+ zoneChangeUI.classList.add('show');
+ newZoneElement.classList.add('show');
+
+ // Set a new timeout
+ zoneChangeTimeout = setTimeout(() => {
+ zoneChangeUI.classList.remove('show');
+ newZoneElement.classList.remove('show');
+
+ // Process the next zone change after the animation is complete
+ setTimeout(processNextZoneChange, 500); // Add a small delay between animations
+ }, 4000);
+}
+
+let escapeEventListenerAdded = false;
+
+function DisplayAreYouSurePopup(display) {
+ if (display) {
+ // Remove any previous modals
+ $('#are-you-sure-modal').remove();
+
+ // HTML
+ const modal = `
+
+
+
+
+
+
${language.CompletingCalloutCannotBeUndone}
+
+
+
+
+
+ `;
+
+ $('body').append(modal);
+
+ // Initialize the modal with static backdrop and disabled keyboard interaction
+ $('#are-you-sure-modal').modal({
+ backdrop: 'static',
+ keyboard: false
+ });
+
+ // Show the modal explicitly
+ $('#are-you-sure-modal').modal('show');
+
+ // Handle Escape key press only when the modal is open
+ $('#are-you-sure-modal').on('shown.bs.modal', function() {
+ if (!escapeEventListenerAdded) {
+ $(document).on('keydown', function(event) {
+ if (event.key === "Escape") {
+ // Close the modal
+ $('#are-you-sure-modal').modal('hide');
+ // Trigger the same action as the "No" button click
+ handleNoClick();
+ // Ensure the event doesn't propagate further
+ event.stopPropagation();
+ }
+ });
+ escapeEventListenerAdded = true;
+ }
+ }).on('hidden.bs.modal', function() {
+ // Remove the Escape key press event listener when the modal is closed
+ $(document).off('keydown');
+ escapeEventListenerAdded = false;
+ });
+
+ } else {
+ $('#are-you-sure-modal').remove();
+ }
+}
+
+
+// Function to handle 'No' button click
+function handleNoClick() {
+ $.post(`https://${resourceName}/AreYouSure`, JSON.stringify({
+ areyousure: false
+ }));
+ $('#are-you-sure-modal').modal('hide');
+}
+
+// Function to handle 'Yes' button click
+function handleYesClick() {
+ $.post(`https://${resourceName}/AreYouSure`, JSON.stringify({
+ areyousure: true
+ }));
+ $('#are-you-sure-modal').modal('hide');
+}
+
+function DisplayPedInteractionModule(display, isDead, service) { // service can be: police, fire, ambulance or tow.
+ if (display) {
+ DisplayUnifiedPrompt(false)
+ if (isDead) {
+ $("#ped-interaction-module").empty();
+ $("#ped-interaction-module").show();
+
+ //HTML
+ $("#ped-interaction-module").append(`
+ ${language.InteractionOptions}
+
+
+
+
+ ${[
+ { id: 'identify', label: language.IdentifyDeadPed, explanation: language.IdentifyDeadPedExplanation },
+ { id: 'drag', label: language.DragPed, explanation: language.DragPedExplanation },
+ { id: 'massivebleeding', label: language.CheckMassiveBleeding, explanation: language.CheckMassiveExplanation },
+ { id: 'airway', label: language.CheckAirway, explanation: language.CheckAirwayExplanation },
+ { id: 'breathing', label: language.CheckBreathing, explanation: language.CheckBreathingExplanation },
+ { id: 'circulation', label: language.CheckCirculation, explanation: language.CheckCirculationExplanation },
+ { id: 'hypothermia', label: language.CheckHypothermia, explanation: language.CheckHypothermiaExplanation },
+ { id: 'cpr', label: language.PerformCPR, explanation: language.PerformCPRExplanation },
+ { id: 'putonstretcher', label: language.PutOnStretcher, explanation: language.PutOnStretcherExplanation },
+ { id: 'takeoffstretcher', label: language.TakeOffStretcher, explanation: language.TakeOffStretcherExplanation },
+ { id: 'bodybag', label: language.PutInBodyBag, explanation: language.PutInBodyBagExplanation }
+ ].map(item => `
+
+
+ ${item.label}
+
+
+ ${item.explanation}
+
+
+ `).join('')}
+
+
+
+
+
+
+
+ ${language.ExitInteraction}
+
+
+
+ `);
+
+ // Apply sound effects to all buttons
+ const buttonIds = [
+ 'identify', 'drag', 'massivebleeding', 'airway', 'breathing', 'circulation', 'hypothermia', 'cpr', 'putonstretcher', 'takeoffstretcher', 'bodybag',
+ 'exit'
+ ];
+ buttonIds.forEach(id => addSoundOnHoverEventListener(`p-btn-${id}`));
+
+ } else {
+ $("#ped-interaction-module").empty();
+ $("#ped-interaction-module").show();
+
+ // Per service we need to show different buttons.
+ var buttonsPerService = {
+ police: [
+ { id: 'greet', label: language.Greet, explanation: language.GreetExplanation },
+ { id: 'id', label: language.AskForId, explanation: language.AskForIdExplanation },
+ { id: 'questioning', label: language.Question, explanation: language.QuestionExplanation },
+ { id: 'breathalyze', label: language.Breathalyze, explanation: language.BreathalyzeExplanation },
+ { id: 'drugtest', label: language.DrugTest, explanation: language.DrugTestExplanation },
+ { id: 'warn', label: language.Warn, explanation: language.WarnExplanation },
+ { id: 'fine', label: language.Fine, explanation: language.FineExplanation },
+ { id: 'grab', label: language.Grab, explanation: language.GrabExplanation },
+ { id: 'getout', label: language.GetOut, explanation: language.GetOutExplanation },
+ { id: 'follow', label: language.AskToFollow, explanation: language.AskToFollowExplanation },
+ { id: 'wait', label: language.AskToWait, explanation: language.AskToWaitExplanation },
+ { id: 'handsup', label: language.HandsUp, explanation: language.HandsUpExplanation },
+ { id: 'search', label: language.Search, explanation: language.SearchExplanation },
+ { id: 'cuff', label: language.Cuff, explanation: language.CuffExplanation },
+ { id: 'putinvehicle', label: language.PutInVehicle, explanation: language.PutInVehicleExplanation }
+ ],
+ ambulance: [
+ { id: 'greet', label: language.Greet, explanation: language.GreetExplanation },
+ { id: 'questioning', label: language.Question, explanation: language.QuestionExplanation },
+ { id: 'breathalyze', label: language.Breathalyze, explanation: language.BreathalyzeExplanation },
+ { id: 'drugtest', label: language.DrugTest, explanation: language.DrugTestExplanation },
+ { id: 'getout', label: language.GetOut, explanation: language.GetOutExplanation },
+ { id: 'follow', label: language.AskToFollow, explanation: language.AskToFollowExplanation },
+ { id: 'wait', label: language.AskToWait, explanation: language.AskToWaitExplanation },
+ { id: 'putinvehicle', label: language.PutInVehicle, explanation: language.PutInVehicleExplanation }
+ ],
+ fire: [
+ { id: 'greet', label: language.Greet, explanation: language.GreetExplanation },
+ { id: 'questioning', label: language.Question, explanation: language.QuestionExplanation },
+ { id: 'breathalyze', label: language.Breathalyze, explanation: language.BreathalyzeExplanation },
+ { id: 'drugtest', label: language.DrugTest, explanation: language.DrugTestExplanation },
+ { id: 'getout', label: language.GetOut, explanation: language.GetOutExplanation },
+ { id: 'follow', label: language.AskToFollow, explanation: language.AskToFollowExplanation },
+ { id: 'wait', label: language.AskToWait, explanation: language.AskToWaitExplanation },
+ { id: 'putinvehicle', label: language.PutInVehicle, explanation: language.PutInVehicleExplanation }
+ ],
+ tow: [
+ { id: 'greet', label: language.Greet, explanation: language.GreetExplanation },
+ { id: 'questioning', label: language.Question, explanation: language.QuestionExplanation },
+ { id: 'getout', label: language.GetOut, explanation: language.GetOutExplanation },
+ { id: 'follow', label: language.AskToFollow, explanation: language.AskToFollowExplanation },
+ { id: 'wait', label: language.AskToWait, explanation: language.AskToWaitExplanation },
+ { id: 'putinvehicle', label: language.PutInVehicle, explanation: language.PutInVehicleExplanation }
+ ]
+ }
+
+ // HTML
+ $("#ped-interaction-module").append(`
+ ${language.InteractionOptions}
+
+
+
+
+ ${(buttonsPerService[service] || buttonsPerService.police).map(item => `
+
+
+ ${item.label}
+
+
+ ${item.explanation}
+
+
+ `).join('')}
+
+
+
+
+
+
+
+ ${language.StopInteraction}
+ ${language.ExitInteraction}
+ ${service === 'police' ? `${language.SendToCustody} ` : ''}
+
+
+
+ `);
+
+ // Apply sound on hover to all buttons
+ const serviceButtons = buttonsPerService[service] || buttonsPerService.police;
+ const buttonIds = [
+ ...serviceButtons.map(item => item.id),
+ 'end', 'exit',
+ ...(service === 'police' ? ['custody'] : [])
+ ];
+ buttonIds.forEach(id => addSoundOnHoverEventListener(`p-btn-${id}`));
+ }
+ } else {
+ $("#ped-interaction-module").hide();
+ }
+}
+
+function PedInteraction(intType) {
+ $.post(`https://${resourceName}/pedInteraction`, JSON.stringify({
+ interactionType: intType
+ }))
+}
+
+function addSoundOnHoverEventListener(buttonId) {
+ var button = document.getElementById(buttonId);
+ button.addEventListener('mouseenter', function() {
+ PlayNUISound('generic-sounds', "rollover", 0.5);
+ });
+}
+
+var unifiedPromptTimeout;
+
+function DisplayUnifiedPrompt(display, promptType, hotkey, message, time) {
+ if (display) {
+ $("#unified-prompt").show();
+
+ clearTimeout(unifiedPromptTimeout);
+
+ // Define prompt configurations
+ const promptConfigs = {
+ 'pedinteraction': {
+ icon: '๐ฌ',
+ color: 'text-warning',
+ text: language.ToInteract,
+ timeout: 1500
+ },
+ 'injuredped': {
+ icon: '๐ฉบ',
+ color: 'text-danger',
+ text: language.ToPerformCPR,
+ timeout: 1500
+ },
+ 'impound': {
+ icon: '๐',
+ color: 'text-danger',
+ text: language.ToInteractImpound,
+ timeout: 1500
+ },
+ 'gear': {
+ icon: '๐',
+ color: 'text-danger',
+ text: language.ToInteractGear,
+ timeout: 1500
+ },
+ 'stretcher': {
+ icon: '๐ฅ',
+ color: 'text-info',
+ text: language.ToUseStretcherVeh,
+ timeout: 1500
+ },
+ 'objectcleanup': {
+ icon: '๐งน',
+ color: 'text-success',
+ text: language.ToCleanupObject,
+ timeout: 1500
+ },
+ 'spikesvehicle': {
+ icon: '๐ข',
+ color: 'text-warning',
+ text: message || '',
+ timeout: time || 1500
+ }
+ };
+
+ const config = promptConfigs[promptType] || promptConfigs['pedinteraction'];
+
+ $("#unified-prompt").html(`
+ ${config.icon} ${language.Press} ${hotkey} ${config.text}
+ `);
+
+ unifiedPromptTimeout = setTimeout(function() {
+ $("#unified-prompt").hide();
+ }, config.timeout);
+
+ } else {
+ $("#unified-prompt").hide();
+ }
+}
+
+// Track last displayed module for TAB key re-display
+var lastDisplayedModule = null; // 'idcard', 'vehicle', or 'inventory'
+var lastIdCardData = null;
+var lastVehicleData = null;
+var lastInventoryData = null;
+
+// Track database check completion states for each module
+var idCardDbCheckCompleted = false;
+var vehicleDbCheckCompleted = false;
+
+var idCardTimeout;
+var vehicleInfoTimeout;
+var dbCheckTimeout;
+var vehicleDbCheckTimeout;
+var moduleClearTimeout; // Timeout to clear module data if hidden for > 1 minute
+var moduleProgressInterval; // Interval to update progress bar
+var moduleClearStartTime; // Timestamp when module clear timer started
+
+function DisplayPedIdCard(display, personaldata, showdatabasecheck, skipDbCheckAnimation) {
+ if (display) {
+ // Hide vehicle info.
+ $("#vehicle-info").hide();
+ // Hide inventory
+ $("#ped-interaction-inventory").hide();
+
+ clearTimeout(idCardTimeout);
+
+ // Only clear dbCheckTimeout if we're not preserving state
+ if (!skipDbCheckAnimation) {
+ clearTimeout(dbCheckTimeout);
+ idCardDbCheckCompleted = false;
+ }
+
+ $("#ped-interaction-id-card").empty();
+
+ var profilePicture = personaldata.ProfilePicture || 'N/A';
+ var firstName = personaldata.FirstName || 'N/A';
+ var lastName = personaldata.LastName || 'N/A';
+ var dob = personaldata.DOB || 'N/A';
+ var gender = personaldata.Gender || 'N/A';
+ var email = personaldata.Email || 'N/A';
+ var phone = personaldata.PhoneNumber || 'N/A';
+ var country = personaldata.Country || 'N/A';
+ var state = personaldata.State || 'N/A';
+ var city = personaldata.City || 'N/A';
+ var postalcode = personaldata.PostalCode || 'N/A';
+ var address = personaldata.Address || 'N/A';
+ var addressType = personaldata.AddressType || 'N/A';
+ var nationality = personaldata.Nationality || 'N/A';
+ var flagsOrMarkers = personaldata.FlagsOrMarkers // This is a list of keys which are true or false, I want each one to be displayed....
+ var driversLicense = personaldata.License_Car || 'N/A';
+ var driversLicenseColour = personaldata.License_Car_Colour || 'text-light';
+ var driversLicenseIcon = personaldata.License_Car_Icon || 'fas fa-car';
+ var bikeLicense = personaldata.License_Bike || 'N/A';
+ var bikeLicenseColour = personaldata.License_Bike_Colour || 'text-light';
+ var bikeLicenseIcon = personaldata.License_Bike_Icon || 'fas fa-bicycle';
+ var boatLicense = personaldata.License_Boat || 'N/A';
+ var boatLicenseColour = personaldata.License_Boat_Colour || 'text-light';
+ var boatLicenseIcon = personaldata.License_Boat_Icon || 'fas fa-ship';
+ var truckLicense = personaldata.License_Truck || 'N/A';
+ var truckLicenseColour = personaldata.License_Truck_Colour || 'text-light';
+ var truckLicenseIcon = personaldata.License_Truck_Icon || 'fas fa-truck';
+ var pilotLicense = personaldata.License_Pilot || 'N/A';
+ var pilotLicenseColour = personaldata.License_Pilot_Colour || 'text-light';
+ var pilotLicenseIcon = personaldata.License_Pilot_Icon || 'fas fa-plane';
+
+ // Store data for TAB key re-display
+ lastDisplayedModule = 'idcard';
+ lastIdCardData = {
+ personaldata: personaldata,
+ showdatabasecheck: showdatabasecheck
+ };
+
+ // Modern Bootstrap 5.2.3 Card Design
+ // DON'T use isolation: isolate - it causes text blurriness
+ $("#ped-interaction-id-card").append(`
+
+
+
+
+ ${language.FullName}
+ ${firstName} ${lastName}
+
+
+ ${language.DOB}
+ ${dob}
+
+
+
+ `);
+
+ $("#ped-interaction-id-card").show();
+
+ if (showdatabasecheck) {
+ // Always create the database-check container first
+ // DON'T use isolation: isolate on the card itself - it causes text blurriness
+ $("#ped-interaction-id-card").append(`
+
+
+ `);
+
+ // Check if database check was already completed
+ if (idCardDbCheckCompleted && skipDbCheckAnimation) {
+ // Show completed results immediately
+ renderIdCardDatabaseResults(personaldata, firstName, lastName, dob, profilePicture, nationality, gender, address, city, state, postalcode, country, email, phone, driversLicense, driversLicenseColour, bikeLicense, bikeLicenseColour, boatLicense, boatLicenseColour, truckLicense, truckLicenseColour, pilotLicense, pilotLicenseColour, flagsOrMarkers);
+ } else {
+ // Show loading spinner
+ $("#database-check").html(`
+
+
+
+
+
+
${language.RunningDatabaseCheck}
+
+
+ `);
+
+ // Delay before results & Display results
+ dbCheckTimeout = setTimeout(function() {
+ idCardDbCheckCompleted = true;
+ $("#initial-check").remove();
+ renderIdCardDatabaseResults(personaldata, firstName, lastName, dob, profilePicture, nationality, gender, address, city, state, postalcode, country, email, phone, driversLicense, driversLicenseColour, bikeLicense, bikeLicenseColour, boatLicense, boatLicenseColour, truckLicense, truckLicenseColour, pilotLicense, pilotLicenseColour, flagsOrMarkers);
+ }, 3000);
+ }
+ }
+
+ // Hide the ID card after a certain period
+ idCardTimeout = setTimeout(function() {
+ $("#ped-interaction-id-card").hide();
+ updateModuleIndicator();
+ scheduleModuleClearIfHidden();
+ }, 15000);
+
+ updateModuleIndicator();
+
+ } else {
+ $("#ped-interaction-id-card").hide();
+ updateModuleIndicator();
+ }
+}
+
+// Separate function to render ID card database results
+function renderIdCardDatabaseResults(personaldata, firstName, lastName, dob, profilePicture, nationality, gender, address, city, state, postalcode, country, email, phone, driversLicense, driversLicenseColour, bikeLicense, bikeLicenseColour, boatLicense, boatLicenseColour, truckLicense, truckLicenseColour, pilotLicense, pilotLicenseColour, flagsOrMarkers) {
+ // Completely clear and replace content to reset rendering context
+ var dbCheckElement = document.getElementById('database-check');
+ if (dbCheckElement) {
+ // Remove all content including loading indicator
+ dbCheckElement.innerHTML = '';
+ // Force a reflow to clear any rendering contexts
+ void dbCheckElement.offsetHeight;
+ }
+
+ $("#database-check").html(`
+
+
+
+
+
+
${firstName} ${lastName}
+ ${dob}
+
+
+
+
+
+
+
+ ${language.Nationality}
+ ${nationality}
+
+
+
+
+ ${language.Gender}
+ ${gender}
+
+
+
+
+ ${language.Address}
+ ${address}, ${city} ${state} ${postalcode}, ${country}
+
+
+
+
+ ${language.Email}
+ ${email}
+
+
+ ${language.PhoneNumber}
+ ${phone}
+
+
+
+
+
+
+
+
${language.Licenses}
+
+
+ ${driversLicense}
+
+
+ ${bikeLicense}
+
+
+ ${boatLicense}
+
+
+ ${truckLicense}
+
+
+ ${pilotLicense}
+
+
+
+
+ ${flagsOrMarkers && Object.keys(flagsOrMarkers).some(key => flagsOrMarkers[key]) ? `
+
+
${language.FlagsOrMarkers}
+
+ ${flagsOrMarkers.armed_and_dangerous ? `
${language.armed_and_dangerous}
` : ''}
+ ${flagsOrMarkers.assault ? `
${language.assault}
` : ''}
+ ${flagsOrMarkers.burglary ? `
${language.burglary}
` : ''}
+ ${flagsOrMarkers.drug_related ? `
${language.drug_related}
` : ''}
+ ${flagsOrMarkers.gang_affiliation ? `
${language.gang_affiliation}
` : ''}
+ ${flagsOrMarkers.homicide ? `
${language.homicide}
` : ''}
+ ${flagsOrMarkers.kidnapping ? `
${language.kidnapping}
` : ''}
+ ${flagsOrMarkers.mental_health_issues ? `
${language.mental_health_issues}
` : ''}
+ ${flagsOrMarkers.sex_offense ? `
${language.sex_offense}
` : ''}
+ ${flagsOrMarkers.terrorism ? `
${language.terrorism}
` : ''}
+ ${flagsOrMarkers.theft ? `
${language.theft}
` : ''}
+ ${flagsOrMarkers.traffic_violation ? `
${language.traffic_violation}
` : ''}
+ ${flagsOrMarkers.wanted_person ? `
${language.wanted_person}
` : ''}
+ ${flagsOrMarkers.other ? `
${language.other}
` : ''}
+ ${flagsOrMarkers.active_warrant ? `
${language.active_warrant}
` : ''}
+
+
+ ` : ''}
+
+ `);
+
+ // Force browser reflow and repaint to ensure text renders crisply
+ var databaseCheck = document.getElementById('database-check');
+ if (databaseCheck) {
+ // Force reflow
+ void databaseCheck.offsetHeight;
+ // Reset any transform that might affect rendering
+ databaseCheck.style.isolation = 'auto';
+ // Force repaint by toggling a property
+ var originalTransform = databaseCheck.style.transform;
+ databaseCheck.style.transform = 'translateZ(0)';
+ // Use requestAnimationFrame to ensure browser has processed the change
+ requestAnimationFrame(function() {
+ databaseCheck.style.transform = originalTransform || 'translateZ(0)';
+ });
+ }
+}
+
+function DisplayVehicleInfo(display, vehicleData, DisableTaxAndMOT, skipDbCheckAnimation) {
+ if (display) {
+ // Hide ID card
+ $("#ped-interaction-id-card").hide();
+ // Hide inventory
+ $("#ped-interaction-inventory").hide();
+
+ clearTimeout(vehicleInfoTimeout);
+
+ // Only clear vehicleDbCheckTimeout if we're not preserving state
+ if (!skipDbCheckAnimation) {
+ clearTimeout(vehicleDbCheckTimeout);
+ vehicleDbCheckCompleted = false;
+ }
+
+ $("#vehicle-info").empty();
+
+ // Vehicle Data
+ var data = {
+ vehNetId: vehicleData.vehNetId,
+ license_plate: vehicleData.license_plate,
+ model: vehicleData.model,
+ model_hash: vehicleData.model_hash,
+ vehicle_class: vehicleData.vehicle_class,
+ vehicle_class_from_name: vehicleData.vehicle_class_from_name,
+ color: vehicleData.color,
+ color_secondary: vehicleData.color_secondary,
+ build_year: vehicleData.build_year,
+ tax: vehicleData.tax,
+ mot: vehicleData.mot,
+ insurance: vehicleData.insurance,
+ stolen: vehicleData.stolen,
+ bolo: vehicleData.bolo,
+ bolo_description: vehicleData.bolo_description,
+ owner_name: vehicleData.owner_name,
+ }
+
+ // Store data for TAB key re-display
+ lastDisplayedModule = 'vehicle';
+ lastVehicleData = {
+ vehicleData: vehicleData,
+ DisableTaxAndMOT: DisableTaxAndMOT
+ };
+
+ // Modern Bootstrap 5.2.3 Vehicle Info Card
+ // DON'T use isolation: isolate - it causes text blurriness
+ $("#vehicle-info").append(`
+
+
+
+
+ ${language.VehiclePlate}
+ ${data.license_plate}
+
+
+ ${language.VehicleModel}
+ ${data.model}
+
+
+
+ `);
+
+ // Always create the database-check container first
+ // DON'T use isolation: isolate on the card itself - it causes text blurriness
+ $("#vehicle-info").append(`
+
+
+ `);
+
+ $("#vehicle-info").show();
+
+ // Check if database check was already completed
+ if (vehicleDbCheckCompleted && skipDbCheckAnimation) {
+ // Show completed results immediately
+ renderVehicleDatabaseResults(data, DisableTaxAndMOT);
+ } else {
+ // Show loading spinner
+ $("#vehicle-info-database-check").html(`
+
+
+
+
+
+
${language.RunningDatabaseCheck}
+
+
+ `);
+
+ vehicleDbCheckTimeout = setTimeout(function() {
+ vehicleDbCheckCompleted = true;
+ renderVehicleDatabaseResults(data, DisableTaxAndMOT);
+ }, 5000);
+ }
+
+ // Hide the vehicle info after a certain period
+ vehicleInfoTimeout = setTimeout(function() {
+ $("#vehicle-info").hide();
+ updateModuleIndicator();
+ scheduleModuleClearIfHidden();
+ }, 15000);
+
+ updateModuleIndicator();
+
+ } else {
+ $("#vehicle-info").hide();
+ updateModuleIndicator();
+ }
+}
+
+// Separate function to render vehicle database results
+function renderVehicleDatabaseResults(data, DisableTaxAndMOT) {
+ // Completely clear and replace content to reset rendering context
+ var vehicleDbCheckElement = document.getElementById('vehicle-info-database-check');
+ if (vehicleDbCheckElement) {
+ // Remove all content including loading indicator
+ vehicleDbCheckElement.innerHTML = '';
+ // Force a reflow to clear any rendering contexts
+ void vehicleDbCheckElement.offsetHeight;
+ }
+
+ $("#vehicle-info-database-check").html(`
+
+
+
+
+
+ ${language.VehicleOwner}
+ ${data.owner_name}
+
+
+
+
+ ${language.VehicleBuildYear}
+ ${data.build_year}
+
+
+
+
+ ${language.VehicleColor}
+ ${data.color}
+
+
+
+
+ ${language.VehicleColorSecondary}
+ ${data.color_secondary}
+
+
+
+ ${DisableTaxAndMOT ? "" : `
+
+
+ ${language.VehicleTax}
+
+ ${data.tax ? language.Paid : language.NotPaid}
+
+
+
+
+
+ ${language.VehicleMOT}
+
+ ${data.mot ? language.Passed : language.Failed}
+
+
+
+ `}
+
+
+ ${language.VehicleInsurance}
+
+ ${data.insurance ? language.Valid : language.Invalid}
+
+
+
+
+
+ ${language.VehicleStolen}
+
+ ${data.stolen ? language.Yes : language.No}
+
+
+
+
+
+ ${language.VehicleBolo}
+
+ ${data.bolo ? language.Yes : language.No}
+
+
+
+ ${!data.bolo ? "" : `
+
+
+
${language.VehicleBoloDescription}
+
${wrapText(data.bolo_description ? data.bolo_description : "-", 50)}
+
+
+ `}
+
+
+ `);
+
+ // Force browser reflow and repaint to ensure text renders crisply
+ var vehicleDbCheck = document.getElementById('vehicle-info-database-check');
+ if (vehicleDbCheck) {
+ // Force reflow
+ void vehicleDbCheck.offsetHeight;
+ // Reset any transform that might affect rendering
+ vehicleDbCheck.style.isolation = 'auto';
+ // Force repaint by toggling a property
+ var originalTransform = vehicleDbCheck.style.transform;
+ vehicleDbCheck.style.transform = 'translateZ(0)';
+ // Use requestAnimationFrame to ensure browser has processed the change
+ requestAnimationFrame(function() {
+ vehicleDbCheck.style.transform = originalTransform || 'translateZ(0)';
+ });
+ }
+}
+
+var inventoryTimeout;
+function DisplayPedInventory(display, itemList) {
+ if (display) {
+ // Hide ID card
+ $("#ped-interaction-id-card").hide();
+ // Hide vehicle info
+ $("#vehicle-info").hide();
+
+ clearTimeout(inventoryTimeout);
+
+ $("#ped-interaction-inventory").empty();
+
+ // Store data for TAB key re-display
+ lastDisplayedModule = 'inventory';
+ lastInventoryData = {
+ itemList: itemList
+ };
+
+ // Modern Bootstrap 5.2.3 Inventory Card
+ $("#ped-interaction-inventory").append(`
+
+ `);
+
+ if (itemList.length === 0) {
+ $("#inventory-list-body").append(`
+
+ `);
+ } else {
+ $.each(itemList, function (key, v) {
+ var isIllegal = v.illegal;
+ var itemClass = isIllegal ? 'border-danger border-opacity-50' : 'border-secondary border-opacity-25';
+ var textClass = isIllegal ? 'text-danger' : 'text-light';
+ var iconClass = isIllegal ? 'fas fa-exclamation-triangle text-danger' : 'fas fa-cube text-info';
+
+ var listItem = `
+
+
+
+ ${v.name}
+ ${isIllegal ? 'Illegal ' : ''}
+
+
+ `;
+ $("#inventory-list-body").append(listItem);
+ });
+ }
+
+ $("#ped-interaction-inventory").show();
+
+ inventoryTimeout = setTimeout(function() {
+ $("#ped-interaction-inventory").hide();
+ updateModuleIndicator();
+ scheduleModuleClearIfHidden();
+ }, 15000);
+
+ updateModuleIndicator();
+
+ } else {
+ $("#ped-interaction-inventory").hide();
+ updateModuleIndicator();
+ }
+}
+
+// Function to clear module data when shift ends
+function clearModuleData() {
+ // Clear stored module data
+ lastDisplayedModule = null;
+ lastIdCardData = null;
+ lastVehicleData = null;
+ lastInventoryData = null;
+
+ // Reset database check completion states
+ idCardDbCheckCompleted = false;
+ vehicleDbCheckCompleted = false;
+
+ // Clear any active timeouts
+ clearTimeout(idCardTimeout);
+ clearTimeout(vehicleInfoTimeout);
+ clearTimeout(inventoryTimeout);
+ clearTimeout(dbCheckTimeout);
+ clearTimeout(vehicleDbCheckTimeout);
+ clearTimeout(moduleClearTimeout);
+ stopModuleProgressBar();
+
+ // Hide any visible modules
+ $("#ped-interaction-id-card").hide();
+ $("#vehicle-info").hide();
+ $("#ped-interaction-inventory").hide();
+
+ // Hide the module indicator
+ $("#module-indicator").fadeOut(200);
+}
+
+// Function to schedule clearing module data if hidden for > 1 minute
+function scheduleModuleClearIfHidden() {
+ // Clear any existing timeout and interval
+ clearTimeout(moduleClearTimeout);
+ clearInterval(moduleProgressInterval);
+
+ // Record start time for progress bar
+ moduleClearStartTime = Date.now();
+
+ // Start progress bar animation
+ startModuleProgressBar();
+
+ // Set new timeout to clear after 60 seconds
+ moduleClearTimeout = setTimeout(function() {
+ // Only clear if module is still hidden
+ if (lastDisplayedModule) {
+ var isCurrentlyVisible = false;
+
+ if (lastDisplayedModule === 'idcard' && $("#ped-interaction-id-card").is(":visible")) {
+ isCurrentlyVisible = true;
+ } else if (lastDisplayedModule === 'vehicle' && $("#vehicle-info").is(":visible")) {
+ isCurrentlyVisible = true;
+ } else if (lastDisplayedModule === 'inventory' && $("#ped-interaction-inventory").is(":visible")) {
+ isCurrentlyVisible = true;
+ }
+
+ // Clear module data if still hidden after 60 seconds
+ if (!isCurrentlyVisible) {
+ clearModuleData();
+ }
+ }
+ }, 30000); // 30 seconds
+}
+
+// Function to start/update progress bar animation
+function startModuleProgressBar() {
+ // Clear any existing interval
+ clearInterval(moduleProgressInterval);
+
+ // Update progress bar every 100ms for smooth animation
+ moduleProgressInterval = setInterval(function() {
+ if (!moduleClearStartTime || !lastDisplayedModule) {
+ clearInterval(moduleProgressInterval);
+ return;
+ }
+
+ var elapsed = Date.now() - moduleClearStartTime;
+ var remaining = Math.max(0, 30000 - elapsed);
+ var progress = (remaining / 30000) * 100; // Percentage remaining (100% to 0%)
+
+ // Update progress bar (shows remaining time, fills from left to right)
+ var progressBar = $("#module-indicator-progress");
+ if (progressBar.length > 0) {
+ progressBar.css('width', progress + '%');
+ }
+
+ // Stop interval if timer is complete
+ if (progress <= 0) {
+ clearInterval(moduleProgressInterval);
+ }
+ }, 100); // Update every 100ms
+}
+
+// Function to stop progress bar animation
+function stopModuleProgressBar() {
+ clearInterval(moduleProgressInterval);
+ moduleClearStartTime = null;
+ var progressBar = $("#module-indicator-progress");
+ if (progressBar.length > 0) {
+ progressBar.css('width', '100%');
+ }
+}
+
+// Function to update module indicator icon
+function updateModuleIndicator() {
+ var indicator = $("#module-indicator");
+
+ // Check if there's a last displayed module and if it's currently hidden
+ if (lastDisplayedModule) {
+ var isCurrentlyVisible = false;
+ var moduleIcon = '';
+
+ if (lastDisplayedModule === 'idcard') {
+ isCurrentlyVisible = $("#ped-interaction-id-card").is(":visible");
+ moduleIcon = 'fas fa-id-card';
+ } else if (lastDisplayedModule === 'vehicle') {
+ isCurrentlyVisible = $("#vehicle-info").is(":visible");
+ moduleIcon = 'fas fa-car';
+ } else if (lastDisplayedModule === 'inventory') {
+ isCurrentlyVisible = $("#ped-interaction-inventory").is(":visible");
+ moduleIcon = 'fas fa-box';
+ }
+
+ // Show indicator only if module is hidden
+ if (!isCurrentlyVisible) {
+ if (indicator.length === 0) {
+ // Create indicator if it doesn't exist
+ $('body').append(`
+
+
+
+ ${language.Press} TAB
+
+
+
+ `);
+ // Show the indicator after creation
+ $("#module-indicator").fadeIn(200);
+ } else {
+ // Update existing indicator
+ indicator.find('i').attr('class', moduleIcon + ' me-2');
+ indicator.find('span').html(`${language.Press} TAB `);
+ // Ensure progress bar container exists
+ if (indicator.find('.module-indicator-progress-container').length === 0) {
+ indicator.append(`
+
+ `);
+ }
+ indicator.fadeIn(200);
+ }
+ } else {
+ // Hide indicator if module is visible
+ indicator.fadeOut(200);
+ stopModuleProgressBar();
+ }
+ } else {
+ // Hide indicator if no last displayed module
+ indicator.fadeOut(200);
+ }
+}
+
+// Function to re-display last shown module (called from Lua client)
+function redisplayLastModule() {
+ // Toggle visibility: hide if visible, show if hidden
+ if (lastDisplayedModule) {
+ var isCurrentlyVisible = false;
+
+ if (lastDisplayedModule === 'idcard' && $("#ped-interaction-id-card").is(":visible")) {
+ isCurrentlyVisible = true;
+ } else if (lastDisplayedModule === 'vehicle' && $("#vehicle-info").is(":visible")) {
+ isCurrentlyVisible = true;
+ } else if (lastDisplayedModule === 'inventory' && $("#ped-interaction-inventory").is(":visible")) {
+ isCurrentlyVisible = true;
+ }
+
+ if (isCurrentlyVisible) {
+ // Hide the module and clear timeout
+ if (lastDisplayedModule === 'idcard') {
+ clearTimeout(idCardTimeout);
+ $("#ped-interaction-id-card").hide();
+ } else if (lastDisplayedModule === 'vehicle') {
+ clearTimeout(vehicleInfoTimeout);
+ $("#vehicle-info").hide();
+ } else if (lastDisplayedModule === 'inventory') {
+ clearTimeout(inventoryTimeout);
+ $("#ped-interaction-inventory").hide();
+ }
+ updateModuleIndicator();
+ scheduleModuleClearIfHidden();
+ } else {
+ // Show the module if currently hidden
+ // Clear the module clear timeout since we're showing it
+ clearTimeout(moduleClearTimeout);
+ stopModuleProgressBar();
+ if (lastDisplayedModule === 'idcard' && lastIdCardData) {
+ DisplayPedIdCard(true, lastIdCardData.personaldata, lastIdCardData.showdatabasecheck, idCardDbCheckCompleted);
+ } else if (lastDisplayedModule === 'vehicle' && lastVehicleData) {
+ DisplayVehicleInfo(true, lastVehicleData.vehicleData, lastVehicleData.DisableTaxAndMOT, vehicleDbCheckCompleted);
+ } else if (lastDisplayedModule === 'inventory' && lastInventoryData) {
+ DisplayPedInventory(true, lastInventoryData.itemList);
+ }
+ updateModuleIndicator();
+ }
+ }
+}
+
+// NPC Backup Request Radial Menu
+var listener = false;
+function setupButton(buttonId, iconClass, instructionText) {
+ const button = document.getElementById(buttonId);
+ $(`#${buttonId}`).html(` `);
+
+ button.addEventListener('mouseenter', () => PlayNUISound('generic-sounds', "rollover", 0.5));
+ button.addEventListener('mouseover', () => {
+ document.getElementById('instruction-text').style.display = 'block';
+ $("#instruction-text").html(`${instructionText}
`);
+ });
+ button.addEventListener('mouseout', () => document.getElementById('instruction-text').style.display = 'none');
+}
+
+function setupRadialMenu() {
+ if (!listener) {
+ listener = true;
+
+ /*
+ TO ADD NEW BACKUP, SIMPLY ADD YOUR BACKUP TYPE TO THE BUTTONS ARRAY BELOW AND CHANGE THE VALUES!
+ MAKE SURE TO CREATE THE REQUIRED FUNCTION AND ADD THE CSS CODE IN STYLES.CSS!
+ */
+
+ // INSTRUCTION
+ // const instructionText = document.getElementById('instruction-text');
+ const buttons = [
+ // THIS ALWAYS NEEDS TO BE THE FIRST ITEM IN THIS ARRAY!
+ { id: 'middle-x', iconClass: '', instruction: language.ExitRadialMenuInstruction, hoverColour: '', function: 'ToggleRadialMenu(false)' },
+ //
+ { id: 'police-transport', iconClass: language.RequestPoliceTransportIcon, instruction: language.RequestPoliceTransportInstruction, hoverColour: '#0057b9', function: 'RequestOrCancelPoliceTransport()' },
+ { id: 'ambulance-request', iconClass: language.RequestAmbulanceIcon, instruction: language.RequestAmbulanceInstruction, hoverColour: '#ff0000', function: 'RequestOrCancelAmbulance()' },
+ { id: 'tow-request', iconClass: language.RequestTowIcon, instruction: language.RequestTowInstruction, hoverColour: '#782323', function: 'RequestOrCancelTow()' },
+ { id: 'taxi-request', iconClass: language.RequestTaxiIcon, instruction: language.RequestTaxiInstruction, hoverColour: '#FF9A18', function: 'RequestOrCancelTaxi()' },
+ { id: 'road-service-request', iconClass: language.RequestRoadServiceIcon, instruction: language.RequestRoadServiceInstruction, hoverColour: '#ffbf00', function: 'RequestOrCancelRoadService()' },
+ { id: 'coroner-request', iconClass: language.RequestCoronerIcon, instruction: language.RequestCoronerInstruction, hoverColour: '#3D3D3D', function: 'RequestOrCancelCoroner()' },
+ { id: 'animal-rescue-request', iconClass: language.RequestAnimalRescueIcon, instruction: language.RequestAnimalRescueInstruction, hoverColour: '#9bde00', function: 'RequestOrCancelAnimalRescue()' },
+ { id: 'mechanic-request', iconClass: language.RequestMechanicIcon, instruction: language.RequestMechanicInstruction, hoverColour: '#3D3D3D', function: 'RequestOrCancelMechanic()' },
+ { id: 'fire-request', iconClass: language.RequestFireIcon, instruction: language.RequestFireInstruction, hoverColour: '#3D3D3D', function: 'RequestOrCancelFire()' }
+ ];
+
+ $('#circle-menu').html(`
+
- ${e.slice(1).map(e=>` `).join("")}
+ ${buttons.slice(1).map(button => ` `).join('')}
- `),e.forEach(({id:e,iconClass:t,instruction:a})=>setupButton(e,t,a))}}function ToggleRadialMenu(e){if(setupRadialMenu(),e){document.addEventListener("keyup",handleRadialMenuEscape);let t=document.getElementById("instruction-title");t.style.display="block",$("#instruction-title").html(`${language.RadialMenuInstructionTitle}
`),$("#circle-menu").show(),$("body").append("
")}else{document.removeEventListener("keyup",handleRadialMenuEscape),PlayNUISound("generic-sounds","radialclose",.5);let a=document.getElementById("instruction-title"),n=document.getElementById("instruction-text");a&&(a.style.display="none"),n&&(n.style.display="none"),$("#circle-menu").hide(),$.post(`https://${resourceName}/closeRadialMenu`,JSON.stringify({})),$(".blur-overlay").remove()}}function handleRadialMenuEscape(e){"Escape"===e.key&&ToggleRadialMenu(!1)}function RequestOrCancelPoliceTransport(){ToggleRadialMenu(!1),$.post(`https://${resourceName}/requestOrCancelPoliceTransport`,JSON.stringify({}))}function RequestOrCancelAmbulance(){ToggleRadialMenu(!1),$.post(`https://${resourceName}/requestOrCancelAmbulance`,JSON.stringify({}))}function RequestOrCancelTow(){ToggleRadialMenu(!1),$.post(`https://${resourceName}/requestOrCancelTow`,JSON.stringify({}))}function RequestOrCancelTaxi(){ToggleRadialMenu(!1),$.post(`https://${resourceName}/requestOrCancelTaxi`,JSON.stringify({}))}function RequestOrCancelRoadService(){ToggleRadialMenu(!1),$.post(`https://${resourceName}/requestOrCancelRoadService`,JSON.stringify({}))}function RequestOrCancelCoroner(){ToggleRadialMenu(!1),$.post(`https://${resourceName}/requestOrCancelCoroner`,JSON.stringify({}))}function RequestOrCancelAnimalRescue(){ToggleRadialMenu(!1),$.post(`https://${resourceName}/requestOrCancelAnimalRescue`,JSON.stringify({}))}function RequestOrCancelMechanic(){ToggleRadialMenu(!1),$.post(`https://${resourceName}/requestOrCancelMechanic`,JSON.stringify({}))}function RequestOrCancelFire(){ToggleRadialMenu(!1),$.post(`https://${resourceName}/requestOrCancelFire`,JSON.stringify({}))}function TogglePursuitModeUI(e,t,a,n){$("#pursuit-mode-ui").toggle(e),e?$("#pursuit-mode-ui").html(` ${language.ActivePursuit}
${t?`
${a} (${language.PursuitHotkeyFocus}) | ${n} (${language.PursuitHotkeyBackup})
`:""}
`):$("#pursuit-mode-ui").hide()}var pursuitRadialMenuListener=!1;function setupPursuitRadialMenuButton(e,t,a){let n=document.getElementById(e);$(`#${e}`).html(` `),n.addEventListener("mouseenter",()=>{PlayNUISound("generic-sounds","rollover",.5),document.getElementById("pursuit-instruction-text").style.display="block",$("#pursuit-instruction-text").html(`${a}
`)}),n.addEventListener("mouseleave",()=>{document.getElementById("pursuit-instruction-text").style.display="none"})}function setupPursuitRadialMenu(){if(!pursuitRadialMenuListener){pursuitRadialMenuListener=!0;let e=[{id:"pursuit-middle-x",iconClass:"",instruction:language.ExitRadialMenuInstruction,hoverColour:"",function:"TogglePursuitRadialMenu(false)"},{id:"pursuit-backup-light",iconClass:language.RequestLightBackupIcon,instruction:language.RequestLightBackupInstruction,hoverColour:"#0080f8",function:"RequestPursuitBackup('light')"},{id:"pursuit-backup-medium",iconClass:language.RequestMediumBackupIcon,instruction:language.RequestMediumBackupInstruction,hoverColour:"#014b8f",function:"RequestPursuitBackup('medium')"},{id:"pursuit-backup-heavy",iconClass:language.RequestHeavyBackupIcon,instruction:language.RequestHeavyBackupInstruction,hoverColour:"#001e39",function:"RequestPursuitBackup('heavy')"},{id:"pursuit-backup-air",iconClass:language.RequestAirBackupIcon,instruction:language.RequestAirBackupInstruction,hoverColour:"#00a2dd",function:"RequestPursuitBackup('air')"},{id:"pursuit-backup-army",iconClass:language.RequestArmyBackupIcon,instruction:language.RequestArmyBackupInstruction,hoverColour:"#647b32",function:"RequestPursuitBackup('army')"},];$("#pursuit-radial-menu").html(`
-
+ `)
+
+ buttons.forEach(({ id, iconClass, instruction }) => setupButton(id, iconClass, instruction));
+ }
+}
+
+function ToggleRadialMenu(display) {
+ setupRadialMenu();
+
+ if (display) {
+ // Add escape key event listener
+ document.addEventListener('keyup', handleRadialMenuEscape);
+
+ const instructionTitle = document.getElementById('instruction-title');
+ instructionTitle.style.display = 'block';
+ $("#instruction-title").html(`${language.RadialMenuInstructionTitle}
`);
+ $("#circle-menu").show();
+
+ // Apply the blur overlay
+ $("body").append("
");
+
+ } else {
+ // Remove escape key event listener
+ document.removeEventListener('keyup', handleRadialMenuEscape);
+
+ PlayNUISound('generic-sounds', "radialclose", 0.5);
+
+ // Hide all instruction elements
+ const instructionTitle = document.getElementById('instruction-title');
+ const instructionText = document.getElementById('instruction-text');
+
+ if (instructionTitle) {
+ instructionTitle.style.display = 'none';
+ }
+ if (instructionText) {
+ instructionText.style.display = 'none';
+ }
+
+ $("#circle-menu").hide();
+ $.post(`https://${resourceName}/closeRadialMenu`, JSON.stringify({}));
+
+ // Remove the blur overlay
+ $(".blur-overlay").remove();
+ }
+}
+
+function handleRadialMenuEscape(event) {
+ if (event.key === 'Escape') {
+ ToggleRadialMenu(false);
+ }
+}
+
+function RequestOrCancelPoliceTransport() {
+ ToggleRadialMenu(false)
+ $.post(`https://${resourceName}/requestOrCancelPoliceTransport`, JSON.stringify({}))
+}
+
+function RequestOrCancelAmbulance() {
+ ToggleRadialMenu(false)
+ $.post(`https://${resourceName}/requestOrCancelAmbulance`, JSON.stringify({}))
+}
+
+function RequestOrCancelTow() {
+ ToggleRadialMenu(false)
+ $.post(`https://${resourceName}/requestOrCancelTow`, JSON.stringify({}))
+}
+
+function RequestOrCancelTaxi() {
+ ToggleRadialMenu(false)
+ $.post(`https://${resourceName}/requestOrCancelTaxi`, JSON.stringify({}))
+}
+
+function RequestOrCancelRoadService() {
+ ToggleRadialMenu(false)
+ $.post(`https://${resourceName}/requestOrCancelRoadService`, JSON.stringify({}))
+}
+
+function RequestOrCancelCoroner() {
+ ToggleRadialMenu(false)
+ $.post(`https://${resourceName}/requestOrCancelCoroner`, JSON.stringify({}))
+}
+
+function RequestOrCancelAnimalRescue() {
+ ToggleRadialMenu(false)
+ $.post(`https://${resourceName}/requestOrCancelAnimalRescue`, JSON.stringify({}))
+}
+
+function RequestOrCancelMechanic() {
+ ToggleRadialMenu(false)
+ $.post(`https://${resourceName}/requestOrCancelMechanic`, JSON.stringify({}))
+}
+
+function RequestOrCancelFire() {
+ ToggleRadialMenu(false)
+ $.post(`https://${resourceName}/requestOrCancelFire`, JSON.stringify({}))
+}
+
+// Pursuit Mode UI
+
+function TogglePursuitModeUI(display, displayhotkeyhint, zoomhotkey, backuphotkey) {
+ $("#pursuit-mode-ui").toggle(display);
+ if (display) {
+
+ // MINIFY
+ $("#pursuit-mode-ui").html(` ${language.ActivePursuit}
${displayhotkeyhint ? `
${zoomhotkey} (${language.PursuitHotkeyFocus}) | ${backuphotkey} (${language.PursuitHotkeyBackup})
` : ''}
`);
+
+ // HTML
+ // $("#pursuit-mode-ui").html(`
+ //
+ //
+ //
+ //
+ // ${language.ActivePursuit}
+ //
+ //
+ //
+ // ${displayhotkeyhint ? `
+ //
+ // ${zoomhotkey} (${language.PursuitHotkeyFocus}) | ${backuphotkey} (${language.PursuitHotkeyBackup})
+ //
+ // ` : ''}
+ //
+ //
+ //
+ // `);
+
+ } else {
+ $("#pursuit-mode-ui").hide();
+ }
+}
+
+//Pursuit Radial Menu
+var pursuitRadialMenuListener = false;
+function setupPursuitRadialMenuButton(buttonId, iconClass, instructionText) {
+ const button = document.getElementById(buttonId);
+ $(`#${buttonId}`).html(` `);
+
+ // Use mouseenter instead of mouseover to prevent event bubbling
+ button.addEventListener('mouseenter', () => {
+ PlayNUISound('generic-sounds', "rollover", 0.5);
+ document.getElementById('pursuit-instruction-text').style.display = 'block';
+ $("#pursuit-instruction-text").html(`${instructionText}
`);
+ });
+
+ // Use mouseleave instead of mouseout to prevent event bubbling
+ button.addEventListener('mouseleave', () => {
+ document.getElementById('pursuit-instruction-text').style.display = 'none';
+ });
+}
+
+function setupPursuitRadialMenu() {
+ if (!pursuitRadialMenuListener) {
+ pursuitRadialMenuListener = true;
+
+ // INSTRUCTION
+ // const instructionText = document.getElementById('pursuit-instruction-text');
+ const buttons = [
+ // THIS ALWAYS NEEDS TO BE THE FIRST ITEM IN THIS ARRAY!
+ { id: 'pursuit-middle-x', iconClass: '', instruction: language.ExitRadialMenuInstruction, hoverColour: '', function: 'TogglePursuitRadialMenu(false)' },
+ //
+ { id: 'pursuit-backup-light', iconClass: language.RequestLightBackupIcon, instruction: language.RequestLightBackupInstruction, hoverColour: '#0080f8', function: "RequestPursuitBackup('light')" },
+ { id: 'pursuit-backup-medium', iconClass: language.RequestMediumBackupIcon, instruction: language.RequestMediumBackupInstruction, hoverColour: '#014b8f', function: "RequestPursuitBackup('medium')" },
+ { id: 'pursuit-backup-heavy', iconClass: language.RequestHeavyBackupIcon, instruction: language.RequestHeavyBackupInstruction, hoverColour: '#001e39', function: "RequestPursuitBackup('heavy')" },
+ { id: 'pursuit-backup-air', iconClass: language.RequestAirBackupIcon, instruction: language.RequestAirBackupInstruction, hoverColour: '#00a2dd', function: "RequestPursuitBackup('air')" },
+ { id: 'pursuit-backup-army', iconClass: language.RequestArmyBackupIcon, instruction: language.RequestArmyBackupInstruction, hoverColour: '#647b32', function: "RequestPursuitBackup('army')" },
+ ];
+
+ $('#pursuit-radial-menu').html(`
+
- ${e.slice(1).map(e=>` `).join("")}
+ ${buttons.slice(1).map(button => ` `).join('')}
- `),e.forEach(({id:e,iconClass:t,instruction:a})=>setupPursuitRadialMenuButton(e,t,a))}}function TogglePursuitRadialMenu(e){if(setupPursuitRadialMenu(),e){document.addEventListener("keyup",handlePursuitRadialMenuEscape);let t=document.getElementById("pursuit-instruction-title");t.style.display="block",$("#pursuit-instruction-title").html(`${language.PursuitRadialMenuInstructionTitle}
`),$("#pursuit-radial-menu").show(),$("body").append("
")}else{document.removeEventListener("keyup",handlePursuitRadialMenuEscape),PlayNUISound("generic-sounds","radialclose",.5);let a=document.getElementById("pursuit-instruction-title");a&&(a.style.display="none");let n=document.getElementById("pursuit-instruction-text");n&&(n.style.display="none"),$("#pursuit-radial-menu").hide(),$.post(`https://${resourceName}/closePursuitRadialMenu`,JSON.stringify({})),$(".blur-overlay").remove()}}function handlePursuitRadialMenuEscape(e){"Escape"===e.key&&TogglePursuitRadialMenu(!1)}function RequestPursuitBackup(e){TogglePursuitRadialMenu(!1),$.post(`https://${resourceName}/requestPursuitBackup`,JSON.stringify({unitType:e}))}var isDispatchQueueProcessing=!1,dispatchMessages=[];function AddMessageToDispatchQueue(e,t,a,n,i,o,s){dispatchMessages.push({display:e,hotkeys:t,messagetype:a,calloutdata:n,timeInMS:i,message:o,toggle:s}),isDispatchQueueProcessing||ProcessDispatchQueue()}function ProcessDispatchQueue(){if(dispatchMessages.length>0){isDispatchQueueProcessing=!0;var e=dispatchMessages[0];DisplayDispatchMessage(e.display,e.hotkeys,e.messagetype,e.calloutdata,e.message,e.toggle);var t=e.toggle?e.timeInMS:3e3;StartCountdownTimer(t),setTimeout(function(){dispatchMessages.shift(),ProcessDispatchQueue()},t)}else isDispatchQueueProcessing=!1,$("#dispatch-message-prompt-container").hide()}function DisplayDispatchMessage(e,t,a,n,i,o){if(!o){$("#dispatch-message-prompt-container").empty().show().append(`
+ `)
+
+ buttons.forEach(({ id, iconClass, instruction }) => setupPursuitRadialMenuButton(id, iconClass, instruction));
+ }
+}
+
+function TogglePursuitRadialMenu(display) {
+ setupPursuitRadialMenu();
+
+ if (display) {
+ // Add escape key event listener
+ document.addEventListener('keyup', handlePursuitRadialMenuEscape);
+
+ const instructionTitle = document.getElementById('pursuit-instruction-title');
+ instructionTitle.style.display = 'block';
+ $("#pursuit-instruction-title").html(`${language.PursuitRadialMenuInstructionTitle}
`);
+ $("#pursuit-radial-menu").show();
+
+ // Apply the blur overlay
+ $("body").append("
");
+
+ } else {
+ // Remove escape key event listener
+ document.removeEventListener('keyup', handlePursuitRadialMenuEscape);
+
+ PlayNUISound('generic-sounds', "radialclose", 0.5);
+ const instructionTitle = document.getElementById('pursuit-instruction-title');
+ if (instructionTitle) {
+ instructionTitle.style.display = 'none';
+ }
+ const instructionText = document.getElementById('pursuit-instruction-text');
+ if (instructionText) {
+ instructionText.style.display = 'none';
+ }
+
+ $("#pursuit-radial-menu").hide();
+ $.post(`https://${resourceName}/closePursuitRadialMenu`, JSON.stringify({}));
+
+ // Remove the blur overlay
+ $(".blur-overlay").remove();
+ }
+}
+
+function handlePursuitRadialMenuEscape(event) {
+ if (event.key === 'Escape') {
+ TogglePursuitRadialMenu(false);
+ }
+}
+
+function RequestPursuitBackup(unitType) {
+ TogglePursuitRadialMenu(false)
+ $.post(`https://${resourceName}/requestPursuitBackup`, JSON.stringify({unitType: unitType}));
+}
+
+// DISPATCH
+
+var isDispatchQueueProcessing = false;
+var dispatchMessages = [];
+
+function AddMessageToDispatchQueue(display, hotkeys, messagetype, calloutdata, timeInMS, message, toggle) {
+ dispatchMessages.push({ display: display, hotkeys: hotkeys, messagetype: messagetype, calloutdata: calloutdata, timeInMS: timeInMS, message: message, toggle: toggle });
+ if (!isDispatchQueueProcessing) {
+ ProcessDispatchQueue();
+ }
+}
+
+function ProcessDispatchQueue() {
+ if (dispatchMessages.length > 0) {
+ isDispatchQueueProcessing = true;
+
+ var message = dispatchMessages[0]; // Get the first message from the queue without removing it
+
+ DisplayDispatchMessage(message.display, message.hotkeys, message.messagetype, message.calloutdata, message.message, message.toggle);
+
+ // Determine the timeout duration based on the message.toggle property
+ var timeoutDuration = message.toggle ? message.timeInMS : 3000; // Use 3000ms if toggle is false
+
+ // Set the countdown timer for the message
+ StartCountdownTimer(timeoutDuration);
+
+ setTimeout(function() {
+ dispatchMessages.shift(); // Remove the first message from the queue
+ ProcessDispatchQueue();
+ }, timeoutDuration);
+ } else {
+ isDispatchQueueProcessing = false;
+ $("#dispatch-message-prompt-container").hide(); // Hide the message prompt container if there are no more messages
+ }
+}
+
+function getDispatchIcon(messagetype) {
+ switch(messagetype) {
+ case "response":
+ return ' ';
+ case "arrival":
+ return ' ';
+ case "detach":
+ return ' ';
+ case "shift":
+ return ' ';
+ case "ambulance":
+ return ' ';
+ case "police":
+ return ' ';
+ case "fire":
+ return ' ';
+ case "tow":
+ return ' ';
+ case "taxi":
+ return ' ';
+ default:
+ return ' ';
+ }
+}
+
+function DisplayDispatchMessage(display, hotkeys, messagetype, calloutdata, message, toggle) {
+
+ if (!toggle) {
+ $("#dispatch-message-prompt-container").empty().show().append(`
${language.DispatchMessage}
- `),applySavedPosition("dispatch-message-prompt-container");return}"response"==a||"arrival"==a?("response"==a&&$.post(`https://${resourceName}/offerToTrackPlayer`,JSON.stringify({calloutdata:n})),$("#dispatch-message-prompt-container").empty().show().append(` ${wrapText(i,50)}
${language.Caller} ${wrapText(`${n.FirstName} ${n.LastName}`,50)} ${language.CallDescription} ${wrapText(n.Description??"N/A",50)} ${language.Location} ${wrapText(n.StreetName??"N/A",50)} ${language.Postal} ${wrapText(n.Postal??"N/A",50)} ${language.CalloutUnitsRequired} ${wrapText(n.CalloutUnitsRequired?.description??"N/A",50)} ${language.DispatchNote} ${wrapText(language.DispatchNoteResponseText??"N/A",50)}
`),applySavedPosition("dispatch-message-prompt-container"),"response"==a&&$("#dispatch-message-prompt-container").append(` `)):($("#dispatch-message-prompt-container").empty().show().append(` `),applySavedPosition("dispatch-message-prompt-container"))}function StartCountdownTimer(e){var t=e/1e3;let a=`${language.NextDispatchMessage} ${t} ${language.Seconds}...`;$("#countdown-timer").html(`${wrapText(a,70)}
`);var n=setInterval(function(){--t>=0?$("#countdown-timer").html(`${wrapText(`${language.NextDispatchMessage} ${t} ${language.Seconds}...`,70)}
`):(clearInterval(n),$("#countdown-timer").html(`${wrapText("Time's up!",70)}
`))},1e3)}function ToggleHotKeyHintPrompt(e,t,a,n){e?($("#hint-prompt-container").empty(),$("#hint-prompt-container").show(),applySavedPosition("hint-prompt-container"),$("#hint-prompt-container").append(` ${a.HotKeys} ${a.Aim} + ${t.OrderOnKneesOrStandUp} ${t.PullOver} ${t.AcceptCallout} ${t.CompleteCallout} ${t.RadialMenu} /${commands.StopTrackingUnit} ${a.Explanation} ${a.OrderOnKneesOrStandUpExplanation} ${a.PullOver} ${a.AcceptCallout} ${a.CompleteCallout} ${a.RadialMenuExplanation} ${a.StopTrackingUnitExplanation}
`),setTimeout(function(){$("#hint-prompt-container").hide(),$.post(`https://${resourceName}/reallowPromptMessages`,JSON.stringify({}))},n)):($("#hint-prompt-container").empty(),$("#hint-prompt-container").hide())}function SelectBackGroundColourByServiceType(e){var t="bg-primary",a="border-primary",n="btn-outline-primary",i="text-light";return"police"==e?(t="bg-primary",a="border-primary",n="btn-outline-primary",i="text-light"):"ambulance"==e?(t="bg-success",a="border-success",n="btn-outline-success",i="text-light"):"fire"==e?(t="bg-danger",a="border-danger",n="btn-outline-danger",i="text-light"):"tow"==e&&(t="bg-warning",a="border-warning",n="btn-outline-warning",i="text-light"),{backgroundColor:t,borderColor:a,textColor:i,buttonOutlineColor:n}}function ToggleCalloutDisplayPrompt(e,t,a,n,i,o){var s=n/1e3,{backgroundColor:l,borderColor:r,textColor:c,buttonOutlineColor:d}=SelectBackGroundColourByServiceType(o);e?($("#callout-prompt-container").empty(),$("#callout-prompt-container").show(),$("#callout-prompt-container").append(` ${a.Caller} ${wrapText(`${i.FirstName} ${i.LastName}`,50)} ${a.CallDescription} ${wrapText(i.Description??"N/A",50)} ${a.Location} ${wrapText(i.StreetName??"N/A",50)} ${a.Postal} ${wrapText(i.Postal??"N/A",50)} ${a.CalloutUnitsRequired} ${wrapText(i.CalloutUnitsRequired?.description??"N/A",50)} ${a.DispatchNote} ${wrapText(a.DispatchNoteResponseText??"N/A",50)}
`),applySavedPosition("callout-prompt-container"),clearInterval(window.countdownInterval),s--,window.countdownInterval=setInterval(function(){s>0?($("#callout-prompt-container").find("#countdown-text").html(` ${a.TimeRemainingToAcceptCallout} ${s} ${a.Seconds}... `),s--):(clearInterval(window.countdownInterval),$("#callout-prompt-container").hide(),$.post(`https://${resourceName}/calloutTimeout`,JSON.stringify({})))},1e3)):($("#callout-prompt-container").empty(),$("#callout-prompt-container").hide())}function ToggleCalloutInterface(e,t,a,n,i,o,s,l,r,c,d,u,g,p,b){isDisplayingCalloutInterface=e,e?($("#callout-interface-container").empty().addClass("slideInFromLeft"),$("#callout-interface-container").show(),$("#callout-interface-container").append(` แฏฝ ${r.CalloutName} ${a>0?` โก ${language.InvolvedPedsRemaining} ${a}/${c} `:""} ${n>0?` โก ${language.InvolvedVehsRemaining} ${n}/${d} `:""} ${i>0?` โก ${language.InvolvedObjsRemaining} ${i}/${u} `:""} ${o>0?` โก ${language.InvolvedPropsRemaining} ${o}/${g} `:""} ${s>0?` โก ${language.ExtinguishAllFires} ${s}/${p} `:""} ${l>0?` โก ${language.ClearAreaOfSmoke} ${l}/${b} `:""}
${language.Press} ${t.ToggleCalloutInfo} ${language.ToHideOrShow}
`),applySavedPosition("callout-interface-container")):($("#callout-interface-container").empty(),$("#callout-interface-container").hide())}function TogglePreCalloutInterface(e,t,a){isDisplayingPreCalloutInterface=e,e?($("#callout-pre-interface-container").empty().addClass("slideInFromLeft"),$("#callout-pre-interface-container").show(),$("#callout-pre-interface-container").append(` แฏฝ ${wrapText(`${language.EmergencyCall} (${a?.CalloutName??"N/A"})`,40)} โก ${language.Caller} ${wrapText(`${a?.FirstName??"N/A"} ${a?.LastName??"N/A"}`,70)} โก ${language.CallDescription} ${wrapText(a?.Description??"N/A",70)} โก ${language.Location} ${wrapText(a?.StreetName??"N/A",70)} โก ${language.Postal} ${wrapText(a?.Postal??"N/A",70)} โก ${language.CalloutUnitsRequired} ${wrapText(a?.CalloutUnitsRequired?.description??"N/A",70)} โก ${language.DispatchNote} ${wrapText(language.DispatchNoteResponseText,70)}
${language.Press} ${t.ToggleCalloutInfo} ${language.ToHideOrShow}
`),applySavedPosition("callout-pre-interface-container")):($("#callout-pre-interface-container").empty(),$("#callout-pre-interface-container").hide())}let Waypoints=[];function UpdateWaypointPosition(e,t,a){if(null==Waypoints[0]){$("#waypoint-container").hide();return}let n=document.getElementById("waypoint-container");$("#waypoint-distance").empty(),$("#waypoint-container").show(),$("#waypoint-distance").append(`
- แฏฝ ${a}
- `);let i=window.innerWidth,o=window.innerHeight,s=n.getBoundingClientRect(),l=s.width,r=s.height;n.style.left=(i-l)*e+"px",n.style.top=(o-r)*t+"px"}function AddWaypoint(e){let t=document.getElementById("waypoint-container");e?Waypoints.push({element:t}):($("#waypoint-container").hide(),Waypoints=[])}let escapeEventListenerAddedERSSelectionMenu=!1,menuSoundInstance=null;function ToggleERSSelectionMenu(e,t){if(e){let a=t.isPolice,n=t.isAmbulance,i=t.isFire,o=t.isTow;fadeOutMenuMusic(menuSoundInstance),menuSoundInstance=PlayNUISound("generic-sounds","selectionmenu",.5);let s=` ${language.PoliceDescription}
${language.AmbulanceDescription}
${language.FireDescription}
${language.TowDescription}
`;$("body").append(s),$("#ers-selection-modal").modal({backdrop:"static",keyboard:!1}),$("#ers-selection-modal").modal("show");for(var l=1;l<=6;l++)addSoundOnHoverEventListener("s-btn-"+l);$("#ers-selection-modal").on("shown.bs.modal",function(){escapeEventListenerAddedERSSelectionMenu||($(document).on("keydown",function(e){"Escape"===e.key&&(fadeOutMenuMusic(menuSoundInstance),$("#ers-selection-modal").modal("hide"),CancelServiceSelection(),e.stopPropagation())}),escapeEventListenerAddedERSSelectionMenu=!0)}).on("hidden.bs.modal",function(){$(document).off("keydown"),escapeEventListenerAddedERSSelectionMenu=!1})}else $("#ers-selection-modal").modal("hide").on("hidden.bs.modal",function(){fadeOutMenuMusic(menuSoundInstance),$(this).remove()})}function CancelServiceSelection(){$.post(`https://${resourceName}/cancelServiceSelection`,JSON.stringify({})),$("#ers-selection-modal").modal("hide").on("hidden.bs.modal",function(){fadeOutMenuMusic(menuSoundInstance),$(this).data("bs.modal",null),$(this).remove()})}function SelectService(e){$.post(`https://${resourceName}/selectedService`,JSON.stringify({service:e})),$("#ers-selection-modal").modal("hide").on("hidden.bs.modal",function(){fadeOutMenuMusic(menuSoundInstance),$(this).data("bs.modal",null),$(this).remove()})}function fadeOutMenuMusic(e){null!==e&&(e.fade(e.volume(),0,5e3),setTimeout(function(){e.stop()},5e3))}let escapeEventListenerAddedERSGearnMenu=!1;function DisplayGearMenu(e,t){if(e){fadeOutMenuMusic(menuSoundInstance),menuSoundInstance=PlayNUISound("generic-sounds","selectionmenu",.5);let a=` `;$("body").append(a);var n=t.ClothingData;let i=function e(t){switch(t){case"police":return"primary";case"fire":return"danger";case"ambulance":return"warning";case"tow":return"info";default:return"secondary"}}(t.ServiceType);$.each(n,function(e,a){let n=encodeURIComponent(JSON.stringify(t));$("#gear-options").append(` `)}),$("#ers-gear-modal").modal({backdrop:"static",keyboard:!1}),$("#ers-gear-modal").modal("show");for(var o=0;o$(e).hide(),250)}function slideIn(e){$(e).show().removeClass("slideOutToLeft").addClass("slideInFromLeft")}function toggleCalloutInfo(){if(isDisplayingPreCalloutInterface){let e=containerIDs.preCallout;$(e).is(":visible")?slideOut(e):slideIn(e)}if(isDisplayingCalloutInterface){let t=containerIDs.callout;$(t).is(":visible")?slideOut(t):slideIn(t)}}function updateWaypoints(e){let t=e.data,a=trackExistingUnitWaypoints();for(let[n,i]of Object.entries(t))null!==i&&i.distanceText>0&&processUnitWaypoint(n,i,a);removeUnusedUnitWaypoints(a)}function trackExistingUnitWaypoints(){let e=new Set;return document.querySelectorAll(".unit-waypoint-container").forEach(t=>{e.add(t.id)}),e}function processUnitWaypoint(e,t,a){let n=`unit-waypoint-container-${e}`,i=document.getElementById(n);i||(i=createUnitWaypointContainer(n)),updateUnitWaypointPosition(i,t),updateUnitWaypointContent(i,t),a.delete(n)}function createUnitWaypointContainer(e){let t=document.createElement("div");return t.classList.add("unit-waypoint-container","smooth-transition"),t.id=e,document.body.appendChild(t),t}function updateUnitWaypointPosition(e,t){let a=window.innerWidth,n=window.innerHeight,i=e.getBoundingClientRect(),o=i.width,s=i.height;t.scaleX>=0&&t.scaleX<=1&&t.scaleY>=0&&t.scaleY<=1&&(e.style.left=(a-o)*t.scaleX+"px",e.style.top=(n-s)*t.scaleY+"px")}function updateUnitWaypointContent(e,t){e.innerHTML=`
- แฏฝ ${t.distanceText}${t.metrics}
- `}function removeUnusedUnitWaypoints(e){e.forEach(e=>{let t=document.getElementById(e);t&&t.remove()})}function ToggleQuestionsModule(e,t){if(e){if($("#question-options-list").empty(),$("#ped-interaction-questioning").show(),t.length>0){let a=`
+ `);
+ return;
+ }
+
+
+ if (messagetype == "response" || messagetype == "arrival") {
+
+ if (messagetype == "response") {
+ $.post(`https://${resourceName}/offerToTrackPlayer`, JSON.stringify({calloutdata: calloutdata}));
+ }
+
+ // HTML
+ $("#dispatch-message-prompt-container").empty().show().append(`
+
+
+
+ ${calloutdata.CalloutName ? `
+
+ ${calloutdata.CalloutName}
+
+ ` : ''}
+
+ ${message}
+ ${calloutdata.Description ? `
+ ${calloutdata.Description}
+ ` : ''}
+
+
+ ${language.Caller}
+ ${calloutdata.FirstName} ${calloutdata.LastName}
+
+
+ ${language.Location}
+ ${calloutdata.StreetName ?? 'N/A'}
+
+
+ ${language.Postal}
+ ${calloutdata.Postal ?? 'N/A'}
+
+
+
+
+ `);
+
+ if (messagetype == "response") {
+
+ // HTML
+ $("#dispatch-message-prompt-container").append(`
+
+
+ ${language.TrackUnitProposal}
+
+ ${hotkeys.TrackUnit}
+
+ `)
+
+ }
+
+ } else { // if (messagetype == "detach" || messagetype == "shift" || messagetype == "ambulance" || messagetype == "police" || messagetype == "taxi" || messagetype == "tow" etc...)
+
+ // HTML
+ $("#dispatch-message-prompt-container").empty().show().append(`
+
+ `);
+ }
+}
+
+function StartCountdownTimer(timeInMS) {
+ var remainingTime = timeInMS / 1000; // Convert milliseconds to seconds
+ const countdownMessage = `${language.NextDispatchMessage} ${remainingTime} ${language.Seconds}...`;
+
+ $("#countdown-timer").html(`${wrapText(countdownMessage, 70)}`); // Wrap text with max 30 characters per line
+
+ var timerInterval = setInterval(function() {
+ remainingTime--;
+
+ if (remainingTime >= 0) {
+ $("#countdown-timer").html(`${wrapText(`${language.NextDispatchMessage} ${remainingTime} ${language.Seconds}...`, 70)}`); // Wrap text with max 30 characters per line
+ } else {
+ clearInterval(timerInterval);
+ $("#countdown-timer").html(`${wrapText("Time's up!", 70)}`); // Wrap "Time's up!" with max 30 characters per line
+ }
+ }, 1000);
+}
+
+// HINTS
+
+function ToggleHotKeyHintPrompt(display, hotkeys, language, time) {
+ if (display) {
+ $("#hint-prompt-container").empty(); // clear previous data
+ $("#hint-prompt-container").show();
+
+ // HTML
+ $("#hint-prompt-container").append(`
+
+
+
+
+ ${language.Aim} + ${hotkeys.OrderOnKneesOrStandUp}
+ ${language.OrderOnKneesOrStandUpExplanation}
+
+
+ ${hotkeys.PullOver}
+ ${language.PullOver}
+
+
+ ${hotkeys.AcceptCallout}
+ ${language.AcceptCallout}
+
+
+ ${hotkeys.CompleteCallout}
+ ${language.CompleteCallout}
+
+
+ ${hotkeys.RadialMenu}
+ ${language.RadialMenuExplanation}
+
+
+ /${commands.StopTrackingUnit}
+ ${language.StopTrackingUnitExplanation}
+
+
+
+
+ `);
+
+ // Auto-hide after elapsed time
+ setTimeout(function() {
+ $("#hint-prompt-container").hide();
+ $.post(`https://${resourceName}/reallowPromptMessages`, JSON.stringify({}));
+ }, time); // 10 seconds in milliseconds
+ } else {
+ $("#hint-prompt-container").empty();
+ $("#hint-prompt-container").hide();
+ }
+}
+
+// CALLOUTS (DISPATCH)
+
+function ToggleCalloutDisplayPrompt(display, hotkeys, language, time, calloutdata, serviceType) {
+ var calloutDisplayPromptTime = (time / 1000); // in s
+
+ if (display) {
+ $("#callout-prompt-container").empty(); // clear previous data
+ $("#callout-prompt-container").show();
+
+ // HTML
+ $("#callout-prompt-container").append(`
+
+
+
+
+ ${language.Caller}
+ ${calloutdata.FirstName} ${calloutdata.LastName}
+
+
+ ${language.Location}
+ ${calloutdata.StreetName ?? 'N/A'}
+
+
+ ${language.Postal}
+ ${calloutdata.Postal ?? 'N/A'}
+
+
+ ${calloutdata.Description ?? 'N/A'}
+
+
+
+
+ `);
+
+ // Clear any existing countdown interval
+ clearInterval(window.countdownInterval);
+
+ // Initial countdown display
+ const countdownMessage = `${calloutDisplayPromptTime} ${language.Seconds}...`;
+ $("#callout-countdown-timer").html(countdownMessage);
+
+ calloutDisplayPromptTime--;
+
+ window.countdownInterval = setInterval(function() {
+ if (calloutDisplayPromptTime > 0) {
+ // Update countdown display
+ const countdownMessage = `${calloutDisplayPromptTime} ${language.Seconds}...`;
+ $("#callout-countdown-timer").html(countdownMessage);
+
+ calloutDisplayPromptTime--;
+ } else {
+ clearInterval(window.countdownInterval);
+ $("#callout-prompt-container").hide();
+ $.post(`https://${resourceName}/calloutTimeout`, JSON.stringify({}));
+ }
+ }, 1000); // update every second
+
+ } else {
+ $("#callout-prompt-container").empty(); // clear previous data
+ $("#callout-prompt-container").hide();
+ }
+}
+
+function ToggleCalloutInterface(display, hotkeys, pedCount, vehicleCount, objectCount, propCount, fireCount, smokeCount, calloutdata, pedOriginalCount, vehOriginalCount, objOriginalCount, propOriginalCount, fireOriginalCount, smokeOriginalCount) {
+ isDisplayingCalloutInterface = display;
+ if (display) {
+ $("#callout-interface-container").empty().removeClass('slideOutToLeft').addClass('slideInFromLeft'); // clear previous data
+ $("#callout-interface-container").show();
+
+ // HTML
+ $("#callout-interface-container").append(`
+ แฏฝ ${calloutdata.CalloutName}
+
+
+ ${pedCount > 0 ? `
+
+ โก ${language.InvolvedPedsRemaining}
+ ${pedCount}/${pedOriginalCount}
+ ` : ''}
+ ${vehicleCount > 0 ? `
+
+ โก ${language.InvolvedVehsRemaining}
+ ${vehicleCount}/${vehOriginalCount}
+ ` : ''}
+ ${objectCount > 0 ? `
+
+ โก ${language.InvolvedObjsRemaining}
+ ${objectCount}/${objOriginalCount}
+ ` : ''}
+ ${propCount > 0 ? `
+
+ โก ${language.InvolvedPropsRemaining}
+ ${propCount}/${propOriginalCount}
+ ` : ''}
+ ${fireCount > 0 ? `
+
+ โก ${language.ExtinguishAllFires}
+ ${fireCount}/${fireOriginalCount}
+ ` : ''}
+ ${smokeCount > 0 ? `
+
+ โก ${language.ClearAreaOfSmoke}
+ ${smokeCount}/${smokeOriginalCount}
+ ` : ''}
+
+
+
+ ${language.Press} ${hotkeys.ToggleCalloutInfo} ${language.ToHideOrShow}
+ `);
+
+ } else {
+ $("#callout-interface-container").empty(); // clear previous data
+ $("#callout-interface-container").hide();
+ $('#minimized-indicator').fadeOut(200); // Hide indicator when interface is completely closed
+ }
+}
+
+
+function TogglePreCalloutInterface(display, hotkeys, calloutdata) {
+ isDisplayingPreCalloutInterface = display;
+ if (display) {
+ $("#callout-pre-interface-container").empty().removeClass('slideOutToLeft').addClass('slideInFromLeft'); // clear previous data
+ $("#callout-pre-interface-container").show();
+
+ // HTML
+ $("#callout-pre-interface-container").append(`
+
+ แฏฝ
+ ${wrapText(`${language.EmergencyCall} (${calloutdata?.CalloutName ?? 'N/A'})`, 40)}
+
+
+
+
+
+
+ โก
+ ${language.Caller}
+ ${wrapText(`${calloutdata?.FirstName ?? 'N/A'} ${calloutdata?.LastName ?? 'N/A'}`, 70)}
+
+
+
+
+ โก
+ ${language.CallDescription}
+ ${wrapText(calloutdata?.Description ?? 'N/A', 70)}
+
+
+
+
+ โก
+ ${language.Location}
+ ${wrapText(calloutdata?.StreetName ?? 'N/A', 70)}
+
+
+
+
+ โก
+ ${language.Postal}
+ ${wrapText(calloutdata?.Postal ?? 'N/A', 70)}
+
+
+
+
+ โก
+ ${language.CalloutUnitsRequired}
+ ${wrapText(calloutdata?.CalloutUnitsRequired?.description ?? 'N/A', 70)}
+
+
+
+
+ โก
+ ${language.DispatchNote}
+ ${wrapText(language.DispatchNoteResponseText, 70)}
+
+
+
+
+
+ ${language.Press} ${hotkeys.ToggleCalloutInfo} ${language.ToHideOrShow}
+ `);
+
+ } else {
+ $("#callout-pre-interface-container").empty(); // clear previous data
+ $("#callout-pre-interface-container").hide();
+ $('#minimized-indicator').fadeOut(200); // Hide indicator when interface is completely closed
+ }
+}
+
+// WAYPOINTS
+
+let Waypoints = []
+
+// Cache DOM elements and values for better performance
+let waypointCache = {
+ container: null,
+ distanceElement: null,
+ lastDistanceText: '',
+ lastScaleX: -999,
+ lastScaleY: -999,
+ width: 0,
+ height: 0,
+ offsetWidth: 0,
+ offsetHeight: 0,
+ needsSizeRecalc: true
+};
+
+// Update window dimensions on resize
+window.addEventListener('resize', function() {
+ waypointCache.width = window.innerWidth;
+ waypointCache.height = window.innerHeight;
+ waypointCache.needsSizeRecalc = true;
+});
+
+function UpdateWaypointPosition(scaleX, scaleY, distanceText) {
+ let waypoint = Waypoints[0];
+ if (waypoint == null) {
+ $("#waypoint-container").hide();
+ return;
+ }
+
+ // Initialize cache if needed
+ if (!waypointCache.container) {
+ waypointCache.container = document.getElementById('waypoint-container');
+ waypointCache.distanceElement = document.getElementById('waypoint-distance');
+ waypointCache.width = window.innerWidth;
+ waypointCache.height = window.innerHeight;
+ }
+
+ // Show container if hidden
+ if (waypointCache.container.style.display === 'none') {
+ $("#waypoint-container").show();
+ waypointCache.needsSizeRecalc = true;
+ }
+
+ // Only update distance text if it changed
+ if (distanceText !== waypointCache.lastDistanceText) {
+ waypointCache.distanceElement.innerHTML = `แฏฝ ${distanceText}`;
+ waypointCache.lastDistanceText = distanceText;
+ waypointCache.needsSizeRecalc = true;
+ }
+
+ // Recalculate size only when needed (text changed or first time)
+ if (waypointCache.needsSizeRecalc) {
+ let positionInfo = waypointCache.container.getBoundingClientRect();
+ waypointCache.offsetWidth = positionInfo.width;
+ waypointCache.offsetHeight = positionInfo.height;
+ waypointCache.needsSizeRecalc = false;
+ }
+
+ // Only update position if it changed significantly (reduce jitter)
+ const threshold = 0.001; // ~1 pixel change threshold
+ if (Math.abs(scaleX - waypointCache.lastScaleX) > threshold ||
+ Math.abs(scaleY - waypointCache.lastScaleY) > threshold) {
+ waypointCache.container.style.left = ((waypointCache.width - waypointCache.offsetWidth) * scaleX) + "px";
+ waypointCache.container.style.top = ((waypointCache.height - waypointCache.offsetHeight) * scaleY) + "px";
+ waypointCache.lastScaleX = scaleX;
+ waypointCache.lastScaleY = scaleY;
+ }
+}
+
+function AddWaypoint(addOrRemove) {
+ let element = document.getElementById('waypoint-container');
+ if (addOrRemove) {
+ Waypoints.push({ element: element });
+ // Reset cache
+ waypointCache.needsSizeRecalc = true;
+ waypointCache.lastDistanceText = '';
+ } else {
+ $("#waypoint-container").hide();
+ Waypoints = [];
+ // Clear cache
+ waypointCache.lastDistanceText = '';
+ waypointCache.lastScaleX = -999;
+ waypointCache.lastScaleY = -999;
+ }
+}
+
+// ERS SELECTION MENU
+
+let escapeEventListenerAddedERSSelectionMenu = false;
+let menuSoundInstance = null;
+function ToggleERSSelectionMenu(display, permissions) {
+ if (display) {
+ const hasPoliceAccess = permissions.isPolice;
+ const hasAmbulanceAccess = permissions.isAmbulance;
+ const hasFireAccess = permissions.isFire;
+ const hasTowAccess = permissions.isTow;
+
+ fadeOutMenuMusic(menuSoundInstance)
+ menuSoundInstance = PlayNUISound('generic-sounds', 'selectionmenu', 0.5);
+
+ // HTML
+ const modal = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${language.PoliceDescription}
+
+
+
+
+
+
+
+
+
+
+ ${language.AmbulanceDescription}
+
+
+
+
+
+
+
+
+
+
+ ${language.FireDescription}
+
+
+
+
+
+
+
+
+
+
+ ${language.TowDescription}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+
+ // Add the modal to the page
+ $('body').append(modal);
+
+ // Initialize the modal with static backdrop and disabled keyboard interaction
+ $('#ers-selection-modal').modal({
+ backdrop: 'static',
+ keyboard: false
+ });
+
+ // Show the modal
+ $('#ers-selection-modal').modal('show');
+
+ for (var i = 1; i <= 6; i++) {
+ addSoundOnHoverEventListener('s-btn-' + i);
+ }
+
+ // Handle Escape key press only when the modal is open
+ $('#ers-selection-modal').on('shown.bs.modal', function() {
+ if (!escapeEventListenerAddedERSSelectionMenu) {
+ $(document).on('keydown', function(event) {
+ if (event.key === "Escape") {
+ // Close the modal
+ fadeOutMenuMusic(menuSoundInstance)
+ $('#ers-selection-modal').modal('hide');
+ // Trigger the same action as the "No" button click
+ CancelServiceSelection();
+ // Ensure the event doesn't propagate further
+ event.stopPropagation();
+ }
+ });
+ escapeEventListenerAddedERSSelectionMenu = true;
+ }
+ }).on('hidden.bs.modal', function() {
+ // Remove the Escape key press event listener when the modal is closed
+ $(document).off('keydown');
+ escapeEventListenerAddedERSSelectionMenu = false;
+ });
+
+ } else {
+ // Hide and remove the modal if display is false
+ $('#ers-selection-modal').modal('hide').on('hidden.bs.modal', function () {
+ fadeOutMenuMusic(menuSoundInstance)
+ $(this).remove();
+ });
+ }
+}
+
+function CancelServiceSelection() {
+ $.post(`https://${resourceName}/cancelServiceSelection`, JSON.stringify({}));
+ $('#ers-selection-modal').modal('hide').on('hidden.bs.modal', function () {
+ fadeOutMenuMusic(menuSoundInstance)
+ $(this).data('bs.modal', null); // Remove data object
+ $(this).remove(); // Remove the modal element from DOM
+ });
+}
+
+function SelectService(serviceType) {
+ $.post(`https://${resourceName}/selectedService`, JSON.stringify({
+ service: serviceType
+ }));
+ $('#ers-selection-modal').modal('hide').on('hidden.bs.modal', function () {
+ fadeOutMenuMusic(menuSoundInstance)
+ $(this).data('bs.modal', null); // Remove data object
+ $(this).remove(); // Remove the modal element from DOM
+ });
+}
+
+function fadeOutMenuMusic(soundInstance) {
+ if (soundInstance !== null) {
+ soundInstance.fade(soundInstance.volume(), 0, 5000);
+ setTimeout(function() {
+ soundInstance.stop();
+ }, 5000);
+ }
+}
+
+let escapeEventListenerAddedERSGearnMenu = false;
+
+function DisplayGearMenu(display, locationData) {
+ if (display) {
+
+ fadeOutMenuMusic(menuSoundInstance);
+ menuSoundInstance = PlayNUISound('generic-sounds', 'selectionmenu', 0.5);
+
+ //HTML
+ const modal = `
+
+ `;
+
+ // Add the modal to the page
+ $('body').append(modal);
+
+ var clothingOptions = locationData.ClothingData;
+
+ function getBackgroundColour(serviceType) {
+ switch (serviceType) {
+ case 'police': return 'primary';
+ case 'fire': return 'danger';
+ case 'ambulance': return 'warning';
+ case 'tow': return 'info';
+ default: return 'secondary';
+ }
+ }
+
+ let backgroundColour = getBackgroundColour(locationData.ServiceType);
+
+ // Generate the HTML for each clothing option
+ $.each(clothingOptions, function (key, v) {
+ let locationDataJson = encodeURIComponent(JSON.stringify(locationData));
+
+ // Minify
+ $("#gear-options").append(` `);
+
+ // HTML
+ // $("#gear-options").append(`
+ //
+ //
+ //
+ //
+ //
+ // ${v.Description}
+ //
+ //
+ //
+ //
+ //
+ // `);
+ });
+
+ // Initialize the modal with static backdrop and disabled keyboard interaction
+ $('#ers-gear-modal').modal({
+ backdrop: 'static',
+ keyboard: false
+ });
+
+ // Show the modal
+ $('#ers-gear-modal').modal('show');
+
+ for (var i = 0; i < clothingOptions.length; i++) {
+ addSoundOnHoverEventListener('g-btn-' + i);
+ }
+
+ // Handle Escape key press only when the modal is open
+ $('#ers-gear-modal').on('shown.bs.modal', function() {
+ if (!escapeEventListenerAddedERSGearnMenu) {
+ $(document).on('keydown', function(event) {
+ if (event.key === "Escape") {
+ // Close the modal
+ fadeOutMenuMusic(menuSoundInstance);
+ $('#ers-gear-modal').modal('hide');
+ // Trigger the same action as the "No" button click
+ CancelGearSelection();
+ // Ensure the event doesn't propagate further
+ event.stopPropagation();
+ }
+ });
+ escapeEventListenerAddedERSGearnMenu = true;
+ }
+ }).on('hidden.bs.modal', function() {
+ // Remove the Escape key press event listener when the modal is closed
+ $(document).off('keydown');
+ escapeEventListenerAddedERSGearnMenu = false;
+ });
+
+ } else {
+ // Hide and remove the modal if display is false
+ $('#ers-gear-modal').modal('hide').on('hidden.bs.modal', function () {
+ fadeOutMenuMusic(menuSoundInstance);
+ $(this).remove();
+ });
+ }
+}
+
+function handleClick(button) {
+ let key = button.getAttribute('data-key');
+ let locationDataJson = button.getAttribute('data-location-data');
+ let locationData = JSON.parse(decodeURIComponent(locationDataJson));
+ SelectedGear(key, locationData);
+}
+
+function SelectedGear(key, locationData) {
+ $.post(`https://${resourceName}/selectedGear`, JSON.stringify({
+ clothingIndex: key,
+ locationData: locationData
+ }));
+ $('#ers-gear-modal').modal('hide').on('hidden.bs.modal', function () {
+ fadeOutMenuMusic(menuSoundInstance);
+ $(this).data('bs.modal', null); // Remove data object
+ $(this).remove(); // Remove the modal element from DOM
+ });
+}
+
+function CancelGearSelection() {
+ $.post(`https://${resourceName}/cancelGearSelection`, JSON.stringify({}));
+ $('#ers-gear-modal').modal('hide').on('hidden.bs.modal', function () {
+ fadeOutMenuMusic(menuSoundInstance)
+ $(this).data('bs.modal', null); // Remove data object
+ $(this).remove(); // Remove the modal element from DOM
+ });
+}
+
+///////////////////// UNIFIED PROMPT SYSTEM /////////////////////
+
+///////////////////// USER INTERFACE TOGGLE /////////////////////
+
+const containerIDs = {
+ preCallout: "#callout-pre-interface-container",
+ callout: "#callout-interface-container",
+};
+
+function slideOut(element) {
+ $(element).removeClass('slideInFromLeft').addClass('slideOutToLeft');
+ setTimeout(() => {
+ $(element).hide();
+ updateMinimizedIndicator();
+ }, 300); // Match duration with animation
+}
+
+// Function to show the element with slide-in animation
+function slideIn(element) {
+ $(element).show().removeClass('slideOutToLeft').addClass('slideInFromLeft');
+ updateMinimizedIndicator();
+}
+
+function updateMinimizedIndicator() {
+ const preCalloutHidden = isDisplayingPreCalloutInterface && !$(containerIDs.preCallout).is(':visible');
+ const calloutHidden = isDisplayingCalloutInterface && !$(containerIDs.callout).is(':visible');
+
+ // Show indicator if any interface is hidden
+ if (preCalloutHidden || calloutHidden) {
+ $('#minimized-indicator').fadeIn(200);
+ } else {
+ $('#minimized-indicator').fadeOut(200);
+ }
+}
+
+function toggleCalloutInfo() {
+ // Toggle pre-callout interface based on its initial state
+ if (isDisplayingPreCalloutInterface) {
+ const preCalloutElement = containerIDs.preCallout;
+ if ($(preCalloutElement).is(':visible')) {
+ slideOut(preCalloutElement);
+ } else {
+ slideIn(preCalloutElement);
+ }
+ }
+
+ // Toggle callout interface based on its initial state
+ if (isDisplayingCalloutInterface) {
+ const calloutElement = containerIDs.callout;
+ if ($(calloutElement).is(':visible')) {
+ slideOut(calloutElement);
+ } else {
+ slideIn(calloutElement);
+ }
+ }
+}
+
+// Click handler for minimized indicator
+document.addEventListener('DOMContentLoaded', function() {
+ const minimizedIndicator = document.getElementById('minimized-indicator');
+ if (minimizedIndicator) {
+ minimizedIndicator.addEventListener('click', function() {
+ toggleCalloutInfo();
+ });
+ }
+});
+
+///////////////////// CALLOUT UNIT WAYPOINTS /////////////////////
+
+function updateWaypoints(data) {
+ const waypointData = data.data;
+ const existingWaypoints = trackExistingUnitWaypoints();
+ for (const [key, value] of Object.entries(waypointData)) {
+ if (value !== null) {
+ if (value.distanceText > 0) {
+ processUnitWaypoint(key, value, existingWaypoints);
+ }
+ }
+ }
+ removeUnusedUnitWaypoints(existingWaypoints);
+}
+
+function trackExistingUnitWaypoints() {
+ const existingWaypoints = new Set();
+ document.querySelectorAll('.unit-waypoint-container').forEach(container => {
+ existingWaypoints.add(container.id);
+ });
+ return existingWaypoints;
+}
+
+function processUnitWaypoint(key, value, existingWaypoints) {
+ const waypointId = `unit-waypoint-container-${key}`;
+ let waypointContainer = document.getElementById(waypointId);
+
+ if (!waypointContainer) {
+ waypointContainer = createUnitWaypointContainer(waypointId);
+ }
+
+ updateUnitWaypointPosition(waypointContainer, value);
+ updateUnitWaypointContent(waypointContainer, value);
+
+ existingWaypoints.delete(waypointId);
+}
+
+function createUnitWaypointContainer(waypointId) {
+ const waypointContainer = document.createElement('div');
+ waypointContainer.classList.add('unit-waypoint-container', 'smooth-transition');
+ waypointContainer.id = waypointId;
+ document.body.appendChild(waypointContainer);
+ return waypointContainer;
+}
+
+function updateUnitWaypointPosition(container, value) {
+ const width = window.innerWidth;
+ const height = window.innerHeight;
+ const positionInfo = container.getBoundingClientRect();
+ const offsetWidth = positionInfo.width;
+ const offsetHeight = positionInfo.height;
+
+ if (value.scaleX >= 0 && value.scaleX <= 1 && value.scaleY >= 0 && value.scaleY <= 1) {
+ container.style.left = ((width - offsetWidth) * value.scaleX) + "px";
+ container.style.top = ((height - offsetHeight) * value.scaleY) + "px";
+ }
+}
+
+function updateUnitWaypointContent(container, value) {
+ container.innerHTML = `
+ แฏฝ ${value.distanceText}${value.metrics}
+ `;
+}
+
+function removeUnusedUnitWaypoints(existingWaypoints) {
+ existingWaypoints.forEach(id => {
+ const container = document.getElementById(id);
+ if (container) {
+ container.remove();
+ }
+ });
+}
+
+///////////////////// QUESTIONING /////////////////////
+
+function ToggleQuestionsModule(display, questions) {
+ if (display) {
+ $("#question-options-list").empty();
+ $("#ped-interaction-questioning").show();
+
+ if (questions.length > 0) {
+
+ // Render the questions in a column with spacing
+ let html = `
- ${t.map((e,t)=>`
+ ${questions.map((q, index) => `
- ${wrapText(e.text,30)}
+ ${wrapText(q.text, 30)}
- `).join("")}
+ `).join('')}
- `;$("#question-options-list").append(a),applySavedPosition("ped-interaction-questioning"),t.forEach((e,t)=>{let a=`s-btn-${t+1}`;document.getElementById(a)&&addSoundOnHoverEventListener(a)}),$("#question-options-list button").off("click").on("click",function(){let e=$(this).data("target"),t=$(this).data("question-text"),a=$(this).data("sound-file"),n=$(this).data("sound-volume");e&&(PlayNUISound("generic-sounds","q_select",1),$.post(`https://${resourceName}/questionSelected`,JSON.stringify({questionData:{id:e,text:t,soundFile:a,soundVolume:n}})))})}let n=`
+ `;
+ $("#question-options-list").append(html);
+
+ // Add sound effects to the question buttons
+ questions.forEach((_, index) => {
+ const buttonId = `s-btn-${index + 1}`;
+ if (document.getElementById(buttonId)) {
+ addSoundOnHoverEventListener(buttonId);
+ }
+ });
+
+ // Add click handlers
+ $("#question-options-list button").off("click").on("click", function() {
+ let target = $(this).data("target");
+ let questionText = $(this).data("question-text");
+ let soundFile = $(this).data("sound-file");
+ let soundVolume = $(this).data("sound-volume");
+ if (target) {
+ // Find the full question data for this button
+ const selectedQuestion = {
+ id: target,
+ text: questionText,
+ soundFile: soundFile,
+ soundVolume: soundVolume
+ };
+
+ PlayNUISound('generic-sounds', "q_select", 1.0);
+
+ $.post(`https://${resourceName}/questionSelected`, JSON.stringify({
+ questionData: selectedQuestion
+ }));
+ }
+ });
+ }
+
+ // Leave button
+ let leaveBtn = `
${language.ExitConversation}
- `;$("#question-leave-btn").html(n),document.getElementById("leave-questioning-btn")&&addSoundOnHoverEventListener("leave-questioning-btn"),$("#leave-questioning-btn").off("click").on("click",function(){PlayNUISound("generic-sounds","radialclose",.5),$.post(`https://${resourceName}/leaveQuestioning`,JSON.stringify({}))})}else $("#ped-interaction-questioning").hide(),$("#question-options-list").empty(),$("#question-leave-btn").empty()}function UpdateAnswers(e,t){if(e){let a=$("#question-answers-list");a.empty(),a.html(`
+ `;
+ $("#question-leave-btn").html(leaveBtn);
+
+ // Add sound effect to leave button
+ if (document.getElementById("leave-questioning-btn")) {
+ addSoundOnHoverEventListener("leave-questioning-btn");
+ }
+
+ // Add click handler for leave button
+ $("#leave-questioning-btn").off("click").on("click", function() {
+ PlayNUISound('generic-sounds', "radialclose", 0.5);
+ $.post(`https://${resourceName}/leaveQuestioning`, JSON.stringify({}));
+ });
+
+ } else {
+ $("#ped-interaction-questioning").hide();
+ $("#question-options-list").empty();
+ $("#question-leave-btn").empty();
+ }
+}
+
+function UpdateAnswers(display, answers) {
+ if (display) {
+ // Get the answers container and clear it
+ let answersContainer = $("#question-answers-list");
+
+ // Clear the answers container
+ answersContainer.empty();
+
+ // Add a title above the answers
+ answersContainer.html(`