├── app ├── client │ ├── Browsers │ │ ├── fleeca.png │ │ ├── ATM │ │ │ ├── logo.png │ │ │ ├── transparent.png │ │ │ └── style.css │ │ ├── Menu │ │ │ └── img │ │ │ │ ├── cash.png │ │ │ │ ├── logo.png │ │ │ │ ├── carlock.png │ │ │ │ ├── cloth.png │ │ │ │ ├── repair.png │ │ │ │ ├── carengine.png │ │ │ │ ├── frontdoor.png │ │ │ │ ├── fuelrate.png │ │ │ │ ├── fueltank.png │ │ │ │ ├── loyality.png │ │ │ │ ├── reardoor.png │ │ │ │ ├── vehicles.png │ │ │ │ ├── barbershop.png │ │ │ │ ├── gasstation.png │ │ │ │ └── violations.png │ │ ├── Character │ │ │ ├── range.png │ │ │ ├── second.html │ │ │ ├── style.css │ │ │ └── first.html │ │ ├── Business │ │ │ ├── GasStation │ │ │ │ ├── RON.png │ │ │ │ ├── gs.css │ │ │ │ └── gs.html │ │ │ ├── CheapCarDealership │ │ │ │ ├── img │ │ │ │ │ ├── asea.jpg │ │ │ │ │ ├── radi.jpg │ │ │ │ │ ├── bifta.jpg │ │ │ │ │ ├── blade.jpg │ │ │ │ │ ├── blista.jpg │ │ │ │ │ ├── brioso.jpg │ │ │ │ │ ├── chino.jpg │ │ │ │ │ ├── ingot.jpg │ │ │ │ │ ├── issi2.jpg │ │ │ │ │ ├── manana.jpg │ │ │ │ │ ├── panto.jpg │ │ │ │ │ ├── peyote.jpg │ │ │ │ │ ├── primo.jpg │ │ │ │ │ ├── rebel.jpg │ │ │ │ │ ├── rebel2.jpg │ │ │ │ │ ├── regina.jpg │ │ │ │ │ ├── surge.jpg │ │ │ │ │ ├── voodoo.jpg │ │ │ │ │ ├── asterope.jpg │ │ │ │ │ ├── buccaneer.jpg │ │ │ │ │ ├── dloader.jpg │ │ │ │ │ ├── emperor.jpg │ │ │ │ │ ├── emperor2.jpg │ │ │ │ │ ├── faction.jpg │ │ │ │ │ ├── glendale.jpg │ │ │ │ │ ├── moonbeam.jpg │ │ │ │ │ ├── pigalle.jpg │ │ │ │ │ ├── prairie.jpg │ │ │ │ │ ├── rhapsody.jpg │ │ │ │ │ ├── serrano.jpg │ │ │ │ │ ├── slamvan.jpg │ │ │ │ │ ├── tornado.jpg │ │ │ │ │ ├── tornado2.jpg │ │ │ │ │ ├── tornado3.jpg │ │ │ │ │ ├── tornado4.jpg │ │ │ │ │ ├── tornado5.jpg │ │ │ │ │ ├── voodoo2.jpg │ │ │ │ │ ├── bfinjection.jpg │ │ │ │ │ └── dilettante.jpg │ │ │ │ └── ccd.css │ │ │ ├── CommercialCarDealership │ │ │ │ ├── img │ │ │ │ │ ├── mule.jpg │ │ │ │ │ ├── mule2.jpg │ │ │ │ │ ├── mule3.jpg │ │ │ │ │ └── mule4.jpg │ │ │ │ ├── ccd.css │ │ │ │ └── ccd.html │ │ │ ├── ClothingShop │ │ │ │ └── ch.css │ │ │ ├── BarberShop │ │ │ │ └── bs.css │ │ │ └── business.css │ │ ├── Jobs │ │ │ ├── MariaCollector │ │ │ │ ├── background.jpg │ │ │ │ ├── collector.css │ │ │ │ └── mariacollector.html │ │ │ ├── OrangeCollector │ │ │ │ ├── background.jpg │ │ │ │ ├── collector.css │ │ │ │ └── collector.html │ │ │ └── CluckinBellCourier │ │ │ │ ├── background.jpg │ │ │ │ ├── cluckinbellcourier.css │ │ │ │ └── cluckinbellcourier.html │ │ ├── Factions │ │ │ ├── VisitorsGarage │ │ │ │ ├── Garage │ │ │ │ │ ├── LSHospitalGarage.css │ │ │ │ │ ├── LSPoliceDepartmentGarage.css │ │ │ │ │ ├── main.css │ │ │ │ │ └── garage.html │ │ │ │ └── Lift │ │ │ │ │ ├── main.css │ │ │ │ │ └── lift.html │ │ │ ├── Police │ │ │ │ ├── interactiveMenu.css │ │ │ │ └── interactiveMenu.html │ │ │ └── Hospital │ │ │ │ ├── interactiveMenu.css │ │ │ │ └── interactiveMenu.html │ │ ├── Login │ │ │ ├── style.css │ │ │ ├── login.html │ │ │ └── register.html │ │ └── Misc │ │ │ ├── chooseWindow.css │ │ │ └── chooseWindow.html │ ├── Basic │ │ ├── cGPS.js │ │ ├── cMenu.js │ │ ├── cLogin.js │ │ ├── cMoney.js │ │ └── cVehicle.js │ ├── Business │ │ ├── cBusiness.js │ │ ├── cGasStation.js │ │ ├── cBarberShop.js │ │ ├── cCarDealership.js │ │ └── cClothingShop.js │ ├── Factions │ │ ├── cPolice.js │ │ ├── cPrison.js │ │ ├── cGarage.js │ │ └── cHospital.js │ ├── Jobs │ │ ├── cOrangeCollector.js │ │ ├── cMariaCollector.js │ │ └── cCluckinBellCourier.js │ ├── index.js │ ├── Character │ │ └── cCharacterCreator.js │ ├── 3rd │ │ └── betternotifs.js │ └── cMisc.js └── server │ ├── Factions │ ├── Police │ │ ├── sPolice.js │ │ ├── sPoliceVisitorsGarage.js │ │ ├── sPoliceBuilding.js │ │ └── Prison │ │ │ ├── sPrisonBuilding.js │ │ │ └── sPrison.js │ ├── Hospital │ │ ├── sHospitalVisitorsGarage.js │ │ └── sHospitalBuilding.js │ └── sBuilding.js │ ├── 3rd │ └── betternotifs.js │ ├── index.js │ ├── sMysql.js.example │ ├── Business │ ├── sCarDealership.js │ ├── sCheapCarDealership.js │ ├── sCommercialCarDealership.js │ ├── sClothingShop.js │ └── sBarberShop.js │ ├── sMailer.js.example │ ├── Basic │ ├── sGPS.js │ ├── sTime.js │ ├── Auth │ │ ├── sAuthAbstract.js │ │ ├── sLogin.js │ │ └── sRegister.js │ ├── sWeather.js │ ├── sMenu.js │ ├── sChat.js │ ├── Money │ │ └── sMoney.js │ └── Vehicles │ │ ├── sVehicle.js │ │ └── sVehiclesData.js │ ├── Character │ ├── sHeadOverlay.js │ └── sCharacterCreator.js │ ├── sMisc.js │ └── Jobs │ └── sJob.js ├── .gitignore ├── webpack.config.js ├── .eslintrc.json ├── package.json └── README.md /app/client/Browsers/fleeca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/fleeca.png -------------------------------------------------------------------------------- /app/client/Browsers/ATM/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/ATM/logo.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/cash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/cash.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/logo.png -------------------------------------------------------------------------------- /app/client/Basic/cGPS.js: -------------------------------------------------------------------------------- 1 | mp.events.add({ 2 | "cGPS-CreateRoute" : (x, y) => { 3 | mp.game.ui.setNewWaypoint(x, y); 4 | }, 5 | 6 | }); -------------------------------------------------------------------------------- /app/client/Browsers/ATM/transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/ATM/transparent.png -------------------------------------------------------------------------------- /app/client/Browsers/Character/range.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Character/range.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/carlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/carlock.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/cloth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/cloth.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/repair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/repair.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/carengine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/carengine.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/frontdoor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/frontdoor.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/fuelrate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/fuelrate.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/fueltank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/fueltank.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/loyality.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/loyality.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/reardoor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/reardoor.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/vehicles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/vehicles.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/barbershop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/barbershop.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/gasstation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/gasstation.png -------------------------------------------------------------------------------- /app/client/Browsers/Menu/img/violations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Menu/img/violations.png -------------------------------------------------------------------------------- /app/client/Browsers/Business/GasStation/RON.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/GasStation/RON.png -------------------------------------------------------------------------------- /app/client/Browsers/Jobs/MariaCollector/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Jobs/MariaCollector/background.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Jobs/OrangeCollector/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Jobs/OrangeCollector/background.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/asea.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/asea.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/radi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/radi.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Jobs/CluckinBellCourier/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Jobs/CluckinBellCourier/background.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/bifta.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/bifta.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/blade.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/blade.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/blista.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/blista.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/brioso.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/brioso.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/chino.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/chino.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/ingot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/ingot.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/issi2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/issi2.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/manana.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/manana.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/panto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/panto.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/peyote.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/peyote.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/primo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/primo.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/rebel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/rebel.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/rebel2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/rebel2.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/regina.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/regina.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/surge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/surge.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/voodoo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/voodoo.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/asterope.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/asterope.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/buccaneer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/buccaneer.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/dloader.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/dloader.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/emperor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/emperor.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/emperor2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/emperor2.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/faction.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/faction.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/glendale.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/glendale.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/moonbeam.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/moonbeam.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/pigalle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/pigalle.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/prairie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/prairie.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/rhapsody.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/rhapsody.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/serrano.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/serrano.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/slamvan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/slamvan.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/tornado.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/tornado.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/tornado2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/tornado2.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/tornado3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/tornado3.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/tornado4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/tornado4.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/tornado5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/tornado5.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/voodoo2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/voodoo2.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CommercialCarDealership/img/mule.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CommercialCarDealership/img/mule.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/bfinjection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/bfinjection.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/img/dilettante.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CheapCarDealership/img/dilettante.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CommercialCarDealership/img/mule2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CommercialCarDealership/img/mule2.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CommercialCarDealership/img/mule3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CommercialCarDealership/img/mule3.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Business/CommercialCarDealership/img/mule4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarkCavalli/rageserver/HEAD/app/client/Browsers/Business/CommercialCarDealership/img/mule4.jpg -------------------------------------------------------------------------------- /app/client/Browsers/Factions/VisitorsGarage/Garage/LSHospitalGarage.css: -------------------------------------------------------------------------------- 1 | .b-title { 2 | color: tomato; 3 | } 4 | 5 | .item:hover { 6 | border: 1px solid tomato; 7 | color: tomato; 8 | } 9 | -------------------------------------------------------------------------------- /app/client/Browsers/Factions/VisitorsGarage/Garage/LSPoliceDepartmentGarage.css: -------------------------------------------------------------------------------- 1 | .b-title { 2 | color: dodgerblue; 3 | } 4 | 5 | .item:hover { 6 | border: 1px solid dodgerblue; 7 | color: dodgerblue; 8 | } -------------------------------------------------------------------------------- /app/client/Browsers/Factions/VisitorsGarage/Lift/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | background: rgba(0, 0, 0, 1); 3 | } 4 | 5 | .b-title { 6 | width: 100%; 7 | } 8 | 9 | .items { 10 | left: 40vw; 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/* 2 | /bridge/* 3 | .listcache 4 | server.exe 5 | node.dll 6 | conf.json 7 | bt.dat 8 | *.log 9 | /plugins/* 10 | /packages/* 11 | /client_packages/* 12 | /app/server/sMysql.js 13 | /app/server/sMailer.js -------------------------------------------------------------------------------- /app/client/Basic/cMenu.js: -------------------------------------------------------------------------------- 1 | const misc = require('../cMisc'); 2 | 3 | mp.events.add({ 4 | "cMenu-Open" : (lang, inject) => { 5 | misc.prepareToCef(1); 6 | misc.openCef("package://RP/Browsers/Menu/Menu.html", lang); 7 | misc.injectCef(inject); 8 | }, 9 | 10 | }); -------------------------------------------------------------------------------- /app/client/Business/cBusiness.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const misc = require('../cMisc'); 4 | 5 | mp.events.add({ 6 | "cBusinnes-ShowMenu" : (lang, inject) => { 7 | misc.prepareToCef(500); 8 | misc.openCef("package://RP/Browsers/Business/business.html", lang); 9 | misc.injectCef(inject); 10 | } 11 | }); 12 | -------------------------------------------------------------------------------- /app/client/Factions/cPolice.js: -------------------------------------------------------------------------------- 1 | 2 | const misc = require('../cMisc'); 3 | 4 | mp.events.add( 5 | { 6 | 7 | /* "cHospital-ShowDoctorMenu" : (lang, inject) => { 8 | misc.prepareToCef(500); 9 | misc.openCef("package://RP/Browsers/Factions/Hospital/interactiveMenu.html", lang); 10 | misc.injectCef(inject); 11 | }, */ 12 | 13 | }); 14 | -------------------------------------------------------------------------------- /app/client/Jobs/cOrangeCollector.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const misc = require('../cMisc'); 4 | 5 | mp.events.add({ 6 | "cOrangeCollector-OpenMainMenu" : (lang, inject) => { 7 | misc.prepareToCef(); 8 | misc.openCef("package://RP/Browsers/Jobs/OrangeCollector/collector.html", lang); 9 | misc.injectCef(inject); 10 | }, 11 | }); -------------------------------------------------------------------------------- /app/client/Jobs/cMariaCollector.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const misc = require('../cMisc'); 4 | 5 | mp.events.add({ 6 | "cMariaCollector-OpenMainMenu" : (lang, inject) => { 7 | misc.prepareToCef(); 8 | misc.openCef("package://RP/Browsers/Jobs/MariaCollector/mariacollector.html", lang); 9 | misc.injectCef(inject); 10 | }, 11 | }); -------------------------------------------------------------------------------- /app/server/Factions/Police/sPolice.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const building = require('./sPoliceBuilding'); 4 | const visitorsGarage = require('./sPoliceVisitorsGarage'); 5 | 6 | 7 | const sFaction = require('../sFaction'); 8 | const sBuilding = require('../sBuilding'); 9 | const sGarage = require('../sGarage'); 10 | const misc = require('../../sMisc'); 11 | const i18n = require('../../sI18n'); 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/client/Business/cGasStation.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const misc = require('../cMisc'); 4 | 5 | mp.events.add({ 6 | "cGasStation-OpenBuyerMenu" : (lang, inject, camData) => { 7 | const d = JSON.parse(camData); 8 | misc.createCam(d.x, d.y, d.z, 0, 0, d.rz, d.viewangle); 9 | misc.prepareToCef(); 10 | misc.openCef("package://RP/Browsers/Business/GasStation/gs.html", lang); 11 | misc.injectCef(inject); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /app/client/Business/cBarberShop.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const misc = require('../cMisc'); 4 | const player = mp.players.local; 5 | 6 | mp.events.add( 7 | { 8 | "cBarberShop-ShowBuyerMenu" : (lang, inject, camD) => { 9 | misc.prepareToCef(); 10 | misc.openCef("package://RP/Browsers/Business/BarberShop/bs.html", lang); 11 | misc.injectCef(inject); 12 | misc.createCam(camD.x, camD.y, camD.z, camD.rx, camD.ry, camD.rz, camD.viewangle); 13 | }, 14 | 15 | "cBarberShop-SetHairColor" : (col1, col2) => player.setHairColor(col1, col2) 16 | }); 17 | -------------------------------------------------------------------------------- /app/client/Business/cCarDealership.js: -------------------------------------------------------------------------------- 1 | const misc = require('../cMisc'); 2 | 3 | mp.events.add({ 4 | "cCheapCarDealership-OpenBuyerMenu" : (lang, inject) => { 5 | misc.prepareToCef(500); 6 | misc.openCef("package://RP/Browsers/Business/CheapCarDealership/ccd.html", lang); 7 | misc.injectCef(inject); 8 | }, 9 | 10 | "cCommercialCarDealership-OpenBuyerMenu" : (lang, inject) => { 11 | misc.prepareToCef(500); 12 | misc.openCef("package://RP/Browsers/Business/CommercialCarDealership/ccd.html", lang); 13 | misc.injectCef(inject); 14 | } 15 | }); -------------------------------------------------------------------------------- /app/server/3rd/betternotifs.js: -------------------------------------------------------------------------------- 1 | mp.events.add("playerReady", (player) => { 2 | player.notify = function(message, flashing = false, textColor = -1, bgColor = -1, flashColor = [77, 77, 77, 200]) { 3 | this.call("BN_Show", [message, flashing, textColor, bgColor, flashColor]); 4 | }; 5 | 6 | player.notifyWithPicture = function(title, sender, message, notifPic, icon = 0, flashing = false, textColor = -1, bgColor = -1, flashColor = [77, 77, 77, 200]) { 7 | this.call("BN_ShowWithPicture", [title, sender, message, notifPic, icon, flashing, textColor, bgColor, flashColor]); 8 | }; 9 | }); -------------------------------------------------------------------------------- /app/server/index.js: -------------------------------------------------------------------------------- 1 | require('./Basic/Auth/sLogin'); 2 | require('./Basic/Auth/sRegister'); 3 | require('./Basic/sChat'); 4 | require('./Basic/Money/sATM'); 5 | require('./Business/sBarberShop'); 6 | require('./Business/sClothingShop'); 7 | require('./Business/sCheapCarDealership'); 8 | require('./Business/sCommercialCarDealership'); 9 | require('./Business/sGasStation'); 10 | require('./Jobs/sOrangeCollector'); 11 | require('./Jobs/sMariaCollector'); 12 | require('./Jobs/sCluckinBellCourier'); 13 | require('./Factions/Police/sPolice'); 14 | require('./Basic/sMenu'); 15 | require('./Basic/sGPS'); 16 | require('./3rd/betternotifs.js'); -------------------------------------------------------------------------------- /app/client/Factions/cPrison.js: -------------------------------------------------------------------------------- 1 | mp.events.add( 2 | { 3 | "cPrison-SetWantedLevel" : l => mp.game.gameplay.setFakeWantedLevel(l), 4 | 5 | "cPrison-SendNotification" : (message) => { 6 | const maxStringLength = 99; 7 | mp.game.ui.setNotificationTextEntry("CELL_EMAIL_BCON"); 8 | for (let i = 0, msgLen = message.length; i < msgLen; i += maxStringLength) mp.game.ui.addTextComponentSubstringPlayerName(message.substr(i, Math.min(maxStringLength, message.length - i))); 9 | mp.game.ui.setNotificationMessage("CHAR_CALL911", "CHAR_CALL911", false, 0, 'LS POLICE', `New violation`); 10 | mp.game.ui.drawNotification(false, true); 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /app/client/Factions/cGarage.js: -------------------------------------------------------------------------------- 1 | const misc = require('../cMisc'); 2 | 3 | mp.events.add({ 4 | 5 | "cGarage-ShowVisitorsGarageMenu" : (lang, execute, cam) => { 6 | misc.prepareToCef(1); 7 | misc.openCef("package://RP/Browsers/Factions/VisitorsGarage/Garage/garage.html", lang); 8 | misc.injectCef(execute); 9 | const c = JSON.parse(cam); 10 | misc.createCam(c.x, c.y, c.z, c.rx, c.ry, c.rz, c.viewangle); 11 | }, 12 | 13 | "cGarage-ShowVisitorsLiftMenu" : (lang, execute) => { 14 | misc.prepareToCef(1); 15 | misc.openCef("package://RP/Browsers/Factions/VisitorsGarage/Lift/lift.html", lang); 16 | misc.injectCef(execute); 17 | }, 18 | }); 19 | 20 | -------------------------------------------------------------------------------- /app/server/sMysql.js.example: -------------------------------------------------------------------------------- 1 | 2 | 3 | const mysql = require("mysql"); 4 | 5 | const connection = mysql.createPool({ 6 | host : "localhost", 7 | user : "root", 8 | password : "", 9 | database : "rpserver", 10 | }); 11 | 12 | // NOTE! all mysql queries here is unsecure! You should wrap all the data with connection.escape(data); 13 | // More https://www.tizag.com/mysqlTutorial/mysql-php-sql-injection.php 14 | 15 | connection.getConnection(function(e) { 16 | if (e) { 17 | console.log("DATABASE IS NOT WORKING"); 18 | throw e; 19 | } 20 | else { 21 | console.log(`DATABASE IS WORKING`); 22 | } 23 | }); 24 | 25 | module.exports = connection; -------------------------------------------------------------------------------- /app/client/Basic/cLogin.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const player = mp.players.local; 4 | const misc = require('../cMisc'); 5 | 6 | class loginClass { 7 | constructor() { 8 | mp.events.add({ 9 | "cLogin-ShowLoginWindow" : () => { 10 | misc.prepareToCef(1); 11 | misc.createCam(3223, 5349, 14, 0, 0, 218, 20); 12 | misc.openCef("package://RP/Browsers/Login/login.html"); 13 | }, 14 | "cLogin-ShowRegWindow" : () => { 15 | misc.prepareToCef(1); 16 | misc.createCam(3223, 5349, 14, 0, 0, 218, 20); 17 | misc.openCef("package://RP/Browsers/Login/register.html"); 18 | }, 19 | }); 20 | } 21 | } 22 | 23 | const login = new loginClass(); 24 | -------------------------------------------------------------------------------- /app/client/index.js: -------------------------------------------------------------------------------- 1 | require('./cKeys'); 2 | require('./Basic/cLogin'); 3 | require('./Basic/cMoney'); 4 | require('./Character/cCharacterCreator'); 5 | require('./Business/cBusiness'); 6 | require('./Business/cCarDealership'); 7 | require('./Business/cClothingShop'); 8 | require('./Business/cBarberShop'); 9 | require('./Business/cGasStation'); 10 | require('./Basic/cVehicle'); 11 | require('./Jobs/cCluckinBellCourier'); 12 | require('./Jobs/cOrangeCollector'); 13 | require('./Jobs/cMariaCollector'); 14 | require('./Factions/cGarage.js'); 15 | require('./Factions/cHospital.js'); 16 | require('./Factions/cPolice.js'); 17 | require('./Factions/cPrison.js'); 18 | require('./Basic/cMenu'); 19 | require('./Basic/cGPS'); 20 | require('./3rd/betternotifs.js'); -------------------------------------------------------------------------------- /app/client/Browsers/Login/style.css: -------------------------------------------------------------------------------- 1 | .app { 2 | display: flex; 3 | align-items: center; 4 | } 5 | 6 | .code-block { 7 | display: flex; 8 | align-items: flex-end; 9 | margin: 2vh 0; 10 | } 11 | .code-block > a, .code-block > input { 12 | margin: 0; 13 | } 14 | 15 | .top-button { 16 | display: block; 17 | padding: 2vh; 18 | font-size: 1.1vw; 19 | } 20 | 21 | .top-button:hover { 22 | background: rgba(255, 255, 255, 0.1); 23 | } 24 | 25 | .checked { 26 | border-color: limegreen; 27 | color: limegreen; 28 | } 29 | 30 | .error-message { 31 | background: tomato; 32 | position: fixed; 33 | bottom: 0; 34 | left: 0; 35 | width: 100vw; 36 | padding: 2vh 0; 37 | font-size: 1.25vw; 38 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const nodeExternals = require('webpack-node-externals'); 3 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 4 | 5 | 6 | module.exports = { 7 | resolve: { 8 | extensions: ['.js',] 9 | }, 10 | entry: { 11 | 'packages/RP': './app/server', 12 | 'client_packages': './app/client', 13 | }, 14 | output: { 15 | path: path.resolve(__dirname), 16 | filename: '[name]/index.js' 17 | }, 18 | target: 'node', // in order to ignore built-in modules like path, fs, etc. 19 | externals: [nodeExternals()], // in order to ignore all modules in node_modules folder 20 | plugins: [ 21 | new CopyWebpackPlugin([ 22 | { from: 'app/client/Browsers', to: 'client_packages/RP/Browsers' } 23 | ]) 24 | ] 25 | }; -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb", 4 | "prettier", 5 | "prettier/react" 6 | ], 7 | "plugins": [ 8 | "prettier" 9 | ], 10 | "env": { 11 | "browser": true, 12 | "node": true 13 | }, 14 | "rules":{ 15 | "linebreak-style": 0, 16 | "no-console":0, 17 | "no-shadow": [2, {"allow": ["done", "query"]}], 18 | "no-param-reassign": 0, 19 | "no-plusplus": 0, 20 | "no-new": 0, 21 | "class-methods-use-this": 0, 22 | "no-unused-vars": 0, 23 | "consistent-return": 0, 24 | "no-restricted-syntax": 0, 25 | "no-else-return": 0, 26 | "func-names": 0, 27 | "no-use-before-define": 0, 28 | "one-var": 0, 29 | "prefer-destructuring": 0, 30 | "no-continue": 0 31 | }, 32 | "globals": { 33 | "mp": true, 34 | "player": true, 35 | "console": true 36 | } 37 | } -------------------------------------------------------------------------------- /app/client/Character/cCharacterCreator.js: -------------------------------------------------------------------------------- 1 | const misc = require('../cMisc'); 2 | 3 | const player = mp.players.local; 4 | 5 | mp.events.add({ 6 | "cCharCreator-OpenMenu" : () => { 7 | misc.prepareToCef(); 8 | misc.createCam(402.6, -998.75, -98.32, 0, 0, 358, 15); 9 | misc.openCef("package://RP/Browsers/Character/first.html"); 10 | }, 11 | 12 | "cCharCreator-UpdateSkinOptions" : (strJSON) => { 13 | const skindata = JSON.parse(strJSON); 14 | player.setHeadBlendData(skindata[0], skindata[1], 0, skindata[2], 0, 0, skindata[3], 0, 0, false); 15 | }, 16 | 17 | "cCharCreator-LoadWindowTwo" : () => misc.openCef("package://RP/Browsers/Character/second.html"), 18 | 19 | "cCharCreator-UpdateFaceOptions" : (strJSON) => { 20 | const facedata = JSON.parse(strJSON); 21 | for (let i = 0; i < facedata.length; i++) { 22 | player.setFaceFeature(i, facedata[i]); 23 | } 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /app/client/Browsers/Misc/chooseWindow.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | background: rgba(0, 0, 0, 0.7); 4 | font-family: Segoe UI; 5 | font-weight: 100; 6 | color: #DDD; 7 | text-align: center; 8 | } 9 | 10 | .info { 11 | font-size: 3vw; 12 | padding-top: 15vh; 13 | position: absolute; 14 | left: 5vw; 15 | top: 0; 16 | width: 90vw; 17 | } 18 | 19 | .who { 20 | font-size: 5vw; 21 | padding: 5vh 0; 22 | } 23 | 24 | .buttons { 25 | display: flex; 26 | width: 100vw; 27 | position: absolute; 28 | left: 0; 29 | bottom: 10vh; 30 | justify-content: space-around; 31 | } 32 | 33 | .button { 34 | font-size: 2vw; 35 | padding: 1vh 10vw; 36 | transition: 0.3s; 37 | } 38 | 39 | .reject { 40 | color: tomato; 41 | border: 1px solid tomato; 42 | } 43 | 44 | .reject:hover { 45 | background: tomato; 46 | color: #ddd; 47 | } 48 | 49 | .confirm { 50 | color: #24cc26; 51 | border: 1px solid #24cc26; 52 | } 53 | 54 | .confirm:hover { 55 | background: #24cc26; 56 | color: #ddd; 57 | } 58 | -------------------------------------------------------------------------------- /app/server/Factions/Police/sPoliceVisitorsGarage.js: -------------------------------------------------------------------------------- 1 | const Garage = require('../sGarage'); 2 | 3 | const garageData = { 4 | basic: { 5 | outPos: { x: -1121.295, y: -843.354, z: 12.966, rot: 310.88, dim: 0 }, 6 | outBlipId: 50, 7 | outBlipCol: 3, 8 | outBlipName: "LS Police Department Garage", 9 | outBlipScale: 0.7, 10 | outShapeR: 3, 11 | startDim: 11, 12 | floors: 5, 13 | camData: { x: -1130.153, y: -846.02, z: 13.577, rx: 0, ry: 0, rz: 299.78, viewangle: 30 }, 14 | }, 15 | topExit: { x: -1124.514, y: -839.376, z: 12.981, rot: 129.81, r: 3, dim: 0 }, 16 | undergroundExit: { x: 224.327, y: -1002.948, z: -98.984, rot: 180.96, r: 3, }, // STATIC 17 | undergroundCheckCoord: { x: 231.896, y: -1003.318, z: -98.985, rot: 358, r: 3}, // STATIC 18 | shapesList: [], 19 | checkShapesList: [], 20 | } 21 | 22 | const liftData = { 23 | topEntrance: {x: 445.839, y: -996.392, z: 30.69, rot: 10, r: 1, dim: 0 }, 24 | undergroundEntrance: {x: 241.378, y: -1004.781, z: -99, rot: 88.36, r: 1, }, // STATIC 25 | shapesList: [], 26 | } 27 | new Garage(garageData, liftData); 28 | -------------------------------------------------------------------------------- /app/client/Browsers/Jobs/MariaCollector/collector.css: -------------------------------------------------------------------------------- 1 | html { 2 | min-height: 100%; 3 | background: url(background.jpg) no-repeat scroll left bottom; 4 | background-size: cover; 5 | font-family: Segoe UI; 6 | font-weight: 100; 7 | font-size: 14pt; 8 | text-align: center; 9 | color: #555; 10 | } 11 | 12 | .button { 13 | background-color: #eee; 14 | margin: 15px 0; 15 | padding: 5px 30px; 16 | border-radius: 5px; 17 | transition: 0.3s; 18 | text-transform: uppercase; 19 | } 20 | 21 | .button:hover { 22 | background-color: orange; 23 | } 24 | 25 | .buttons { 26 | position: fixed; 27 | bottom: 5%; 28 | left: 5%; 29 | width: 460px; 30 | } 31 | 32 | .message { 33 | position: fixed; 34 | top: 10%; 35 | left: 5%; 36 | width: 400px; 37 | padding: 30px; 38 | background-color: #eee; 39 | border-radius: 5px; 40 | } 41 | 42 | .b3-close { 43 | background: coral; 44 | position: absolute; 45 | right: 0; 46 | top: 0; 47 | width: 6vh; 48 | height: 3vh; 49 | font-size: 2vh; 50 | transition: 0.3s; 51 | } 52 | 53 | .b3-close:hover { 54 | background: tomato; 55 | } 56 | -------------------------------------------------------------------------------- /app/client/Browsers/Jobs/OrangeCollector/collector.css: -------------------------------------------------------------------------------- 1 | html { 2 | min-height: 100%; 3 | background: url(background.jpg) no-repeat scroll left bottom; 4 | background-size: cover; 5 | font-family: Segoe UI; 6 | font-weight: 100; 7 | font-size: 14pt; 8 | text-align: center; 9 | color: #555; 10 | } 11 | 12 | .button { 13 | background-color: #eee; 14 | margin: 15px 0; 15 | padding: 5px 30px; 16 | border-radius: 5px; 17 | transition: 0.3s; 18 | text-transform: uppercase; 19 | } 20 | 21 | .button:hover { 22 | background-color: orange; 23 | } 24 | 25 | .buttons { 26 | position: fixed; 27 | bottom: 5%; 28 | left: 5%; 29 | width: 460px; 30 | } 31 | 32 | .message { 33 | position: fixed; 34 | top: 10%; 35 | left: 5%; 36 | width: 400px; 37 | padding: 30px; 38 | background-color: #eee; 39 | border-radius: 5px; 40 | } 41 | 42 | .b3-close { 43 | background: coral; 44 | position: absolute; 45 | right: 0; 46 | top: 0; 47 | width: 6vh; 48 | height: 3vh; 49 | font-size: 2vh; 50 | transition: 0.3s; 51 | } 52 | 53 | .b3-close:hover { 54 | background: tomato; 55 | } 56 | -------------------------------------------------------------------------------- /app/client/Browsers/Jobs/CluckinBellCourier/cluckinbellcourier.css: -------------------------------------------------------------------------------- 1 | html { 2 | min-height: 100%; 3 | background: url(background.jpg) no-repeat scroll left bottom; 4 | background-size: cover; 5 | font-family: Segoe UI; 6 | font-weight: 100; 7 | font-size: 14pt; 8 | text-align: center; 9 | color: #555; 10 | } 11 | 12 | .button { 13 | background-color: #eee; 14 | margin: 15px 0; 15 | padding: 5px 30px; 16 | border-radius: 5px; 17 | transition: 0.3s; 18 | text-transform: uppercase; 19 | } 20 | 21 | .button:hover { 22 | background-color: orange; 23 | } 24 | 25 | .buttons { 26 | position: fixed; 27 | bottom: 5%; 28 | left: 5%; 29 | width: 460px; 30 | } 31 | 32 | .message { 33 | position: fixed; 34 | top: 10%; 35 | left: 5%; 36 | width: 400px; 37 | padding: 30px; 38 | background-color: #eee; 39 | border-radius: 5px; 40 | } 41 | 42 | .b3-close { 43 | background: coral; 44 | position: absolute; 45 | right: 0; 46 | top: 0; 47 | width: 6vh; 48 | height: 3vh; 49 | font-size: 2vh; 50 | transition: 0.3s; 51 | } 52 | 53 | .b3-close:hover { 54 | background: tomato; 55 | } 56 | -------------------------------------------------------------------------------- /app/client/Basic/cMoney.js: -------------------------------------------------------------------------------- 1 | 2 | const misc = require('../cMisc'); 3 | 4 | let money; 5 | 6 | mp.events.add( 7 | { 8 | "cMoney-Update" : (value) => money = value, 9 | 10 | "render" : () => { 11 | if (money >= 0 && mp.gui.cursor.visible === false) { 12 | mp.game.graphics.drawText(`$${misc.prettify(money)} `, [0.940, 0.050], { 13 | font: 7, 14 | color: [115, 186, 131, 255], 15 | scale: [0.7, 0.7], 16 | }); 17 | } 18 | }, 19 | 20 | "cMoney-ShowATM" : (lang, execute) => { 21 | misc.prepareToCef(1); 22 | misc.openCef("package://RP/Browsers/ATM/atm.html", lang); 23 | misc.injectCef(execute); 24 | }, 25 | 26 | "cMoney-SendNotification" : (message) => { 27 | const maxStringLength = 99; 28 | mp.game.ui.setNotificationTextEntry("CELL_EMAIL_BCON"); 29 | for (let i = 0, msgLen = message.length; i < msgLen; i += maxStringLength) mp.game.ui.addTextComponentSubstringPlayerName(message.substr(i, Math.min(maxStringLength, message.length - i))); 30 | mp.game.ui.setNotificationMessage("CHAR_BANK_FLEECA", "CHAR_BANK_FLEECA", false, 2, 'FLEECA BANK', `New message`); 31 | mp.game.ui.drawNotification(false, true); 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rpserver", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "log4js": "^6.1.0", 8 | "mysql": "^2.17.1", 9 | "nodemailer": "^6.4.2" 10 | }, 11 | "devDependencies": { 12 | "copy-webpack-plugin": "^5.1.1", 13 | "eslint": "^6.8.0", 14 | "eslint-config-airbnb": "^18.0.1", 15 | "eslint-config-prettier": "^6.8.0", 16 | "eslint-plugin-import": "^2.19.1", 17 | "eslint-plugin-jsx-a11y": "^6.2.3", 18 | "eslint-plugin-prettier": "^3.1.2", 19 | "eslint-plugin-react": "^7.17.0", 20 | "webpack": "^4.41.4", 21 | "webpack-cli": "^3.3.10", 22 | "webpack-node-externals": "^1.7.2" 23 | }, 24 | "scripts": { 25 | "test": "echo \"Error: no test specified\" && exit 1", 26 | "build": "webpack --mode=production", 27 | "watch": "webpack --mode=development --watch" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/MarkCavalli/rageserver.git" 32 | }, 33 | "author": "", 34 | "license": "ISC", 35 | "bugs": { 36 | "url": "https://github.com/MarkCavalli/rageserver/issues" 37 | }, 38 | "homepage": "https://github.com/MarkCavalli/rageserver#readme" 39 | } 40 | -------------------------------------------------------------------------------- /app/server/Business/sCarDealership.js: -------------------------------------------------------------------------------- 1 | 2 | const business = require('./sBusiness'); 3 | const misc = require('../sMisc'); 4 | const vehicleSingletone = require('../Basic/Vehicles/sVehicleSingletone'); 5 | const vehiclesDataSingleton = require('../Basic/Vehicles/sVehiclesData'); 6 | const i18n = require('../sI18n'); 7 | 8 | 9 | 10 | class CarDealership extends business { 11 | constructor(d) { 12 | super(d); 13 | this.newCarCoord = d.newCarCoord; 14 | } 15 | 16 | async buyNewCar(player, model) { 17 | const carPrice = vehiclesDataSingleton.getPrice(model); 18 | if (!carPrice) return; 19 | const shopTax = misc.roundNum(carPrice * this.margin / 400); 20 | const fullPrice = carPrice + shopTax; 21 | const canBuy = await player.changeMoney(-fullPrice); 22 | if (!canBuy) return; 23 | await this.addMoneyToBalance(shopTax); 24 | await vehicleSingletone.saveNewCar(player, model, this.newCarCoord); 25 | 26 | player.notify(`~g~${i18n.get('basic', 'success', player.lang)}`); 27 | misc.log.debug(`${player.name} bought a car ${model} for $${fullPrice}`); 28 | } 29 | 30 | } 31 | module.exports = CarDealership; 32 | 33 | mp.events.add({ 34 | "sCarDealership-BuyCar" : (player, str) => { 35 | const d = JSON.parse(str); 36 | const shop = business.getBusiness(d.id); 37 | shop.buyNewCar(player, d.model); 38 | }, 39 | }); -------------------------------------------------------------------------------- /app/client/Browsers/Factions/Police/interactiveMenu.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | background: rgba(0, 0, 0, 0.7); 4 | font-family: Segoe UI; 5 | font-weight: 100; 6 | color: #DDD; 7 | text-align: center; 8 | } 9 | 10 | .b-patient { 11 | padding: 25vh 0; 12 | position: absolute; 13 | left: 0; 14 | top: 0; 15 | width: 100%; 16 | font-size: 9vh; 17 | line-height: 7vh; 18 | } 19 | 20 | .b-patient > span { 21 | font-size: 2.5vh; 22 | text-transform: uppercase; 23 | } 24 | 25 | .items { 26 | width: 50vw; 27 | position: absolute; 28 | left: 25vw; 29 | top: 40vh; 30 | height: 60vh; 31 | display: flex; 32 | flex-wrap: wrap; 33 | align-content: center; 34 | } 35 | 36 | 37 | .item { 38 | display: flex; 39 | width: 100%; 40 | justify-content: space-between; 41 | font-size: 2vw; 42 | transition: 0.2s; 43 | border: 1px solid #ddd; 44 | margin: 2% 0; 45 | } 46 | 47 | .item:hover { 48 | background-color: rgba(0, 0, 0, 0.2); 49 | } 50 | 51 | .item > div { 52 | padding: 2.5vh; 53 | text-transform: uppercase; 54 | } 55 | 56 | .item_price { 57 | color: limegreen; 58 | } 59 | 60 | .b3-close { 61 | background: coral; 62 | position: absolute; 63 | right: 0; 64 | top: 0; 65 | width: 6vh; 66 | height: 3vh; 67 | font-size: 2vh; 68 | transition: 0.3s; 69 | cursor: pointer; 70 | } 71 | 72 | .b3-close:hover { 73 | background: tomato; 74 | } -------------------------------------------------------------------------------- /app/client/Browsers/Factions/Hospital/interactiveMenu.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | background: rgba(0, 0, 0, 0.7); 4 | font-family: Segoe UI; 5 | font-weight: 100; 6 | color: #DDD; 7 | text-align: center; 8 | } 9 | 10 | .b-patient { 11 | padding: 25vh 0; 12 | position: absolute; 13 | left: 0; 14 | top: 0; 15 | width: 100%; 16 | font-size: 9vh; 17 | line-height: 7vh; 18 | } 19 | 20 | .b-patient > span { 21 | font-size: 2.5vh; 22 | text-transform: uppercase; 23 | } 24 | 25 | .items { 26 | width: 50vw; 27 | position: absolute; 28 | left: 25vw; 29 | top: 40vh; 30 | height: 60vh; 31 | display: flex; 32 | flex-wrap: wrap; 33 | align-content: center; 34 | } 35 | 36 | 37 | .item { 38 | display: flex; 39 | width: 100%; 40 | justify-content: space-between; 41 | font-size: 2vw; 42 | transition: 0.2s; 43 | border: 1px solid #ddd; 44 | margin: 2% 0; 45 | } 46 | 47 | .item:hover { 48 | background-color: rgba(0, 0, 0, 0.2); 49 | } 50 | 51 | .item > div { 52 | padding: 2.5vh; 53 | text-transform: uppercase; 54 | } 55 | 56 | .item_price { 57 | color: limegreen; 58 | } 59 | 60 | .b3-close { 61 | background: coral; 62 | position: absolute; 63 | right: 0; 64 | top: 0; 65 | width: 6vh; 66 | height: 3vh; 67 | font-size: 2vh; 68 | transition: 0.3s; 69 | cursor: pointer; 70 | } 71 | 72 | .b3-close:hover { 73 | background: tomato; 74 | } -------------------------------------------------------------------------------- /app/server/Factions/Hospital/sHospitalVisitorsGarage.js: -------------------------------------------------------------------------------- 1 | const Garage = require('../sGarage'); 2 | const i18n = require('../../sI18n'); 3 | 4 | const garageData = { 5 | basic: { 6 | outPos: { x: -515.651, y: -295.108, z: 34.795, rot: 201.06, dim: 0 }, 7 | outBlipId: 50, 8 | outBlipCol: 1, 9 | outBlipName: "LS Hospital Garage", 10 | outBlipScale: 0.7, 11 | outShapeR: 3, 12 | startDim: 1, 13 | floors: 5, 14 | camData: { x: -514.154, y: -285.073, z: 35.8, rx: 0, ry: 0, rz: 177.02, viewangle: 30 }, 15 | }, 16 | topExit: { x: -460.698, y: -272.399, z: 35.347, rot: 23.34, r: 3, dim: 0 }, 17 | undergroundExit: { x: 224.327, y: -1002.948, z: -98.984, rot: 180.96, r: 3, }, // STATIC 18 | undergroundCheckCoord: { x: 231.896, y: -1003.318, z: -98.985, rot: 358, r: 3}, // STATIC 19 | shapesList: [], 20 | checkShapesList: [], 21 | } 22 | 23 | const liftData = { 24 | topEntrance: { x: 246.519, y: -1372.557, z: 24.50, rot: 316, r: 1, dim: 0 }, 25 | undergroundEntrance: { x: 241.378, y: -1004.781, z: -99, rot: 88.36, r: 1, }, // STATIC 26 | shapesList: [], 27 | } 28 | const garage = new Garage(garageData, liftData); 29 | 30 | garage.enterLift = function(player, floor) { 31 | if (player.health < 75) return player.notify(`~r~${i18n.get('sHospital', 'needHelp', player.lang)}`); 32 | const d = garage.getLiftEnterPos(floor); 33 | player.tp(d); 34 | } -------------------------------------------------------------------------------- /app/client/Business/cClothingShop.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const misc = require('../cMisc'); 4 | let camD; 5 | 6 | mp.events.add( 7 | { 8 | "cClothingShop-ShowBuyerMenu" : (lang, inject, camData) => { 9 | misc.prepareToCef(); 10 | misc.openCef("package://RP/Browsers/Business/ClothingShop/ch.html", lang); 11 | misc.injectCef(inject); 12 | camD = camData; 13 | misc.createCam(camD.x, camD.y, camD.z, camD.rx, camD.ry, camD.rz, camD.viewangle); 14 | }, 15 | 16 | "cClothingShop-SetCamera" : (type) => { 17 | switch (type) { 18 | case "Hats": 19 | misc.createCam(camD.x, camD.y, camD.z + 0.7, camD.rx, camD.ry, camD.rz, camD.viewangle - 20); 20 | break; 21 | 22 | case "Glasses": 23 | misc.createCam(camD.x, camD.y, camD.z + 0.7, camD.rx, camD.ry, camD.rz, camD.viewangle - 25); 24 | break; 25 | 26 | case "Tops": 27 | misc.createCam(camD.x, camD.y, camD.z + 0.4, camD.rx, camD.ry, camD.rz, camD.viewangle - 10); 28 | break; 29 | 30 | case "Legs": 31 | misc.createCam(camD.x, camD.y, camD.z - 0.4, camD.rx, camD.ry, camD.rz, camD.viewangle - 10); 32 | break; 33 | 34 | case "Feet": 35 | misc.createCam(camD.x, camD.y, camD.z - 0.7, camD.rx, camD.ry, camD.rz, camD.viewangle - 20); 36 | break; 37 | 38 | default: 39 | misc.createCam(camD.x, camD.y, camD.z, camD.rx, camD.ry, camD.rz, camD.viewangle); 40 | break; 41 | } 42 | } 43 | }); 44 | -------------------------------------------------------------------------------- /app/client/Browsers/Factions/VisitorsGarage/Garage/main.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | background: rgba(0, 0, 0, 0.5); 4 | font-family: Segoe UI; 5 | font-weight: 100; 6 | color: #DDD; 7 | text-align: center; 8 | } 9 | 10 | .b-title { 11 | padding: 15vh 0; 12 | position: absolute; 13 | left: 0; 14 | top: 0; 15 | width: 50%; 16 | font-size: 8vh; 17 | line-height: 7vh; 18 | color: tomato; 19 | } 20 | 21 | .b-title > span { 22 | font-size: 2.5vh; 23 | text-transform: uppercase; 24 | color: #ddd; 25 | } 26 | 27 | .items { 28 | width: 20vw; 29 | position: absolute; 30 | left: 15vw; 31 | top: 30vh; 32 | height: 70vh; 33 | display: flex; 34 | flex-wrap: wrap; 35 | align-content: center; 36 | } 37 | 38 | 39 | .item { 40 | width: 100%; 41 | font-size: 1.5vw; 42 | transition: 0.2s; 43 | border: 1px solid #ddd; 44 | margin: 3% 0; 45 | } 46 | 47 | .item:hover { 48 | border: 1px solid tomato; 49 | color: tomato; 50 | } 51 | 52 | .item > div { 53 | padding: 1vh; 54 | text-transform: uppercase; 55 | } 56 | 57 | .b3-close { 58 | background: coral; 59 | position: absolute; 60 | right: 0; 61 | top: 0; 62 | width: 6vh; 63 | height: 3vh; 64 | font-size: 2vh; 65 | transition: 0.3s; 66 | cursor: pointer; 67 | } 68 | 69 | .b3-close:hover { 70 | background: tomato; 71 | } -------------------------------------------------------------------------------- /app/server/sMailer.js.example: -------------------------------------------------------------------------------- 1 | const nodemailer = require("nodemailer"); 2 | 3 | 4 | class MailerSingletone { 5 | constructor() { 6 | 7 | // this.createTransport() 8 | } 9 | 10 | createTransport() { 11 | this.transporter = nodemailer.createTransport({ 12 | service: 'gmail', 13 | auth: { 14 | user: 'mail@gmail.com', 15 | pass: 'pass' 16 | }, 17 | tls:{ 18 | rejectUnauthorized: false 19 | } 20 | }); 21 | 22 | this.transporter.verify((error, success) => { 23 | if (error) { 24 | console.log(error); 25 | } 26 | else { 27 | console.log('EMAIL SERVER READY!'); 28 | } 29 | }); 30 | } 31 | 32 | 33 | getMailAdress() { 34 | return 'Open Source RP server '; 35 | } 36 | 37 | isEmailValid(email) { 38 | const re = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; 39 | return re.test(email); 40 | } 41 | 42 | sendMail(message) { 43 | this.transporter.sendMail(message, (err, info) => { 44 | if (err) { 45 | console.log(`Error occurred. ${err.message}`); 46 | return process.exit(1); 47 | } 48 | }); 49 | } 50 | 51 | } 52 | const mailerSingletone = new MailerSingletone(); 53 | module.exports = mailerSingletone; 54 | -------------------------------------------------------------------------------- /app/server/Basic/sGPS.js: -------------------------------------------------------------------------------- 1 | const moneyAPI = require('./Money/sMoney'); 2 | const business = require('../Business/sBusiness'); 3 | 4 | 5 | class GPS { 6 | constructor () { 7 | mp.events.add('sGPS-CreateRoute', (player, str) => { 8 | const d = JSON.parse(str); 9 | let x, y; 10 | if (d.name === "Hospital") { 11 | x = -498.184; 12 | y = -335.741; 13 | } 14 | if (d.name === "Prison") { 15 | x = 1846.283; 16 | y = 2585.906; 17 | } 18 | if (d.name === "Orange Collector") { 19 | x = 405.676; 20 | y = 6526.119; 21 | } 22 | if (d.name === "Maria Collector") { 23 | x = 2212.994; 24 | y = 5577.482; 25 | } 26 | if (d.name === "Clickin Bell Delivery Courier") { 27 | x = -136.757; 28 | y = 6198.713; 29 | } 30 | if (d.name === "ATM") { 31 | const pos = moneyAPI.getNearestATM(player.position); 32 | x = pos.x; 33 | y = pos.y; 34 | } 35 | if (d.name === "Gas Station" || d.name === "Clothing Shop" || d.name === "Barber Shop") { 36 | const pos = business.getNearestBusiness(d.name, player.position); 37 | x = pos.x; 38 | y = pos.y; 39 | } 40 | if (d.name === "Business") { 41 | const pos = business.getBusinessPositionById(d.id); 42 | if (!pos) return; 43 | x = pos.x; 44 | y = pos.y; 45 | } 46 | if (d.name === "Find Vehicle") { 47 | const pos = mp.vehicles.at(d.id).position; 48 | if (!pos) return; 49 | x = pos.x; 50 | y = pos.y; 51 | } 52 | this.createRoute(player, x, y); 53 | }); 54 | 55 | } 56 | 57 | createRoute(player, x, y) { 58 | player.call("cGPS-CreateRoute", [x, y]); 59 | } 60 | 61 | } 62 | new GPS(); 63 | -------------------------------------------------------------------------------- /app/client/Browsers/Factions/VisitorsGarage/Garage/garage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 |
9 | {{ title }}
10 | {{ titleInfo }} 11 |
12 | 13 |
14 |
15 |
{{ floor }}
16 |
17 |
18 | 19 |
X
20 |
21 | 22 | 23 | 24 | 25 | 73 | -------------------------------------------------------------------------------- /app/server/Basic/sTime.js: -------------------------------------------------------------------------------- 1 | const business = require('../Business/sBusiness'); 2 | 3 | 4 | class TimeSingleton { 5 | constructor() { 6 | this.timer = 0; 7 | } 8 | 9 | everyMinuteEvent() { 10 | const players = mp.players.toArray(); 11 | for (const player of players) { 12 | if (!player.loggedIn) return; 13 | player.addHP(); 14 | player.jailEvent(); 15 | } 16 | } 17 | 18 | every5MinutesEvent() { 19 | 20 | } 21 | 22 | everyHourEvent() { 23 | business.payTaxes(); 24 | } 25 | 26 | runTimer(isFirstRunning) { 27 | const currentDate = new Date(); 28 | const remainingMilliseconds = (60 - currentDate.getSeconds()) * 1000 + (1000 - currentDate.getMilliseconds()); 29 | this.changeTime(currentDate, !!isFirstRunning); 30 | clearTimeout(this.timer); 31 | this.timer = setTimeout(() => { 32 | this.runTimer(); 33 | }, remainingMilliseconds); 34 | } 35 | 36 | changeTime(currentDate, isFirstRunning) { 37 | mp.world.time.hour = currentDate.getHours(); 38 | mp.world.time.minute = currentDate.getMinutes(); 39 | if (isFirstRunning) return false; 40 | this.everyMinuteEvent(); 41 | if (currentDate.getMinutes() === 0) this.everyHourEvent(); 42 | if (currentDate.getMinutes() % 5 === 0) this.every5MinutesEvent(); 43 | } 44 | 45 | getTime() { 46 | const currentTime = new Date(); 47 | let h = currentTime.getHours(); 48 | let m = currentTime.getMinutes(); 49 | let s = currentTime.getSeconds(); 50 | if (h < 10) h = `0${h}`; 51 | if (m < 10) m = `0${m}`; 52 | if (s < 10) s = `0${s}`; 53 | return `${h}:${m}:${s}`; 54 | } 55 | } 56 | const timeSingleton = new TimeSingleton(); 57 | timeSingleton.runTimer(true); 58 | module.exports = timeSingleton; -------------------------------------------------------------------------------- /app/client/Jobs/cCluckinBellCourier.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const misc = require('../cMisc'); 4 | 5 | let deliveryPointsListClient = []; 6 | 7 | let blip = []; 8 | 9 | mp.events.add({ 10 | "cCluckinBellCourier-OpenMainMenu": (lang, inject) => { 11 | misc.prepareToCef(); 12 | misc.openCef("package://RP/Browsers/Jobs/CluckinBellCourier/cluckinbellcourier.html", lang); 13 | misc.injectCef(inject); 14 | }, 15 | 16 | "createCluckinBellBlip": (i, blipname) => { 17 | deliveryPointsListClient = []; 18 | for (const pos of i) { 19 | const blip = mp.blips.new(1, new mp.Vector3(pos.x, pos.y, pos.z), { 20 | name: blipname, 21 | shortRange: true, 22 | scale: 0.7, 23 | color: 60, 24 | alpha: 0, 25 | }); 26 | const obj = { blip } 27 | deliveryPointsListClient.push(obj); 28 | } 29 | 30 | mp.events.callRemote('sCluckinBellCourier-NewOrderFromClient'); 31 | }, 32 | 33 | "unrouteCluckinBellBlip": (i) => { 34 | deliveryPointsListClient[i].blip.alpha = 0; 35 | deliveryPointsListClient[i].blip.setDisplay(0); 36 | deliveryPointsListClient[i].blip.setRoute(false); 37 | blip.destroy(); 38 | }, 39 | 40 | "routeCluckinBellBlip": (pos, blipname) => { 41 | blip = mp.blips.new(1, new mp.Vector3(pos.x, pos.y, pos.z), { 42 | name: blipname, 43 | shortRange: true, 44 | scale: 0.7, 45 | color: 60, 46 | alpha: 255, 47 | }); 48 | 49 | blip.setRoute(true); 50 | }, 51 | }); -------------------------------------------------------------------------------- /app/client/Browsers/Business/CheapCarDealership/ccd.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | background: rgba(0, 0, 0, 0.7); 4 | font-family: Segoe UI; 5 | font-weight: 100; 6 | color: #eee; 7 | text-align: center; 8 | } 9 | 10 | .title { 11 | position: fixed; 12 | background: #268F3A; 13 | width: 100%; 14 | top: 0; 15 | left: 0; 16 | padding: 1vh 0; 17 | font-size: 2vh; 18 | } 19 | 20 | 21 | .content { 22 | margin-top: 6vh; 23 | display: flex; 24 | flex-wrap: wrap; 25 | justify-content: space-around; 26 | } 27 | 28 | .item { 29 | margin-bottom: 10px; 30 | width: 250px; 31 | height: 140px; 32 | } 33 | 34 | .item-bg-color { 35 | transition: 0.3s; 36 | } 37 | 38 | .item-bg-color:hover { 39 | background: rgba(0, 0, 0, 0.85); 40 | } 41 | 42 | .item-table { 43 | width: 100%; 44 | text-align: left; 45 | color: #eee; 46 | font-size: 14px; 47 | height: 140px; 48 | } 49 | 50 | .item-table td { 51 | padding: 5px 10px; 52 | } 53 | 54 | .buy { 55 | color: limegreen; 56 | transition: 0.3s; 57 | } 58 | 59 | .buy:hover { 60 | background: green; 61 | color: #DDD; 62 | } 63 | 64 | .right { 65 | text-align: right; 66 | } 67 | 68 | .price:before { 69 | content: "$"; 70 | } 71 | 72 | .hideText td { 73 | color: transparent; 74 | } 75 | 76 | .b3-close { 77 | background: coral; 78 | position: fixed; 79 | right: 0; 80 | top: 0; 81 | width: 6vh; 82 | height: 3vh; 83 | font-size: 2vh; 84 | transition: 0.3s; 85 | cursor: pointer; 86 | z-index: 3; 87 | } 88 | 89 | .b3-close:hover { 90 | background: tomato; 91 | } 92 | -------------------------------------------------------------------------------- /app/client/Browsers/Factions/VisitorsGarage/Lift/lift.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 |
10 | {{ title }}
11 | {{ titleInfo }} 12 |
13 | 14 |
15 |
16 |
{{ floor }}
17 |
18 |
19 | 20 |
X
21 |
22 | 23 | 24 | 25 | 26 | 73 | -------------------------------------------------------------------------------- /app/server/Factions/Police/sPoliceBuilding.js: -------------------------------------------------------------------------------- 1 | 2 | const sBuilding = require('../sBuilding'); 3 | const i18n = require('../../sI18n'); 4 | 5 | 6 | class PoliceBuilding extends sBuilding { 7 | constructor() { 8 | super(); 9 | this.mainEntranceData = { 10 | outPos: {x: -1107.097, y: -846.088, z: 19.317, rot: 129.39, dim: 0}, 11 | inPos: {x: 437.286, y: -978.417, z: 30.69, rot: 178.7, dim: 0}, 12 | 13 | outBlipId: 461, 14 | outBlipCol: 3, 15 | outBlipName: "LS Police Department", 16 | outBlipScale: 1, 17 | outShapeR: 1, 18 | outMarkerId: 1, 19 | outMarkerHeightAdjust: -1, 20 | outMarkerR: 0.75, 21 | outMarkerCol: [30, 144, 255, 15], 22 | 23 | inShapeR: 1, 24 | inMarkerId: 1, 25 | inMarkerHeightAdjust: -1, 26 | inMarkerR: 0.75, 27 | inMarkerCol: [30, 144, 255, 15], 28 | } 29 | this.createMainEntrance(); 30 | } 31 | 32 | createMainEntrance() { 33 | this.mainEntrance = super.createDoubleEntrance(this.mainEntranceData); 34 | } 35 | 36 | enteredBuildingShape(player, entranceId) { 37 | if (entranceId === this.mainEntrance.out.entranceId) { 38 | player.notify(`${i18n.get('basic', 'pressE', player.lang)} ${i18n.get('basic', 'toEnter', player.lang)}`); 39 | } 40 | else if (entranceId === this.mainEntrance.in.entranceId) { 41 | player.notify(`${i18n.get('basic', 'pressE', player.lang)} ${i18n.get('basic', 'toExit', player.lang)}`); 42 | } 43 | } 44 | 45 | tryToEnter(player, entranceId) { 46 | if (entranceId === this.mainEntrance.out.entranceId) { 47 | if (player.vehicle) return; 48 | return player.tp(this.mainEntranceData.inPos); 49 | } 50 | else if (entranceId === this.mainEntrance.in.entranceId) { 51 | if (player.vehicle) return; 52 | player.tp(this.mainEntranceData.outPos); 53 | } 54 | } 55 | 56 | } 57 | const building = new PoliceBuilding(); 58 | module.exports = building; -------------------------------------------------------------------------------- /app/server/Basic/Auth/sAuthAbstract.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | const misc = require('../../sMisc'); 3 | const mailer = require('../../sMailer'); 4 | 5 | 6 | class AbstractAuth { 7 | showError(player, text) { 8 | player.call("cInjectCef", [`app.showError('${text}');`]); 9 | } 10 | 11 | sendCode(player, email) { 12 | const code = misc.getRandomInt(100, 999); 13 | player.verificationCode = code; 14 | player.verificationDate = new Date().getTime(); 15 | player.verificationCodeTries = 0; 16 | const mail = { 17 | from: `${mailer.getMailAdress()}`, 18 | to: `${email}`, 19 | subject: `Verification code: ${code}`, 20 | text: `Hello! Your verification code is: ${code}`, 21 | html: `Hello!
Your verification code is: ${code}`, 22 | } 23 | mailer.sendMail(mail); 24 | player.call("cInjectCef", [`app.showInfo('Please check your mailbox!');`]); 25 | } 26 | 27 | hashPassword(str) { 28 | const cipher = crypto.createCipher('aes192', 'a pass'); 29 | let encrypted = cipher.update(str, 'utf8', 'hex'); 30 | encrypted += cipher.final('hex'); 31 | return encrypted; 32 | } 33 | 34 | canCheckCode(player) { 35 | if (player.verificationCodeTries < 5) return true; 36 | this.showError(player, `Too many wrong codes`); 37 | player.loggedIn = false; 38 | misc.log.warn(`${player.socialClub} too many wrong codes`); 39 | player.kick('You tried wrong codes for too many times.'); 40 | return false; 41 | } 42 | 43 | checkCode(player, code) { 44 | if (!this.canCheckCode(player)) return false; 45 | if (player.verificationCode !== code) { 46 | player.verificationCodeTries++; 47 | this.showError(player, `Wrong code!`); 48 | return false; 49 | } 50 | return true; 51 | } 52 | } 53 | module.exports = AbstractAuth; -------------------------------------------------------------------------------- /app/server/Character/sHeadOverlay.js: -------------------------------------------------------------------------------- 1 | const misc = require('../sMisc'); 2 | 3 | 4 | class HeadOverlaySingletone { 5 | async saveHeadOverlay(player, d) { 6 | if (d.hairStyle) await misc.query(`UPDATE usersHeadOverlay SET hair = '${d.hairStyle}' WHERE id = ${player.guid}`); 7 | else if (d.hairCol1 && d.hairCol2) { 8 | const hairColor = { c1: d.hairCol1, c2: d.hairCol2 }; 9 | await misc.query(`UPDATE usersHeadOverlay SET hairColor = '${JSON.stringify(hairColor)}' WHERE id = ${player.guid}`); 10 | } 11 | else if (d.browStyle && d.browOp) { 12 | const brow = { s: d.browStyle, o: d.browOp } 13 | await misc.query(`UPDATE usersHeadOverlay SET brow = '${JSON.stringify(brow)}' WHERE id = ${player.guid}`); 14 | } 15 | else if (d.beardStyle && d.beardOp) { 16 | const beard = { s: d.beardStyle, o: d.beardOp } 17 | await misc.query(`UPDATE usersHeadOverlay SET beard = '${JSON.stringify(beard)}' WHERE id = ${player.guid}`); 18 | } 19 | } 20 | 21 | loadUser(player) { 22 | player.updateHeadOverlay = async function() { 23 | const d = await misc.query(`SELECT * FROM usersHeadOverlay WHERE id = '${this.guid}'`); 24 | if (d[0].hair) { 25 | this.setClothes(2, d[0].hair, 0, 0); 26 | } 27 | if (d[0].hairColor) { 28 | const c = JSON.parse(d[0].hairColor); 29 | this.setHairColor(c.c1, c.c2); 30 | } 31 | if (d[0].brow) { 32 | const c = JSON.parse(d[0].brow); 33 | this.setHeadOverlay(2, [c.s, c.o, 1, 1]); 34 | } 35 | if (d[0].beard) { 36 | const c = JSON.parse(d[0].beard); 37 | this.setHeadOverlay(1, [c.s, c.o, 1, 1]); 38 | } 39 | } 40 | player.updateHeadOverlay(); 41 | } 42 | 43 | async createNewUser(id) { 44 | const hairColor = { c1: misc.getRandomInt(0, 63), c2: misc.getRandomInt(0, 63) } 45 | const brow = { s: misc.getRandomInt(0, 33), o: 0, } 46 | const beard = { s: misc.getRandomInt(0, 28), o: 0 } 47 | await misc.query(`INSERT INTO usersHeadOverlay (id, hair, hairColor, brow, beard) VALUES ('${id}', '15', '${JSON.stringify(hairColor)}', '${JSON.stringify(brow)}', '${JSON.stringify(beard)}');`); 48 | } 49 | } 50 | const headOverlaySingletone = new HeadOverlaySingletone(); 51 | module.exports = headOverlaySingletone; -------------------------------------------------------------------------------- /app/client/Browsers/Business/ClothingShop/ch.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | font-family: Segoe UI; 4 | font-weight: 100; 5 | color: #DDD; 6 | text-align: center; 7 | } 8 | 9 | .b1 { 10 | position: fixed; 11 | right: 0; 12 | top: 0; 13 | height: 100vh; 14 | width: 20vw; 15 | } 16 | 17 | .item { 18 | height: 6vh; 19 | font-size: 1.5vw; 20 | line-height: 6vh; 21 | } 22 | 23 | .title { 24 | background: #555; 25 | } 26 | 27 | .buttons { 28 | background: #555; 29 | transition: 0.3s; 30 | } 31 | 32 | .buttons:hover { 33 | background: #777; 34 | } 35 | 36 | .b { 37 | position: relative; 38 | background: #666; 39 | } 40 | 41 | .name { 42 | font-size: 1.3vw; 43 | } 44 | 45 | .exit { 46 | position: absolute; 47 | bottom: 0; 48 | left: 0; 49 | background: tomato; 50 | width: 100%; 51 | transition: 0.3s; 52 | } 53 | 54 | .exit:hover { 55 | background: red; 56 | } 57 | 58 | .arrow { 59 | position: absolute; 60 | top: 0; 61 | width: 3vw; 62 | transition: 0.3s; 63 | } 64 | 65 | .arrow:hover { 66 | background: #555; 67 | } 68 | 69 | .l-arrow { 70 | left: 0; 71 | } 72 | 73 | .r-arrow { 74 | right: 0; 75 | } 76 | 77 | .buy { 78 | display: flex; 79 | justify-content: space-around; 80 | transition: 0.3s; 81 | } 82 | 83 | .buy:hover { 84 | background: limegreen; 85 | } 86 | 87 | .buy>div:nth-child(2):before { 88 | content: "$"; 89 | } 90 | 91 | .rotation { 92 | position: fixed; 93 | left: 0; 94 | top: 0; 95 | width: 80vw; 96 | } 97 | 98 | .rotation input:focus { 99 | outline: none; 100 | } 101 | 102 | .rotation input[type=range] { 103 | -webkit-appearance: none; 104 | width: 80vw; 105 | background: url(transparent.png); 106 | } 107 | 108 | .rotation input[type=range]::-webkit-slider-runnable-track { 109 | height: 100vh; 110 | } 111 | 112 | .rotation input[type=range]::-webkit-slider-thumb { 113 | height: 100vh; 114 | width: 10vw; 115 | -webkit-appearance: none; 116 | } 117 | 118 | .reset { 119 | position: absolute; 120 | bottom: 6vh; 121 | left: 0; 122 | width: 100%; 123 | } 124 | -------------------------------------------------------------------------------- /app/server/Business/sCheapCarDealership.js: -------------------------------------------------------------------------------- 1 | 2 | const business = require('./sBusiness'); 3 | const misc = require('../sMisc'); 4 | const i18n = require('../sI18n'); 5 | const carDealership = require('./sCarDealership'); 6 | 7 | 8 | 9 | class CheapCarDealership extends carDealership { 10 | 11 | setLocalSettings() { 12 | this.blip.model = 225; 13 | this.blip.name = `Cheap Car Dealership`; 14 | this.blip.color = 31; 15 | } 16 | 17 | openBuyerMenu(player) { 18 | if (player.vehicle) return; 19 | let execute = `app.id = ${this.id};`; 20 | execute += `app.margin = ${this.margin};`; 21 | 22 | player.call("cCheapCarDealership-OpenBuyerMenu", [player.lang, execute]); 23 | misc.log.debug(`${player.name} enter a cheap car dealership menu`); 24 | } 25 | 26 | } 27 | 28 | async function loadShops() { 29 | const d = await misc.query("SELECT * FROM business INNER JOIN cheapcardealership ON business.id = cheapcardealership.id"); 30 | for (let i = 0; i < d.length; i++) { 31 | new CheapCarDealership(d[i]); 32 | } 33 | } 34 | loadShops(); 35 | 36 | 37 | mp.events.addCommand({ 38 | 'createcheapcardealership' : async (player, enteredprice) => { 39 | if (player.adminlvl < 1) return; 40 | const id = business.getCountOfBusinesses() + 1; 41 | const coord = misc.getPlayerCoordJSON(player); 42 | const price = Number(enteredprice.replace(/\D+/g,"")); 43 | const query1 = misc.query(`INSERT INTO business (title, coord, price) VALUES ('Cheap Car Dealership', '${coord}', '${price}');`); 44 | const query2 = misc.query(`INSERT INTO cheapcardealership (id) VALUES ('${id}');`); 45 | await Promise.all([query1, query2]); 46 | player.outputChatBox("!{#4caf50} Cheap Car Dealership successfully created!"); 47 | player.outputChatBox("!{#4caf50} Now do /setbusbuyermenu [id] and other commands!"); 48 | }, 49 | 50 | 'setccardealernewcarcoord' : async (player, id) => { 51 | if (player.adminlvl < 1) return; 52 | if (!player.vehicle) return player.notify(`~r~You're not in vehicle!`); 53 | const coord = misc.getPlayerCoordJSON(player); 54 | await misc.query(`UPDATE cheapcardealership SET newCarCoord = '${coord}' WHERE id = ${id}`); 55 | player.notify(`~g~${i18n.get('basic', 'success', player.lang)}`); 56 | 57 | }, 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /app/client/Factions/cHospital.js: -------------------------------------------------------------------------------- 1 | 2 | const misc = require('../cMisc'); 3 | 4 | mp.events.add( 5 | { 6 | "render" : () => drawLightInHospital(), 7 | "cHospital-DisableHealthRegeneration" : () => mp.game.player.setHealthRechargeMultiplier(0.0), 8 | 9 | "cHospital-ShowDoctorMenu" : (lang, inject) => { 10 | misc.prepareToCef(500); 11 | misc.openCef("package://RP/Browsers/Factions/Hospital/interactiveMenu.html", lang); 12 | misc.injectCef(inject); 13 | }, 14 | 15 | }); 16 | 17 | // This need to be reworked. 18 | function drawLightInHospital() { 19 | mp.game.graphics.drawLightWithRange(273.552, -1359.888, 26.538, 255, 255, 255, 10, 3); 20 | mp.game.graphics.drawLightWithRange(267.438, -1354.475, 26.538, 255, 255, 255, 10, 3); 21 | mp.game.graphics.drawLightWithRange(264.446, -1360.933, 26.538, 255, 255, 255, 10, 3); 22 | mp.game.graphics.drawLightWithRange(260.973, -1355.263, 26.538, 255, 255, 255, 10, 3); 23 | mp.game.graphics.drawLightWithRange(258.704, -1358.373, 26.538, 255, 255, 255, 10, 3); 24 | mp.game.graphics.drawLightWithRange(253.371, -1364.243, 26.538, 255, 255, 255, 10, 3); 25 | mp.game.graphics.drawLightWithRange(266.289, -1349.066, 26.538, 255, 255, 255, 10, 2); 26 | mp.game.graphics.drawLightWithRange(269.465, -1345.382, 26.538, 255, 255, 255, 10, 2); 27 | mp.game.graphics.drawLightWithRange(272.605, -1341.442, 26.538, 255, 255, 255, 10, 2); 28 | mp.game.graphics.drawLightWithRange(277.381, -1344.953, 26.538, 255, 255, 255, 10, 2); 29 | mp.game.graphics.drawLightWithRange(281.043, -1347.922, 26.538, 255, 255, 255, 10, 2); 30 | mp.game.graphics.drawLightWithRange(287.233, -1340.630, 26.538, 255, 255, 255, 10, 2); 31 | mp.game.graphics.drawLightWithRange(263.554, -1344.159, 26.538, 255, 255, 255, 10, 2); 32 | mp.game.graphics.drawLightWithRange(258.473, -1339.332, 26.538, 255, 255, 255, 10, 2); 33 | mp.game.graphics.drawLightWithRange(257.234, -1346.424, 26.538, 255, 255, 255, 10, 2); 34 | mp.game.graphics.drawLightWithRange(255.189, -1349.291, 26.538, 255, 255, 255, 10, 2); 35 | mp.game.graphics.drawLightWithRange(247.997, -1351.431, 26.538, 255, 255, 255, 10, 2); 36 | mp.game.graphics.drawLightWithRange(245.483, -1354.224, 26.538, 255, 255, 255, 10, 2); 37 | mp.game.graphics.drawLightWithRange(253.495, -1356.144, 26.538, 255, 255, 255, 10, 2); 38 | } 39 | -------------------------------------------------------------------------------- /app/client/3rd/betternotifs.js: -------------------------------------------------------------------------------- 1 | const _SET_NOTIFICATION_COLOR_NEXT = "0x39BBF623FC803EAC"; 2 | const _SET_NOTIFICATION_BACKGROUND_COLOR = "0x92F0DA1E27DB96DC"; 3 | const maxStringLength = 99; 4 | 5 | mp.events.add("BN_Show", (message, flashing = false, textColor = -1, bgColor = -1, flashColor = [77, 77, 77, 200]) => { 6 | if (textColor > -1) mp.game.invoke(_SET_NOTIFICATION_COLOR_NEXT, textColor); 7 | if (bgColor > -1) mp.game.invoke(_SET_NOTIFICATION_BACKGROUND_COLOR, bgColor); 8 | if (flashing) mp.game.ui.setNotificationFlashColor(flashColor[0], flashColor[1], flashColor[2], flashColor[3]); 9 | 10 | mp.game.ui.setNotificationTextEntry("CELL_EMAIL_BCON"); 11 | for (let i = 0, msgLen = message.length; i < msgLen; i += maxStringLength) mp.game.ui.addTextComponentSubstringPlayerName(message.substr(i, Math.min(maxStringLength, message.length - i))); 12 | mp.game.ui.drawNotification(flashing, true); 13 | }); 14 | 15 | mp.events.add("BN_ShowWithPicture", (title, sender, message, notifPic, icon = 0, flashing = false, textColor = -1, bgColor = -1, flashColor = [77, 77, 77, 200]) => { 16 | if (textColor > -1) mp.game.invoke(_SET_NOTIFICATION_COLOR_NEXT, textColor); 17 | if (bgColor > -1) mp.game.invoke(_SET_NOTIFICATION_BACKGROUND_COLOR, bgColor); 18 | if (flashing) mp.game.ui.setNotificationFlashColor(flashColor[0], flashColor[1], flashColor[2], flashColor[3]); 19 | 20 | mp.game.ui.setNotificationTextEntry("CELL_EMAIL_BCON"); 21 | for (let i = 0, msgLen = message.length; i < msgLen; i += maxStringLength) mp.game.ui.addTextComponentSubstringPlayerName(message.substr(i, Math.min(maxStringLength, message.length - i))); 22 | mp.game.ui.setNotificationMessage(notifPic, notifPic, flashing, icon, title, sender); 23 | mp.game.ui.drawNotification(false, true); 24 | }); 25 | 26 | mp.game.ui.notifications = { 27 | show: (message, flashing = false, textColor = -1, bgColor = -1, flashColor = [77, 77, 77, 200]) => mp.events.call("BN_Show", message, flashing, textColor, bgColor, flashColor), 28 | showWithPicture: (title, sender, message, notifPic, icon = 0, flashing = false, textColor = -1, bgColor = -1, flashColor = [77, 77, 77, 200]) => mp.events.call("BN_ShowWithPicture", title, sender, message, notifPic, icon, flashing, textColor, bgColor, flashColor) 29 | }; -------------------------------------------------------------------------------- /app/server/Business/sCommercialCarDealership.js: -------------------------------------------------------------------------------- 1 | 2 | const business = require('./sBusiness'); 3 | const misc = require('../sMisc'); 4 | const i18n = require('../sI18n'); 5 | const carDealership = require('./sCarDealership'); 6 | 7 | 8 | 9 | class CommercialCarDealership extends carDealership { 10 | 11 | setLocalSettings() { 12 | this.blip.model = 634; 13 | this.blip.name = `Commercial Car Dealership`; 14 | this.blip.color = 38; 15 | } 16 | 17 | openBuyerMenu(player) { 18 | if (player.vehicle) return; 19 | let execute = `app.id = ${this.id};`; 20 | execute += `app.margin = ${this.margin};`; 21 | 22 | player.call("cCommercialCarDealership-OpenBuyerMenu", [player.lang, execute]); 23 | misc.log.debug(`${player.name} enter a commercial car dealership menu`); 24 | } 25 | 26 | } 27 | 28 | async function loadShops() { 29 | const d = await misc.query("SELECT * FROM business INNER JOIN commercialcardealership ON business.id = commercialcardealership.id"); 30 | for (let i = 0; i < d.length; i++) { 31 | new CommercialCarDealership(d[i]); 32 | } 33 | } 34 | loadShops(); 35 | 36 | 37 | mp.events.addCommand({ 38 | 'createcommercialcardealership' : async (player, enteredprice) => { 39 | if (player.adminlvl < 1) return; 40 | const id = business.getCountOfBusinesses() + 1; 41 | const coord = misc.getPlayerCoordJSON(player); 42 | const price = Number(enteredprice.replace(/\D+/g,"")); 43 | const query1 = misc.query(`INSERT INTO business (title, coord, price) VALUES ('Commercial Car Dealership', '${coord}', '${price}');`); 44 | const query2 = misc.query(`INSERT INTO commercialcardealership (id) VALUES ('${id}');`); 45 | await Promise.all([query1, query2]); 46 | player.outputChatBox("!{#4caf50} Cheap Car Dealership successfully created!"); 47 | player.outputChatBox("!{#4caf50} Now do /setbusbuyermenu [id] and other commands!"); 48 | }, 49 | 50 | 'setcommercialcardealernewcarcoord' : async (player, id) => { 51 | if (player.adminlvl < 1) return; 52 | if (!player.vehicle) return player.nofity(`~r~You're not in vehicle!`); 53 | const coord = misc.getPlayerCoordJSON(player); 54 | await misc.query(`UPDATE commercialcardealership SET newCarCoord = '${coord}' WHERE id = ${id}`); 55 | player.notify(`~g~${i18n.get('basic', 'success', player.lang)}`); 56 | 57 | }, 58 | 59 | }); 60 | -------------------------------------------------------------------------------- /app/client/Browsers/Business/BarberShop/bs.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | font-family: Segoe UI; 4 | font-weight: 100; 5 | color: #DDD; 6 | text-align: center; 7 | } 8 | 9 | .b1 { 10 | position: fixed; 11 | right: 0; 12 | top: 0; 13 | height: 100vh; 14 | width: 20vw; 15 | } 16 | 17 | .item { 18 | height: 5.5vh; 19 | font-size: 1.5vw; 20 | line-height: 5.5vh; 21 | } 22 | 23 | .title { 24 | background: #555; 25 | } 26 | 27 | .buttons { 28 | background: #555; 29 | transition: 0.3s; 30 | } 31 | 32 | .buttons:hover { 33 | background: #777; 34 | } 35 | 36 | .b { 37 | position: relative; 38 | background: #666; 39 | } 40 | 41 | .name { 42 | font-size: 1.3vw; 43 | } 44 | 45 | .exit { 46 | position: absolute; 47 | bottom: 0; 48 | left: 0; 49 | background: tomato; 50 | width: 100%; 51 | transition: 0.3s; 52 | } 53 | 54 | .exit:hover { 55 | background: red; 56 | } 57 | 58 | .arrow { 59 | position: absolute; 60 | top: 0; 61 | width: 3vw; 62 | transition: 0.3s; 63 | } 64 | 65 | .arrow:hover { 66 | background: #555; 67 | } 68 | 69 | .l-arrow { 70 | left: 0; 71 | } 72 | 73 | .r-arrow { 74 | right: 0; 75 | } 76 | 77 | .buy { 78 | display: flex; 79 | justify-content: space-around; 80 | transition: 0.3s; 81 | } 82 | 83 | .buy:hover { 84 | background: limegreen; 85 | } 86 | 87 | .buy>div:nth-child(2):before { 88 | content: "$"; 89 | } 90 | 91 | .rotation { 92 | position: fixed; 93 | left: 0; 94 | top: 0; 95 | width: 80vw; 96 | } 97 | 98 | .rotation input:focus { 99 | outline: none; 100 | } 101 | 102 | .rotation input[type=range] { 103 | -webkit-appearance: none; 104 | width: 80vw; 105 | background: transparent; 106 | } 107 | 108 | .rotation input[type=range]::-webkit-slider-runnable-track { 109 | height: 100vh; 110 | } 111 | 112 | .rotation input[type=range]::-webkit-slider-thumb { 113 | height: 100vh; 114 | width: 10vw; 115 | -webkit-appearance: none; 116 | } 117 | 118 | .reset { 119 | position: absolute; 120 | bottom: 5vh; 121 | left: 0; 122 | width: 100%; 123 | } -------------------------------------------------------------------------------- /app/server/Basic/sWeather.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const misc = require('../sMisc'); 4 | 5 | 6 | class WeatherSingleton { 7 | constructor() { 8 | this.currentWeather = 0; 9 | this.weathers = 10 | [ "ExtraSunny", // 0 11 | "Clear", // 1 12 | "Clouds", // 2 13 | "Smog", // 3 14 | "Foggy", // 4 15 | "Overcast", // 5 16 | "Rain", // 6 17 | "Thunder", // 7 18 | "Clearing", // 8 19 | "Neutral", // 9 20 | "Blizzard", // 10 21 | "Snowlight", // 11 22 | "Halloween", // 12 23 | "xmax", // 13 24 | ]; 25 | } 26 | 27 | changeWeatherTo(number) { 28 | this.currentWeather = number; 29 | mp.world.setWeatherTransition(this.weathers[this.currentWeather]); 30 | misc.log.trace(`Current weather: ${this.weathers[this.currentWeather]}`); 31 | } 32 | 33 | changeToSunWeather() { 34 | const rnd = misc.getRandomInt(); 35 | let next; 36 | if (rnd <= 30) { 37 | next = 0; 38 | } 39 | else if (rnd > 30 && rnd <= 55) { 40 | next = 1; 41 | } 42 | else if (rnd > 55 && rnd <= 65) { 43 | next = 2; 44 | } 45 | else if (rnd > 65 && rnd <= 75) { 46 | next = 3; 47 | } 48 | else if (rnd > 75 && rnd <= 85) { 49 | next = 4; 50 | } 51 | else if (rnd > 85) { 52 | next = 5; 53 | } 54 | return this.changeWeatherTo(next); 55 | } 56 | 57 | changeWeather() { 58 | const chance = misc.getRandomInt(); 59 | if (chance <= 25) return; // Continue current weather 60 | if (this.currentWeather === 5) { 61 | return this.changeWeatherTo(6); 62 | } 63 | if (this.currentWeather === 6) { 64 | const localChance = misc.getRandomInt(); 65 | if (localChance <= 33) { 66 | return this.changeWeatherTo(7); 67 | } 68 | else { 69 | return this.changeWeatherTo(8) 70 | } 71 | } 72 | if (this.currentWeather === 7) { 73 | return this.changeWeatherTo(6); 74 | } 75 | else { 76 | this.changeToSunWeather(); 77 | } 78 | } 79 | } 80 | const weatherSingleton = new WeatherSingleton(); 81 | weatherSingleton.changeWeather(); 82 | module.exports = weatherSingleton; 83 | 84 | 85 | 86 | mp.events.addCommand( 87 | { 88 | 'w' : (player, fullText, id) => { 89 | if (player.adminlvl < 1) { 90 | return; 91 | } 92 | weatherSingleton.changeWeatherTo(+id); 93 | player.outputChatBox(`Current weather: ${ weatherSingleton.weathers[id]}`); 94 | }, 95 | 96 | }); 97 | 98 | -------------------------------------------------------------------------------- /app/client/Browsers/Factions/Hospital/interactiveMenu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 |
9 | {{ patientName }}
10 | {{ currentPatientText }} 11 |
12 | 13 |
14 |
15 |
{{ increaseText }}
16 |
$500
17 |
18 |
19 |
{{ healText }}
20 |
$5 000
21 |
22 |
23 | 24 |
X
25 |
26 | 27 | 28 | 29 | 30 | 93 | -------------------------------------------------------------------------------- /app/client/Browsers/Factions/Police/interactiveMenu.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 |
9 | {{ patientName }}
10 | {{ currentPatientText }} 11 |
12 | 13 |
14 |
15 |
{{ increaseText }}
16 |
$500
17 |
18 |
19 |
{{ healText }}
20 |
$5 000
21 |
22 |
23 | 24 |
X
25 |
26 | 27 | 28 | 29 | 30 | 93 | -------------------------------------------------------------------------------- /app/client/Browsers/Misc/chooseWindow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 |
{{ whoName }}
9 |
10 | {{ wantText }} 11 |
{{ priceText }} ${{ showPrice() }} 12 |
13 |
14 | 15 |
16 |
{{ rejectText }}
17 |
{{ confirmText }}
18 |
19 |
20 | 21 | 22 | 23 | 24 | 99 | -------------------------------------------------------------------------------- /app/server/Factions/Police/Prison/sPrisonBuilding.js: -------------------------------------------------------------------------------- 1 | const sBuilding = require('../../sBuilding'); 2 | const i18n = require('../../../sI18n'); 3 | 4 | 5 | class PrisonBuilding extends sBuilding { 6 | constructor() { 7 | super(); 8 | this.mainEntranceData = { 9 | outPos: {x: 1846.283, y: 2585.906, z: 45.672, rot: 268.06, dim: 0}, 10 | inPos: {x: 1818.348, y: 2594.317, z: 45.72, rot: 97.25, dim: 0}, 11 | 12 | outShapeR: 1, 13 | outMarkerId: 1, 14 | outMarkerHeightAdjust: -1, 15 | outMarkerR: 0.75, 16 | outMarkerCol: [30, 144, 255, 15], 17 | 18 | inShapeR: 1, 19 | inMarkerId: 1, 20 | inMarkerHeightAdjust: -1, 21 | inMarkerR: 0.75, 22 | inMarkerCol: [30, 144, 255, 15], 23 | } 24 | 25 | this.secondEntranceData = { 26 | outPos: {x: 1690.713, y: 2591.354, z: 45.914, rot: 0, dim: 0}, 27 | inPos: {x: 1689.259, y: 2529.241, z: 45.565, rot: 183.25, dim: 0}, 28 | 29 | outBlipId: 188, 30 | outBlipCol: 3, 31 | outBlipName: "Prison", 32 | outBlipScale: 1, 33 | outShapeR: 1, 34 | } 35 | 36 | this.createMainEntrance(); 37 | this.createSecondEntrance(); 38 | } 39 | 40 | createMainEntrance() { 41 | this.mainEntrance = super.createDoubleEntrance(this.mainEntranceData); 42 | } 43 | 44 | createSecondEntrance() { 45 | this.secondEntrance = super.createSingleEntrance(this.secondEntranceData); 46 | } 47 | 48 | enteredBuildingShape(player, entranceId) { 49 | if (entranceId === this.mainEntrance.out.entranceId) { 50 | player.notify(`${i18n.get('basic', 'pressE', player.lang)} ${i18n.get('basic', 'toEnter', player.lang)}`); 51 | } 52 | else if (entranceId === this.secondEntrance.out.entranceId) { 53 | player.notify(`${i18n.get('basic', 'pressE', player.lang)} to surrender`); 54 | } 55 | else if (entranceId === this.mainEntrance.in.entranceId) { 56 | player.notify(`${i18n.get('basic', 'pressE', player.lang)} ${i18n.get('basic', 'toExit', player.lang)}`); 57 | } 58 | } 59 | 60 | tryToEnter(player, entranceId) { 61 | if (entranceId === this.mainEntrance.out.entranceId) { 62 | this.enterMainEntrance(player); 63 | } 64 | else if (entranceId === this.secondEntrance.out.entranceId) { 65 | this.enterSecondEntrance(player); 66 | } 67 | else if (entranceId === this.mainEntrance.in.entranceId) { 68 | this.exitMainEntrance(player); 69 | } 70 | } 71 | 72 | enterMainEntrance(player) { 73 | if (player.vehicle) return; 74 | player.tp(this.mainEntranceData.inPos); 75 | } 76 | 77 | exitMainEntrance(player) { 78 | if (player.vehicle) return; 79 | player.tp(this.mainEntranceData.outPos); 80 | } 81 | 82 | enterSecondEntrance(player) { 83 | if (player.vehicle) return; 84 | player.startJail(); 85 | } 86 | } 87 | const building = new PrisonBuilding(); 88 | module.exports = building; -------------------------------------------------------------------------------- /app/client/Browsers/Business/GasStation/gs.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | font-family: Segoe UI; 4 | font-weight: 100; 5 | color: #DDD; 6 | text-align: center; 7 | } 8 | 9 | 10 | .left-b { 11 | height: 100%; 12 | background: rgba(0, 0, 0, 0.75); 13 | width: 20vw; 14 | position: absolute; 15 | left: 0; 16 | top: 0; 17 | overflow: hidden; 18 | } 19 | 20 | .dollar:before { 21 | content: '$'; 22 | } 23 | 24 | .info-b { 25 | margin: 3vh 0; 26 | } 27 | 28 | .info-b > img { 29 | width: 10vh; 30 | height: 10vh; 31 | border-radius: 50%; 32 | 33 | } 34 | 35 | .info-b_title { 36 | font-size: 2.8vh; 37 | height: 5vh; 38 | color: orange; 39 | line-height: 5vh; 40 | overflow: hidden; 41 | } 42 | 43 | .info-b_price { 44 | font-size: 2.5vh; 45 | color: limegreen; 46 | } 47 | 48 | .info-b_price_info { 49 | font-size: 1.5vh; 50 | text-transform: uppercase; 51 | } 52 | 53 | .cars { 54 | width: 100%; 55 | overflow: auto; 56 | height: 48%; 57 | position: absolute; 58 | bottom: 0; 59 | 60 | } 61 | 62 | .car { 63 | font-size: 2.5vh; 64 | border-top: 1px solid #AAA; 65 | padding: 1vh 0; 66 | transition: 0.3s; 67 | } 68 | 69 | 70 | .car > span { 71 | display: block; 72 | font-size: 1.8vh; 73 | line-height: 1.8vh; 74 | } 75 | 76 | 77 | .fill { 78 | font-size: 2.5vh; 79 | background: orange; 80 | padding: 1vh 0; 81 | transition: 0.3s; 82 | color: black; 83 | } 84 | 85 | .fill:hover { 86 | background: darkorange; 87 | } 88 | 89 | .fill-info { 90 | font-size: 2.5vh; 91 | padding: 1vh 0; 92 | display: flex; 93 | justify-content: space-around; 94 | } 95 | 96 | .fill-info_price { 97 | color: limegreen; 98 | width: 6vh; 99 | } 100 | 101 | .fill-info_litres { 102 | color: orange; 103 | } 104 | 105 | .fill-info_litres > input { 106 | width: 6vh; 107 | background: transparent; 108 | border: 0; 109 | font-family: Segoe UI; 110 | font-weight: 100; 111 | color: orange; 112 | font-size: 2.5vh; 113 | text-align: right; 114 | } 115 | 116 | .hover:hover { 117 | background: rgba(0, 0, 0, 0.5); 118 | } 119 | 120 | .b3-close { 121 | background: coral; 122 | position: fixed; 123 | right: 0; 124 | top: 0; 125 | width: 6vh; 126 | height: 3vh; 127 | font-size: 2vh; 128 | transition: 0.3s; 129 | z-index: 3; 130 | } 131 | 132 | .b3-close:hover { 133 | background: tomato; 134 | } 135 | 136 | ::-webkit-scrollbar { 137 | width: 0; 138 | } -------------------------------------------------------------------------------- /app/client/Browsers/Business/business.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | background-color: rgba(0, 0, 0, 0.5); 4 | font-family: Segoe UI; 5 | font-weight: 100; 6 | color: #DDD; 7 | text-align: center; 8 | } 9 | 10 | .b3-close { 11 | background: coral; 12 | position: absolute; 13 | right: 0; 14 | top: 0; 15 | width: 6vh; 16 | height: 3vh; 17 | font-size: 2vh; 18 | transition: 0.3s; 19 | z-index: 2; 20 | } 21 | 22 | .b3-close:hover { 23 | background: tomato; 24 | } 25 | 26 | .b1-logo { 27 | background: #268F3A; 28 | width: 100%; 29 | position: absolute; 30 | left: 0; 31 | top: 0; 32 | height: 40vh; 33 | z-index: 1; 34 | display: flex; 35 | flex-wrap: wrap; 36 | position: fixed; 37 | transition: 1s; 38 | } 39 | 40 | .logoLoading { 41 | height: 100vh; 42 | } 43 | 44 | 45 | .logo { 46 | margin: auto; 47 | /*height: 40vh;*/ 48 | } 49 | 50 | .b1-tax { 51 | text-transform: uppercase; 52 | font-size: 2.5vh; 53 | color: white; 54 | } 55 | 56 | .b2 { 57 | position: fixed; 58 | left: 0; 59 | top: 40vh; 60 | width: 100%; 61 | height: 70vh; 62 | } 63 | 64 | .b2-gs { 65 | width: 100%; 66 | background: #DDD; 67 | border-bottom: 1px solid #777; 68 | display: flex; 69 | padding: 2vh 0; 70 | } 71 | 72 | .b2-gs-item { 73 | width: 100%; 74 | text-align: center; 75 | border-left: 1px solid #777; 76 | font-size: 3vh; 77 | color: green; 78 | padding: 0 1vw; 79 | } 80 | 81 | .b2-gs-item:nth-child(1) { 82 | border-left: 1px solid transparent; 83 | } 84 | 85 | .b2-title { 86 | color: #777; 87 | } 88 | 89 | .gs-main { 90 | font-size: 3vh; 91 | } 92 | 93 | .gs-info { 94 | font-size: 1.5vh; 95 | color: #777; 96 | text-transform: uppercase; 97 | } 98 | 99 | .gs-dollar-before:before { 100 | content: "$"; 101 | } 102 | 103 | .gs-percent-after:after { 104 | content: "%"; 105 | } 106 | 107 | .b2-buttons { 108 | display: flex; 109 | flex-wrap: wrap; 110 | width: 100%; 111 | height: 42vh; 112 | position: relative; 113 | } 114 | 115 | .b2-button { 116 | width: calc(100% - 40vw); 117 | background: #268F3A; 118 | text-align: center; 119 | margin: auto 20vw; 120 | color: #EEE; 121 | font-size: 2vh; 122 | padding: 2vh 0; 123 | text-transform: uppercase; 124 | transition: 0.3s; 125 | position: relative; 126 | } 127 | 128 | .b2-button:hover { 129 | background: green; 130 | color: #ddd; 131 | } 132 | 133 | .b2-cb { 134 | position: absolute; 135 | top: 0; 136 | padding: 2vh 0; 137 | width: 7vh; 138 | color: white; 139 | transition: 0.3s; 140 | } 141 | 142 | .b2-cb_left { 143 | left: 0; 144 | } 145 | 146 | .b2-cb_left:hover { 147 | background: tomato; 148 | } 149 | 150 | .b2-cb_right { 151 | right: 0; 152 | } 153 | 154 | .b2-cb_right:hover { 155 | background: limegreen; 156 | } 157 | -------------------------------------------------------------------------------- /app/client/Browsers/Business/CommercialCarDealership/ccd.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | background: rgba(0, 0, 0, 0.4); 4 | font-family: Segoe UI; 5 | font-weight: 100; 6 | color: #eee; 7 | text-align: center; 8 | } 9 | 10 | .close { 11 | background: coral; 12 | position: absolute; 13 | right: 0; 14 | top: 0; 15 | width: 6vh; 16 | height: 3vh; 17 | font-size: 2vh; 18 | transition: 0.3s; 19 | z-index: 2; 20 | } 21 | 22 | .close:hover { 23 | background: tomato; 24 | } 25 | 26 | ::-webkit-scrollbar { 27 | width: 0; 28 | } 29 | 30 | .title { 31 | display: block; 32 | position: fixed; 33 | top: 0; 34 | left: 0; 35 | width: 100%; 36 | background: #333; 37 | font-size: 3vh; 38 | padding: 2vh 0; 39 | } 40 | 41 | .items { 42 | display: flex; 43 | flex-wrap: wrap; 44 | justify-content: space-evenly; 45 | align-items: flex-start; 46 | align-content: flex-start; 47 | position: fixed; 48 | bottom: 0; 49 | left: 0; 50 | width: 100%; 51 | height: 90vh; 52 | padding-top: 2vh; 53 | background: rgba(0, 0, 0, 0.4); 54 | overflow: auto; 55 | } 56 | 57 | .item { 58 | display: flex; 59 | flex-direction: column; 60 | width: 50vh; 61 | border: 1px solid white; 62 | margin-bottom: 3vh; 63 | overflow: hidden; 64 | } 65 | 66 | .item > img { 67 | width: 100%; 68 | } 69 | 70 | .item-info { 71 | background: rgba(0, 0, 0, 0.4); 72 | } 73 | 74 | .item-info > .model { 75 | font-size: 2.75vh; 76 | padding-top: 1vh; 77 | } 78 | 79 | .veh-info_props { 80 | display: flex; 81 | justify-content: space-evenly; 82 | flex-wrap: wrap; 83 | padding: 1vh 0; 84 | } 85 | 86 | .veh-info_prop { 87 | display: flex; 88 | flex-direction: column; 89 | font-size: 1.75vh; 90 | width: 26%; 91 | padding: 1.5vh 0; 92 | transition: 0.2s; 93 | } 94 | 95 | .veh-info_prop > img { 96 | width: 6vh; 97 | height: 6vh; 98 | margin: auto; 99 | } 100 | 101 | .veh-info_prop > span { 102 | color: deepskyblue; 103 | font-size: 1em; 104 | } 105 | 106 | .veh-info_prop > div { 107 | font-size: 0.75em; 108 | text-transform: uppercase; 109 | } 110 | 111 | .item-info > .buy { 112 | font-size: 2vh; 113 | padding: 1vh 0; 114 | color: limegreen; 115 | } 116 | 117 | .veh-item { 118 | display: flex; 119 | flex-direction: column; 120 | padding: 1vh 0; 121 | font-size: 2vh; 122 | position: relative; 123 | transition: 0.3s; 124 | } 125 | 126 | .veh-item.lefted { 127 | transform: translateX(-5vh); 128 | } 129 | 130 | .veh-item .confirm { 131 | position: absolute; 132 | right: -5vh; 133 | top: 0; 134 | height: 100%; 135 | width: 5vh; 136 | background: limegreen; 137 | display: flex; 138 | justify-content: center; 139 | align-items: center; 140 | transition: 0.2s; 141 | z-index: 2; 142 | } 143 | 144 | .veh-item .confirm:hover { 145 | background: green; 146 | } 147 | 148 | .veh-item:hover { 149 | background: rgba(0, 0, 0, 0.4); 150 | } 151 | -------------------------------------------------------------------------------- /app/server/Factions/Hospital/sHospitalBuilding.js: -------------------------------------------------------------------------------- 1 | const sBuilding = require('../sBuilding'); 2 | const i18n = require('../../sI18n'); 3 | 4 | 5 | class HospitalBuilding extends sBuilding { 6 | constructor() { 7 | super(); 8 | this.mainEntranceData = { 9 | outPos: {x: -498.184, y: -335.741, z: 34.502, rot: 263.72, dim: 0}, 10 | inPos: {x: 275.446, y: -1361.11, z: 24.5378, rot: 46.77, dim: 0}, 11 | 12 | outBlipId: 153, 13 | outBlipCol: 1, 14 | outBlipName: "Hospital", 15 | outBlipScale: 1, 16 | outShapeR: 1, 17 | outMarkerId: 1, 18 | outMarkerHeightAdjust: -1, 19 | outMarkerR: 0.75, 20 | outMarkerCol: [255, 0, 0, 15], 21 | 22 | inShapeR: 1, 23 | inMarkerId: 1, 24 | inMarkerHeightAdjust: -1, 25 | inMarkerR: 0.75, 26 | inMarkerCol: [255, 0, 0, 15], 27 | } 28 | this.createMainEntrance(); 29 | this.createHealingShape(); 30 | } 31 | 32 | createMainEntrance() { 33 | this.mainEntrance = super.createDoubleEntrance(this.mainEntranceData); 34 | } 35 | 36 | enteredBuildingShape(player, entranceId) { 37 | if (entranceId === this.mainEntrance.out.entranceId) { 38 | player.notify(`${i18n.get('basic', 'pressE', player.lang)} ${i18n.get('basic', 'toEnter', player.lang)}`); 39 | } 40 | else if (entranceId === this.mainEntrance.in.entranceId) { 41 | player.notify(`${i18n.get('basic', 'pressE', player.lang)} ${i18n.get('basic', 'toExit', player.lang)}`); 42 | } 43 | } 44 | 45 | tryToEnter(player, entranceId) { 46 | if (entranceId === this.mainEntrance.out.entranceId) { 47 | if (player.vehicle) return; 48 | player.tp(this.mainEntranceData.inPos); 49 | } 50 | else if (entranceId === this.mainEntrance.in.entranceId) { 51 | if (player.vehicle) return; 52 | if (player.health < 75) return player.notify(`~r~${i18n.get('sHospital', 'needHelp', player.lang)}`); 53 | player.stopHealing(); 54 | player.tp(this.mainEntranceData.outPos); 55 | } 56 | } 57 | 58 | createHealingShape() { 59 | const startHealCoord = {x: 266.775, y: -1356.136, z: 24.538, rot: 135.53}; 60 | const startHealShape = mp.colshapes.newSphere(startHealCoord.x, startHealCoord.y, startHealCoord.z, 1); 61 | mp.markers.new(1, new mp.Vector3(startHealCoord.x, startHealCoord.y, startHealCoord.z - 1), 0.75, { color: [255, 0, 0, 15] }); 62 | 63 | mp.events.add({ 64 | "playerEnterColshape" : (player, shape) => { 65 | if (!player.loggedIn || shape !== startHealShape) return; 66 | player.canStartHeal = true; 67 | player.notify(`${i18n.get('basic', 'pressE', player.lang)} ${i18n.get('sHospital', 'toStartHealing', player.lang)}`); 68 | }, 69 | 70 | "playerExitColshape" : (player, shape) => { 71 | if (!player.loggedIn || shape !== startHealShape) return; 72 | player.canStartHeal = false; 73 | }, 74 | 75 | "sKeys-E" : (player) => { 76 | if (!player.loggedIn || !player.canStartHeal) return; 77 | player.startHealing(); 78 | }, 79 | }); 80 | } 81 | 82 | } 83 | const hospitalBuilding = new HospitalBuilding(); 84 | module.exports = hospitalBuilding; 85 | -------------------------------------------------------------------------------- /app/server/Character/sCharacterCreator.js: -------------------------------------------------------------------------------- 1 | const misc = require('../sMisc'); 2 | const clothesSingleton = require('./sClothes'); 3 | const headOverlaySingleton = require('./sHeadOverlay'); 4 | 5 | 6 | 7 | class CharCreator { 8 | constructor() { 9 | this.dimension = 2; 10 | 11 | mp.events.add({ 12 | "sCharCreator-ChangeGender" : async (player, gender) => { 13 | if (gender === 0) player.model = 1885233650; 14 | else player.model = -1667301416; 15 | const q2 = clothesSingleton.loadPlayerClothes(player); 16 | const q3 = headOverlaySingleton.loadUser(player); 17 | await Promise.all([q2, q3]); 18 | }, 19 | 20 | "sCharCreator-SaveSkinOptions" : (player, strJSON) => { 21 | misc.query(`UPDATE usersBody SET skindata = '${strJSON}' WHERE id = '${player.guid}'`); 22 | }, 23 | 24 | "sCharCreator-SaveFaceOptions" : async (player, strJSON) => { 25 | let gender = 'w'; 26 | if (player.model === 1885233650) gender = 'm'; 27 | await misc.query(`UPDATE usersBody SET gender = '${gender}', facedata = '${strJSON}' WHERE id = '${player.guid}'`); 28 | 29 | const q1 = this.loadPlayerBody(player); 30 | const q2 = clothesSingleton.loadPlayerClothes(player); 31 | const q3 = headOverlaySingleton.loadUser(player); 32 | await Promise.all([q1, q2, q3]); 33 | const firstSpawn = { 34 | x: -164, 35 | y: 6426, 36 | z: 32, 37 | rot: 48, 38 | dim: 0, 39 | } 40 | player.tp(firstSpawn); 41 | 42 | }, 43 | }); 44 | } 45 | 46 | changeDimenstion() { 47 | this.dimension++ 48 | if (this.dimension === 500) this.dimension = 2; 49 | } 50 | 51 | openMenu(player) { 52 | player.model = 1885233650; 53 | player.position = new mp.Vector3(402.55, -996.37, -99.01); 54 | player.heading = 180; 55 | player.dimension = this.dimension; 56 | this.changeDimenstion(); 57 | player.call("cCharCreator-OpenMenu"); 58 | } 59 | 60 | async createNewUser(id) { 61 | await misc.query(`INSERT INTO usersBody (id, gender) VALUES ('${id}', NULL);`); 62 | } 63 | 64 | async loadPlayerBody(player) { 65 | const d = await misc.query(`SELECT * FROM usersBody WHERE id = '${player.guid}'`); 66 | if (!d[0].gender) return this.openMenu(player); 67 | if (d[0].gender === 'm') player.model = 1885233650; 68 | else if (d[0].gender === 'w') player.model = 2147483647; 69 | this.setBody(player, d[0].skindata); 70 | this.setFace(player, d[0].facedata); 71 | } 72 | 73 | setBody(player, strJSON) { 74 | if (!misc.isValueString(strJSON)) return misc.log.error(`sCharacterCreator-setBody | str is not a string: ${strJSON}`); 75 | const skindata = JSON.parse(strJSON); 76 | player.setHeadBlend(skindata[0], skindata[1], 0, skindata[2], 0, 0, skindata[3], 0, 0); 77 | } 78 | 79 | setFace(player, strJSON) { 80 | if (!misc.isValueString(strJSON)) return misc.log.error(`sCharacterCreator-setFace | str is not a string: ${strJSON}`); 81 | const facedata = JSON.parse(strJSON); 82 | for (let i = 0; i < 20; i++) { 83 | player.setFaceFeature(i, facedata[i]); 84 | } 85 | } 86 | } 87 | 88 | const charCreator = new CharCreator(); 89 | module.exports = charCreator; -------------------------------------------------------------------------------- /app/client/Basic/cVehicle.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | const misc = require('../cMisc'); 4 | const player = mp.players.local; 5 | 6 | class cVehicle { 7 | constructor() { 8 | this.fuel = null; 9 | this.fuelRate = 0; 10 | this.speed = 0; 11 | 12 | mp.events.add({ 13 | "cVehicle-setFuel" : (fuel, fuelRate) => this.setFuel(fuel, fuelRate), 14 | 15 | "playerLeaveVehicle" : () => { 16 | if (this.fuel !== null) mp.events.callRemote('sVehicle-SetFuel', player.vehicle, this.fuel); 17 | }, 18 | 19 | "cVehicle-setLights" : (vehicle, state) => { 20 | vehicle.setLights(state); 21 | }, 22 | 23 | "cVehicle-rollUpWindow" : (vehicle, window) => vehicle.rollUpWindow(window), 24 | "cVehicle-rollDownWindow" : (vehicle, window) => vehicle.rollDownWindow(window), 25 | 26 | "render" : () => { 27 | this.setLightMultiplier(); 28 | this.showSpeed(); 29 | this.showFuel(); 30 | this.showBrakeLights(); 31 | }, 32 | }); 33 | } 34 | 35 | setFuel(fuel, fuelRate, showSpeed) { 36 | if (typeof fuel !== "number") return this.fuel = null; 37 | this.fuel = fuel; 38 | this.fuelRate = fuelRate; 39 | } 40 | 41 | setLightMultiplier() { 42 | if (player.vehicle) player.vehicle.setLightMultiplier(4); 43 | } 44 | 45 | showSpeed() { 46 | const vehicle = player.vehicle; 47 | if (!vehicle || mp.gui.cursor.visible) return; 48 | this.speed = misc.roundNum(vehicle.getSpeed() * 4); 49 | mp.game.graphics.drawText(" Speed: " + this.speed + " km/h", [0.920, 0.835], { 50 | font: 1, 51 | color: [255, 255, 255, 255], 52 | scale: [0.6, 0.6], 53 | }); 54 | } 55 | 56 | showFuel() { 57 | const vehicle = player.vehicle; 58 | if (mp.gui.cursor.visible || !vehicle || this.fuel === null || !vehicle.getIsEngineRunning()) return; 59 | mp.game.graphics.drawText(" Fuel: " + this.fuel.toFixed(1) + " L", [0.927, 0.80], { 60 | font: 1, 61 | color: [255, 255, 255, 255], 62 | scale: [0.6, 0.6], 63 | }); 64 | const rpm = misc.roundNum(vehicle.rpm * 5000); 65 | let gear = vehicle.gear; 66 | if (gear === 0) gear = 1; 67 | 68 | this.fuel -= (rpm + (this.speed * 400)) / gear * this.fuelRate * Math.pow(5, -13); 69 | 70 | if (this.fuel < 0.1) mp.events.callRemote('sVehicle-SetFuel', vehicle, this.fuel); 71 | } 72 | 73 | showBrakeLights() { 74 | if (!player.vehicle || this.speed !== 0) return; 75 | player.vehicle.setBrakeLights(true); 76 | } 77 | 78 | getIntoVehicleAsPassenger() { 79 | if (mp.gui.cursor.visible || player.vehicle) return; 80 | const pos = player.position; 81 | const vehHandle = mp.game.vehicle.getClosestVehicle(pos.x, pos.y, pos.z, 5, 0, 70); 82 | const vehicle = mp.vehicles.atHandle(vehHandle); 83 | 84 | if (!vehicle || !vehicle.isAnySeatEmpty() || vehicle.getSpeed() > 5) return; 85 | 86 | for (let i = 0; i < vehicle.getMaxNumberOfPassengers(); i++) { 87 | if (!vehicle.isSeatFree(i)) continue; 88 | player.taskEnterVehicle(vehicle.handle, 5000, i, 1, 1, 0); 89 | break; 90 | } 91 | } 92 | } 93 | 94 | const veh = new cVehicle(); 95 | 96 | mp.keys.bind(71, false, function() { // G 97 | veh.getIntoVehicleAsPassenger(); 98 | }); 99 | -------------------------------------------------------------------------------- /app/client/Browsers/Business/CommercialCarDealership/ccd.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
X
8 |
{{ i18n.dealer }} №{{ id }}
9 |
10 |
11 | 12 |
13 |
{{ v.title }}
14 |
15 |
16 | 17 | {{ v.fuelTank }} L 18 |
{{ i18n.fuelTank }}
19 |
20 |
21 | 22 | ${{ prettify(v.price + v.price * margin / 400) }} 23 |
{{ i18n.price }}
24 |
25 |
26 | 27 | {{ v.fuelRate }}L / 100 km 28 |
{{ i18n.fuelRate }}
29 |
30 |
31 | 32 | {{ i18n.buy }} 33 |
34 |
35 |
36 |
37 |
38 |
39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/client/cMisc.js: -------------------------------------------------------------------------------- 1 | let cef = null; 2 | let camera = null; 3 | const player = mp.players.local; 4 | 5 | function prettify(num) { 6 | const n = num.toString(); 7 | const separator = " "; 8 | return n.replace(/(\d{1,3}(?=(?:\d\d\d)+(?!\d)))/g, `$1${separator}`); 9 | } 10 | exports.prettify = prettify; 11 | 12 | const roundNum = (number, ends = 0) => parseFloat(number.toFixed(ends)) 13 | exports.roundNum = roundNum; 14 | 15 | // CEF // 16 | function prepareToCef(blurred = null) { 17 | mp.gui.cursor.visible = true; 18 | mp.game.ui.displayRadar(false); 19 | mp.gui.chat.show(false); 20 | if (blurred) mp.game.graphics.transitionToBlurred(blurred); 21 | } 22 | exports.prepareToCef = prepareToCef; 23 | 24 | 25 | function injectCef(execute) { 26 | if(!cef) return; 27 | cef.execute(execute); 28 | } 29 | exports.injectCef = injectCef; 30 | 31 | 32 | function openCef(url, lang = "eng") { 33 | if (cef) cef.destroy(); 34 | cef = mp.browsers.new(url); 35 | if (lang === "rus") injectCef("loadRusLang();"); 36 | else if (lang === "ger") injectCef("loadGerLang();"); 37 | else if (lang === "br") injectCef("loadBrLang();"); 38 | else if (lang === "zhs") injectCef("loadZhsLang();"); 39 | else if (lang === "zht") injectCef("loadZhtLang();"); 40 | else if (lang === "cs") injectCef("loadCsLang();"); 41 | } 42 | exports.openCef = openCef; 43 | 44 | 45 | function closeCef() { 46 | if (cef) { 47 | cef.destroy(); 48 | cef = null; 49 | } 50 | mp.gui.cursor.visible = false; 51 | mp.game.ui.displayRadar(true); 52 | mp.gui.chat.show(true); 53 | mp.game.graphics.transitionFromBlurred(1); 54 | } 55 | exports.closeCef = closeCef; 56 | // CEF // 57 | 58 | // CAMERA // 59 | function createCam(x, y, z, rx, ry, rz, viewangle) { 60 | camera = mp.cameras.new("Cam", {x, y, z}, {x: rx, y: ry, z: rz}, viewangle); 61 | camera.setActive(true); 62 | mp.game.cam.renderScriptCams(true, true, 20000000000000000000000000, false, false); 63 | } 64 | exports.createCam = createCam; 65 | 66 | function destroyCam() { 67 | if (!camera) return; 68 | camera.setActive(false); 69 | mp.game.cam.renderScriptCams(false, true, 0, true, true); 70 | camera.destroy(); 71 | camera = null; 72 | } 73 | exports.destroyCam = destroyCam; 74 | // CAMERA // 75 | 76 | mp.events.add( 77 | { 78 | "cInjectCef" : execute => injectCef(execute), 79 | "cCloseCef" : () => closeCef(), 80 | "cDestroyCam" : () => destroyCam(), 81 | 82 | "cCloseCefAndDestroyCam" : () => { 83 | closeCef(); 84 | destroyCam(); 85 | }, 86 | 87 | "cChangeHeading" : angle => player.setHeading(angle), 88 | 89 | "cMisc-CreateChooseWindow" : (lang, execute, confirmEvent, rejectEvent) => { 90 | prepareToCef(500); 91 | openCef("package://RP/Browsers/Misc/chooseWindow.html", lang); 92 | const str1 = `app.confirmEvent = '${confirmEvent}';`; 93 | const str2 = `app.rejectEvent = '${rejectEvent}';`; 94 | const inject = execute + str1+ str2; 95 | injectCef(inject); 96 | }, 97 | 98 | "cMisc-CallServerEvent" : (eventName, id, price) => mp.events.callRemote(eventName, id, price), 99 | 100 | "cMisc-CallServerEvenWithTimeout" : (eventName, timeout) => { 101 | setTimeout(() => { 102 | mp.events.callRemote(eventName); 103 | }, timeout); 104 | } 105 | 106 | }); 107 | 108 | -------------------------------------------------------------------------------- /app/client/Browsers/Jobs/CluckinBellCourier/cluckinbellcourier.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
X
8 | 9 |
10 | {{ messageText1 }} 11 |

12 | {{ messageText2 }} 13 |

14 | {{ messageText3 }} 15 |

16 | {{ messageText4 }} 17 |
18 | 19 |
20 |
{{ startText }}
21 |
{{ finishText }}
22 |
23 |
24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rageserver 2 | Open source rage mp role play server 3 | 4 | Hello! 5 | All about this server you can read at: 6 | https://rage.mp/forums/topic/1559-open-source-role-play-server/ 7 | 8 | 9 | # NOTE! all mysql queries here is unsecure! Read sMailer.js for more info 10 | 11 | # Installation guide: 12 | 1. Install Ragemp server. 13 | 2. Put all files in your project. 14 | 3. Run `npm i` by cmd in main directory with "server.exe" excutable script. 15 | 4. Server using MySQL as a database. So you have to import sql file in to your database. In database management make one like "ragerp". 16 | Import sql structure file (rpserver.sql) into it. 17 | 5. Rename sMailer.js.example and sMysql.js.example to sMailer.js and sMysql.js in ./app/server/,then modify the settings.(More sMailer instructions 18 | see https://nodemailer.com/usage/) 19 | 6. Modify files in `app` directory (if you need). 20 | 7. Do `npm run build` by cmd in main directory (BESURE DO THIS every time after some improvements in 'app' directory). 21 | or `npm run watch` for watching for changes and autorebuild 22 | 8. Start a server 23 | 24 | THEN ENJOY. 25 | 26 | ## Command list (21st of March, 2019) 27 | 28 | ### Basic - Chat: `/do` 29 | For more information view source code. 30 | ### Basic - Chat: `/g` 31 | For more information view source code. 32 | ### Basic - Chat: `/me` 33 | For more information view source code. 34 | ### Basic - Player: `/pos` 35 | For more information view source code. 36 | ### Basic - Player: `/save` 37 | For more information view source code. 38 | ### Basic - Weather: `/w` 39 | For more information view source code. 40 | ### Basic - Vehicles: `/tp` 41 | For more information view source code. 42 | ### Basic - Vehicles: `/v` 43 | For more information view source code. 44 | ### Basic - Vehicles: `/veh` 45 | For more information view source code. 46 | ### Business - BarberShop: `/createbarbershop` 47 | For more information view source code. 48 | ### Business - BarberShop: `/setbscamdata` 49 | For more information view source code. 50 | ### Business - Business: `/setbusbuyermenu` 51 | For more information view source code. 52 | ### Business - CheapCarDealership: `/createcheapcardealership` 53 | For more information view source code. 54 | ### Business - CheapCarDealership: `/setccardealernewcarcoord` 55 | For more information view source code. 56 | ### Business - ClothingShop: `/createclothingshop` 57 | For more information view source code. 58 | ### Business - ClothingShop: `/setchbuyerstandcoord` 59 | For more information view source code. 60 | ### Business - ClothingShop: `/setchcamdata` 61 | For more information view source code. 62 | ### Business - CommercialCarDealership: `/createcommercialcardealership` 63 | For more information view source code. 64 | ### Business - CommercialCarDealership: `/setcommercialcardealernewcarcoord` 65 | For more information view source code. 66 | ### Business - GasStation: `/creategasstation` 67 | For more information view source code. 68 | ### Business - GasStation: `/setgasstationcamdata` 69 | For more information view source code. 70 | ### Business - GasStation: `/setgasstationfillingpos` 71 | For more information view source code. 72 | ### Factions - Hospital: `/sethospitalleader` 73 | For more information view source code. 74 | ### Factions - Faction: `/invite` 75 | For more information view source code. 76 | ### Factions - Faction: `/setrank` 77 | For more information view source code. 78 | ### Factions - Faction: `/uninvite` 79 | For more information view source code. 80 | -------------------------------------------------------------------------------- /app/server/Factions/sBuilding.js: -------------------------------------------------------------------------------- 1 | const misc = require('../sMisc'); 2 | 3 | 4 | const buildingList = []; 5 | let entranceId = 1; 6 | 7 | 8 | class building { 9 | constructor() { 10 | this.id = buildingList.length + 1; 11 | buildingList.push(this); 12 | } 13 | 14 | createBlip(id, pos, color, name, scale, dim) { 15 | if (!misc.isValueNumber(id) || !misc.isValueNumber(color) || !misc.isValueString(name) || !misc.isValueNumber(scale) || !misc.isValueNumber(dim)) return; 16 | return mp.blips.new(id, new mp.Vector3(pos.x, pos.y, pos.z),{ shortRange: true, color, name, scale, dimension: dim }); 17 | } 18 | 19 | createMarker(id, pos, height, radius, color, dim) { 20 | if (!misc.isValueNumber(id) || !misc.isValueNumber(height) || !misc.isValueNumber(radius) || !misc.isValueNumber(dim)) return; 21 | return mp.markers.new(id, new mp.Vector3(pos.x, pos.y, pos.z + height), radius, { color, dimension: dim }); 22 | } 23 | 24 | createDoubleEntrance(d) { 25 | if (d.outBlipId) this.createBlip(d.outBlipId, d.outPos, d.outBlipCol, d.outBlipName, d.outBlipScale, d.outPos.dim); 26 | if (d.inBlipId) this.createBlip(d.inBlipId, d.inPos, d.inBlipCol, d.inBlipName, d.inBlipScale, d.inPos.dim); 27 | 28 | const outShape = mp.colshapes.newSphere(d.outPos.x, d.outPos.y, d.outPos.z, d.outShapeR); 29 | outShape.dimension = d.outPos.dim; 30 | this.setShapeData(outShape); 31 | const inShape = mp.colshapes.newSphere(d.inPos.x, d.inPos.y, d.inPos.z, d.inShapeR); 32 | inShape.dimension = d.inPos.dim; 33 | this.setShapeData(inShape); 34 | 35 | if (d.outMarkerId) this.createMarker(d.outMarkerId, d.outPos, d.outMarkerHeightAdjust, d.outMarkerR, d.outMarkerCol, d.outPos.dim); 36 | if (d.inMarkerId) this.createMarker(d.inMarkerId, d.inPos, d.inMarkerHeightAdjust, d.inMarkerR, d.inMarkerCol, d.outPos.dim); 37 | 38 | const obj = { out: outShape, in: inShape } 39 | return obj; 40 | } 41 | 42 | createSingleEntrance(d) { 43 | if (d.outBlipId) this.createBlip(d.outBlipId, d.outPos, d.outBlipCol, d.outBlipName, d.outBlipScale, d.outPos.dim); 44 | if (d.outMarkerId) this.createMarker(d.outMarkerId, d.outPos, d.outMarkerHeightAdjust, d.outMarkerR, d.outMarkerCol); 45 | const outShape = mp.colshapes.newSphere(d.outPos.x, d.outPos.y, d.outPos.z, d.outShapeR); 46 | outShape.dimension = d.outPos.dim; 47 | this.setShapeData(outShape); 48 | const obj = { out: outShape } 49 | return obj; 50 | } 51 | 52 | setShapeData(shape) { 53 | shape.buildingId = this.id; 54 | shape.entranceId = entranceId; 55 | entranceId++; 56 | } 57 | 58 | 59 | } 60 | module.exports = building; 61 | 62 | 63 | function getBuilding(id) { 64 | for (let i = 0; i < buildingList.length; i++) { 65 | if (buildingList[i].id === id) { 66 | return buildingList[i]; 67 | } 68 | } 69 | } 70 | module.exports.getBuilding = getBuilding; 71 | 72 | mp.events.add({ 73 | "playerEnterColshape" : (player, shape) => { 74 | if (!player.loggedIn || !shape.buildingId) return; 75 | player.canEnter.building = { 76 | id: shape.buildingId, 77 | entranceId: shape.entranceId, 78 | }; 79 | const b = getBuilding(shape.buildingId); 80 | b.enteredBuildingShape(player, shape.entranceId); 81 | }, 82 | 83 | "sKeys-E" : (player) => { 84 | if (!player.loggedIn || !player.canEnter.building) return; 85 | const b = getBuilding(player.canEnter.building.id); 86 | b.tryToEnter(player, player.canEnter.building.entranceId); 87 | }, 88 | 89 | "playerExitColshape" : (player, shape) => { 90 | if (!player.loggedIn || !shape.buildingId) return; 91 | player.canEnter.building = false; 92 | }, 93 | }); -------------------------------------------------------------------------------- /app/client/Browsers/Login/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Get code 18 | 19 | Log in 20 | 21 | {{ errorMessage }} 22 | {{ infoMessage }} 23 | Create new account 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/client/Browsers/Character/second.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 |

{{ i18n.rot }}

10 |
11 | 12 |
13 | 14 |

{{ item }}

15 |
16 |
{{ i18n.next }}
17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/server/Basic/sMenu.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | const misc = require('../sMisc'); 3 | const vehicleAPI = require('./Vehicles/sVehicleSingletone'); 4 | const i18n = require('../sI18n'); 5 | const mailer = require('../sMailer'); 6 | 7 | 8 | class Menu { 9 | constructor() { 10 | mp.events.add({ 11 | "sKeys-M" : (player) => { 12 | if (!player.loggedIn) return; 13 | let execute = `app.d.cash = ${player.money.cash};`; 14 | execute += `app.pName = '${player.name}';`; 15 | execute += `app.d.loyality = ${player.loyality};`; 16 | if (player.vehicle) execute += `app.d.currentVehicleId = ${player.vehicle.id};`; 17 | execute += `app.loadVehicles('${vehicleAPI.getVehiclesForPlayerMenu(player.guid)}');`; 18 | execute += `app.loadPassengers('${vehicleAPI.getPassengersForPlayerMenu(player)}');`; 19 | execute += `app.loadViolations('${JSON.stringify(player.jail.violations)}');`; 20 | player.call("cMenu-Open", [player.lang, execute]); 21 | misc.log.debug(`${player.name} opens menu`); 22 | }, 23 | 24 | "sMenu-SetLang" : (player, id) => { 25 | const languages = ['eng', 'rus', 'ger', 'br', 'zhs', 'zht', 'cs',]; 26 | const lang = languages[id]; 27 | if (!lang) return; 28 | player.notify(`~g~${i18n.get('basic', 'success', player.lang)}!`); 29 | misc.query(`UPDATE users SET lang = '${lang}' WHERE id = '${player.guid}'`); 30 | player.lang = lang; 31 | }, 32 | 33 | "sMenu-ChangePass" : async (player, str) => { 34 | const d = JSON.parse(str); 35 | const db = await misc.query(`SELECT password FROM users WHERE id = '${player.guid}' LIMIT 1`); 36 | const oldPass = this.hashPassword(d.oldPass); 37 | if (oldPass !== db[0].password) return player.notify(`~r~${i18n.get('sMenu', 'wrongOldPass', player.lang)}!`); 38 | const newPass = this.hashPassword(d.newPass); 39 | await misc.query(`UPDATE users SET password = '${newPass}' WHERE id = '${player.guid}' LIMIT 1`); 40 | const mail = { 41 | from: `${mailer.getMailAdress()}`, 42 | to: `${player.email}`, 43 | subject: `Password has been changed`, 44 | text: `Hello! Your new password is: ${d.newPass}`, 45 | html: `Hello!
Your new password is: ${d.newPass}`, 46 | } 47 | mailer.sendMail(mail); 48 | player.notify(`~g~${i18n.get('basic', 'success', player.lang)}!`); 49 | }, 50 | 51 | "sMenu-RestoreVehicle" : async (player, id) => { 52 | const vehicle = mp.vehicles.at(id); 53 | if (!vehicle || vehicle.ownerId !== player.guid) return; 54 | vehicle.position = new mp.Vector3(417.153, -1627.647, 28.857); 55 | vehicle.rotation.z = 240; 56 | vehicle.repair(); 57 | vehicle.locked = true; 58 | vehicle.engine = false; 59 | player.newFine(vehicle.info.price / 5, `911 call for ${vehicle.title}`); 60 | }, 61 | 62 | }); 63 | } 64 | 65 | hashPassword(str) { 66 | const cipher = crypto.createCipher('aes192', 'a pass'); 67 | let encrypted = cipher.update(str, 'utf8', 'hex'); 68 | encrypted += cipher.final('hex'); 69 | return encrypted; 70 | } 71 | 72 | } 73 | new Menu(); 74 | -------------------------------------------------------------------------------- /app/server/Basic/sChat.js: -------------------------------------------------------------------------------- 1 | 2 | const i18n = require('../sI18n'); 3 | const misc = require('../sMisc'); 4 | const time = require('./sTime'); 5 | 6 | 7 | 8 | class ChatSingleton { 9 | constructor () { 10 | mp.events.add('playerChat', (player, message) => { 11 | if (!message) return player.notify("Please enter message"); 12 | this.sayRP(player, message); 13 | misc.log.debug(`${player.name}[${player.id}]: ${message}`); 14 | }); 15 | 16 | mp.events.addCommand({ 17 | 'me' : (player, fullText) => { 18 | if (!fullText) return player.notify("Please enter message"); 19 | this.sayME(player, fullText); 20 | }, 21 | 22 | 'do' : (player, fullText) => { 23 | if (!fullText) return player.notify("Please enter message"); 24 | this.sayDO(player, fullText); 25 | }, 26 | 27 | 'g' : (player, fullText) => { 28 | if (!fullText) return player.notify("Please enter message"); 29 | mp.players.broadcast(`[${time.getTime()}] [Global] ${player.name}: ${fullText}`); 30 | misc.log.debug(`${player.name} ${fullText}`); 31 | }, 32 | 33 | }); 34 | } 35 | 36 | getColorInRange(color, dist) { 37 | if (color === 'white') return this.getWhiteColor(dist); 38 | else if (color === 'purple') return this.getPurpleColor(dist); 39 | } 40 | 41 | getWhiteColor(dist) { 42 | if (dist >= 0 && dist < 2) return '#ffffff'; 43 | else if (dist >= 2 && dist < 4) return '#cecece'; 44 | else if (dist >= 4 && dist < 6) return '#afafaf'; 45 | else if (dist >= 6 && dist < 8) return '#919191'; 46 | else if (dist >= 8 && dist < 10) return '#727272'; 47 | } 48 | 49 | getPurpleColor(dist) { 50 | if (dist >= 0 && dist < 2) return '#c2a2da'; 51 | else if (dist >= 2 && dist < 4) return '#a58bba'; 52 | else if (dist >= 4 && dist < 6) return '#8a749b'; 53 | else if (dist >= 6 && dist < 8) return '#6e5d7c'; 54 | else if (dist >= 8 && dist < 10) return '#53465e'; 55 | } 56 | 57 | sayRP(player, text, anon = false) { 58 | mp.players.forEachInRange(player.position, 10, (client) => { 59 | const dist = client.dist(player.position); 60 | const color = this.getColorInRange("white", dist); 61 | const currentTime = misc.getTime(); 62 | if (anon) { 63 | client.outputChatBox(`!{${color}}[${currentTime}] ${i18n.get('sChat', 'someone', player.lang)}: ${text}`); 64 | } 65 | else { 66 | client.outputChatBox(`!{${color}}[${currentTime}] ${player.name}[${player.id}]: ${text}`); 67 | } 68 | }); 69 | } 70 | 71 | sayME(player, text, anon = false) { 72 | mp.players.forEachInRange(player.position, 10, (client) => { 73 | const dist = client.dist(player.position); 74 | const color = this.getColorInRange("purple", dist); 75 | const currentTime = misc.getTime(); 76 | if (anon) { 77 | client.outputChatBox(`!{${color}}[${currentTime}] ${i18n.get('sChat', 'someone', player.lang)}: ${text}`); 78 | } 79 | else { 80 | client.outputChatBox(`!{${color}}[${currentTime}] ${player.name} ${text}`); 81 | } 82 | misc.log.debug(`${player.name} ${text}.`); 83 | }); 84 | } 85 | 86 | sayDO(player, text, anon = false) { 87 | mp.players.forEachInRange(player.position, 10, (client) => { 88 | const dist = client.dist(player.position); 89 | const color = this.getColorInRange("purple", dist); 90 | const currentTime = misc.getTime(); 91 | if (anon) { 92 | client.outputChatBox(`!{${color}}[${currentTime}] ${text}`); 93 | } 94 | else { 95 | client.outputChatBox(`!{${color}}[${currentTime}] ${text} | ${player.name}`); 96 | } 97 | misc.log.debug(`${text} | ${player.name}.`); 98 | }); 99 | } 100 | } 101 | const chat = new ChatSingleton(); 102 | module.exports = chat; -------------------------------------------------------------------------------- /app/server/sMisc.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const log4js = require('log4js'); 4 | const mysql = require("./sMysql"); 5 | 6 | 7 | /* 8 | logger.trace('Entering cheese testing'); 9 | logger.debug('Got cheese.'); 10 | logger.info('Cheese is Gouda.'); 11 | logger.log('Something funny about cheese.'); 12 | logger.warn('Cheese is quite smelly.'); 13 | logger.error('Cheese %s is too ripe!', 'gouda'); 14 | logger.fatal('Cheese was breeding ground for listeria.'); 15 | */ 16 | 17 | 18 | class MiscSingleton { 19 | constructor() { 20 | log4js.configure({ 21 | appenders: { 22 | file: { type: 'file', filename: `serverLogs.log` }, 23 | console: { type: 'console' }, 24 | }, 25 | categories: { default: { appenders: ['file', 'console'], level: 'debug' } } 26 | }); 27 | this.log = log4js.getLogger(); 28 | this.log.fatal("Server Started"); 29 | } 30 | 31 | dbquery(query) { 32 | return new Promise( (r, j) => mysql.query(query, null , (err, data) => { 33 | if (err) { 34 | this.log.error(query); 35 | return j(err); 36 | } 37 | r(data); 38 | })) 39 | } 40 | 41 | async query(query) { 42 | const start = new Date().getTime(); 43 | const data = await this.dbquery(query); 44 | const time = new Date().getTime() - start; 45 | if (time >= 500) { 46 | this.log.warn(`'${query}' ends with: ${time / 1000}s`); 47 | } 48 | else { 49 | this.log.trace(`'${query}' ends with: ${time / 1000}s`); 50 | } 51 | return data; 52 | } 53 | 54 | roundNum(number, ends = 0) { 55 | return parseFloat(number.toFixed(ends)); 56 | } 57 | 58 | isValueNumber(value) { 59 | if (typeof value !== "number") return false; 60 | return true; 61 | } 62 | 63 | isValueString(value) { 64 | if (typeof value !== "string") return false; 65 | return true; 66 | } 67 | 68 | getRandomInt(min = 0, max = 100) { 69 | return Math.floor(Math.random() * (max - min + 1)) + min; 70 | } 71 | 72 | getPlayersInRange(position, range) { 73 | if (!this.isValueNumber(range)) return false; 74 | const players = mp.players.toArray(); 75 | const playersInRange = []; 76 | for (const player of players) { 77 | if (player.dist(position) < range) { 78 | playersInRange.push(player); 79 | } 80 | } 81 | return playersInRange; 82 | } 83 | 84 | getNearestPlayerInRange(position, range) { 85 | const playersInRange = this.getPlayersInRange(position, range); 86 | if (!playersInRange) return false; 87 | let nearestPlayer = 0; 88 | for (const player of playersInRange) { 89 | if (player.dist(position) < playersInRange[nearestPlayer].dist(position)) { 90 | nearestPlayer = playersInRange.indexOf(player); 91 | } 92 | } 93 | return playersInRange[nearestPlayer]; 94 | } 95 | 96 | getTime() { 97 | const currentTime = new Date(); 98 | let h = currentTime.getHours(); 99 | let m = currentTime.getMinutes(); 100 | let s = currentTime.getSeconds(); 101 | if (h < 10) h = `0${h}`; 102 | if (m < 10) m = `0${m}`; 103 | if (s < 10) s = `0${s}`; 104 | return `${h}:${m}:${s}`; 105 | } 106 | 107 | getPlayerByGuid(id) { 108 | const players = mp.players.toArray(); 109 | for (const player of players) { 110 | if (player.guid === id) return player; 111 | } 112 | return false; 113 | } 114 | 115 | getPlayerCoordJSON(player) { 116 | const obj = { 117 | x: player.position.x, 118 | y: player.position.y, 119 | z: player.position.z, 120 | rot: player.heading, 121 | dim: player.dimension, 122 | } 123 | if (player.vehicle) obj.rot = player.vehicle.rotation.z; 124 | return JSON.stringify(obj); 125 | } 126 | 127 | } 128 | const miscSingleton = new MiscSingleton(); 129 | module.exports = miscSingleton; -------------------------------------------------------------------------------- /app/server/Jobs/sJob.js: -------------------------------------------------------------------------------- 1 | const misc = require('../sMisc'); 2 | const clothes = require('../Character/sClothes'); 3 | const i18n = require('../sI18n'); 4 | 5 | 6 | 7 | const jobsList = []; 8 | 9 | function getJobByName(name) { 10 | for (const job of jobsList) { 11 | if (job.name === name) return job; 12 | } 13 | } 14 | module.exports.getJobByName = getJobByName; 15 | 16 | 17 | mp.events.add({ 18 | "playerEnterColshape" : (player, shape) => { 19 | if (!player.loggedIn || player.vehicle) return; 20 | if (!shape.job) return; 21 | player.canOpen.job = shape.job; 22 | const job = getJobByName(player.canOpen.job); 23 | job.enteredMainShape(player); 24 | }, 25 | 26 | "playerExitColshape" : (player, shape) => { 27 | if (shape.job) player.canOpen.job = false; 28 | }, 29 | 30 | "sKeys-E" : (player) => { 31 | if (!player.loggedIn || !player.canOpen.job) return; 32 | if (player.job.name !== i18n.get('sJob', 'unemployed', player.lang) && player.job.name !== player.canOpen.job) return player.notify(`~r~${i18n.get('basic', 'workingOnOtherJob', player.lang)}!`); 33 | const job = getJobByName(player.canOpen.job); 34 | job.pressedKeyOnMainShape(player); 35 | }, 36 | 37 | }); 38 | 39 | 40 | 41 | 42 | class Job { 43 | constructor(d) { 44 | this.name = d.name; 45 | this.mainMenu = { x: d.x, y: d.y, z: d.z, rot: d.rot, dim: d.dim }; 46 | 47 | this.createMainEntities(); 48 | this.setLocalSettings(); 49 | jobsList.push(this); 50 | } 51 | 52 | createMainEntities() { 53 | this.marker = mp.markers.new(1, new mp.Vector3(this.mainMenu.x, this.mainMenu.y, this.mainMenu.z - 1), 0.75, 54 | { 55 | color: [0, 255, 0, 100], 56 | visible: true, 57 | }); 58 | this.mainShape = mp.colshapes.newSphere(this.mainMenu.x, this.mainMenu.y, this.mainMenu.z, 1); 59 | this.mainShape.job = this.name; 60 | 61 | this.blip = mp.blips.new(514, new mp.Vector3(this.mainMenu.x, this.mainMenu.y, this.mainMenu.z), 62 | { 63 | name: this.name, 64 | shortRange: true, 65 | scale: 0.7, 66 | color: 17, 67 | }); 68 | } 69 | 70 | enteredMainShape(player) { 71 | player.notify(`${i18n.get('basic', 'pressEToOpenMenu', player.lang)}`); 72 | } 73 | 74 | startWork(player) { 75 | player.outputChatBox(`!{0, 200, 0}${i18n.get('sJob', 'start', player.lang)} ${this.name}`); 76 | if (player.model === 1885233650) this.setWorkingClothesForMan(player); 77 | else this.setWorkingClothesForWoman(player); 78 | misc.log.debug(`${player.name} started works as ${this.name}`); 79 | } 80 | 81 | setWorkingClothesForMan(player) { 82 | 83 | } 84 | 85 | setWorkingClothesForWoman(player) { 86 | 87 | } 88 | 89 | finishWork(player) { 90 | player.outputChatBox(`!{200, 0, 0}${i18n.get('sJob', 'finish', player.lang)} ${this.name}`); 91 | misc.log.debug(`${player.name} finished works as ${this.name}`); 92 | player.job = {name: i18n.get('sJob', 'unemployed', player.lang) }; 93 | clothes.loadPlayerClothes(player); 94 | } 95 | 96 | isPlayerWorksHere(player) { 97 | if (player.job.name && player.job.name === this.name) return true; 98 | return false; 99 | } 100 | 101 | isPlayerWorksOnOtherJob(player) { 102 | if (player.job.name !== i18n.get('sJob', 'unemployed', player.lang) && player.job.name !== player.canOpen.job) return true; 103 | return false; 104 | } 105 | 106 | } 107 | module.exports = Job; -------------------------------------------------------------------------------- /app/client/Browsers/Business/GasStation/gs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
X
8 |
9 | 10 |
{{ titleText }} №{{ id }}
11 |
{{ priceForLitre }}
12 |
{{ forLitreText }}
13 |
14 | 15 |
16 |
17 | {{ currentCar.title }} 18 | {{ currentCar.numberPlate }} 19 |
20 |
21 |
22 | L 23 |
24 | {{ forText }} 25 | {{ price }} 26 |
27 |
{{ fillUpText }}
28 |
29 | 30 |
31 |
32 | {{ car.title }} 33 | {{ car.numberPlate }} 34 |
35 |
36 |
37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/server/Business/sClothingShop.js: -------------------------------------------------------------------------------- 1 | 2 | const business = require('./sBusiness'); 3 | const misc = require('../sMisc'); 4 | const clothes = require('../Character/sClothes'); 5 | const i18n = require('../sI18n'); 6 | 7 | 8 | class ClothingShop extends business { 9 | constructor(d) { 10 | super(d); 11 | this.camData = JSON.parse(d.camData); 12 | this.buyerStandCoord = d.buyerStandCoord; 13 | } 14 | 15 | setLocalSettings() { 16 | this.buyerColshape.clothingShopId = this.id; 17 | this.blip.model = 73; 18 | this.blip.name = `Clothing shop`; 19 | } 20 | 21 | async buyCloth(player, d) { 22 | const price = clothes.getPrice(player, d.title, d.number); 23 | const shopTax = misc.roundNum(price * this.margin / 100); 24 | const endPrice = price + shopTax; 25 | const canBuy = await player.changeMoney(-endPrice); 26 | if (!canBuy) return; 27 | 28 | await this.addMoneyToBalance(shopTax); 29 | await clothes.saveClothes(player, d); 30 | player.notify(`~g~${i18n.get('basic', 'success', player.lang)}!`); 31 | misc.log.debug(`${player.name} bought a cloth for $${endPrice}`); 32 | } 33 | 34 | async updateCamData(player) { 35 | const pos = player.position; 36 | const obj = { 37 | x: misc.roundNum(pos.x, 2), 38 | y: misc.roundNum(pos.y, 2), 39 | z: misc.roundNum(pos.z, 2), 40 | rz: misc.roundNum(player.heading, 2), 41 | viewangle: 35, 42 | } 43 | const data = JSON.stringify(obj); 44 | await misc.query(`UPDATE clothingshop SET camData = '${data}' WHERE id = ${this.id}`); 45 | this.camData = obj; 46 | 47 | player.notify(`~g~${i18n.get('basic', 'success', player.lang)}!`); 48 | } 49 | 50 | 51 | openBuyerMenu(player) { 52 | if (player.vehicle) return; 53 | player.tp(JSON.parse(this.buyerStandCoord)); 54 | let execute; 55 | if (player.model === 1885233650) execute = `loadMans();`; 56 | else execute = `loadWomans();`; 57 | 58 | execute += `app.id = ${this.id};`; 59 | execute += `app.margin = ${this.margin};`; 60 | execute += `app.camRotation = ${this.camData.rz - 180};`; 61 | 62 | player.call("cClothingShop-ShowBuyerMenu", [player.lang, execute, this.camData]); 63 | misc.log.debug(`${player.name} enter a clothing shop menu`); 64 | } 65 | } 66 | 67 | 68 | async function loadClothingShops() { 69 | const d = await misc.query("SELECT * FROM business INNER JOIN clothingshop ON business.id = clothingshop.id"); 70 | for (let i = 0; i < d.length; i++) { 71 | new ClothingShop(d[i]); 72 | } 73 | } 74 | loadClothingShops(); 75 | 76 | 77 | 78 | mp.events.add({ 79 | "sClothingShop-BuyCloth" : (player, data) => { 80 | const d = JSON.parse(data); 81 | const shop = business.getBusiness(d.id); 82 | shop.buyCloth(player, d); 83 | }, 84 | 85 | "sClothingShop-ReloadClothes" : (player) => { 86 | clothes.loadPlayerClothes(player); 87 | }, 88 | }); 89 | 90 | mp.events.addCommand({ 91 | 'createclothingshop' : async (player, enteredprice) => { 92 | if (player.adminlvl < 1) return; 93 | const id = business.getCountOfBusinesses() + 1; 94 | const coord = misc.getPlayerCoordJSON(player); 95 | const price = Number(enteredprice.replace(/\D+/g,"")); 96 | const query1 = misc.query(`INSERT INTO business (id, title, coord, price) VALUES ('${id}', 'Clothing Shop', '${coord}', '${price}');`); 97 | const query2 = misc.query(`INSERT INTO clothingshop (id) VALUES ('${id}');`); 98 | await Promise.all([query1, query2]); 99 | player.outputChatBox("!{#4caf50} Clothing shop successfully created!"); 100 | }, 101 | 102 | 'setchbuyerstandcoord' : async (player, id) => { 103 | if (player.adminlvl < 1) return; 104 | const coord = misc.getPlayerCoordJSON(player); 105 | await misc.query(`UPDATE clothingshop SET buyerStandCoord = '${coord}' WHERE id = ${id}`); 106 | player.notify(`~g~${i18n.get('basic', 'success', player.lang)}!`); 107 | }, 108 | 109 | 'setchcamdata' : async (player, id) => { 110 | if (player.adminlvl < 1) return; 111 | const shop = business.getBusiness(+id); 112 | shop.updateCamData(player); 113 | }, 114 | 115 | }); 116 | -------------------------------------------------------------------------------- /app/server/Basic/Money/sMoney.js: -------------------------------------------------------------------------------- 1 | const misc = require('../../sMisc'); 2 | const i18n = require('../../sI18n'); 3 | 4 | 5 | 6 | class MoneySingletone { 7 | async createNewUser(id) { 8 | await misc.query(`INSERT INTO usersMoney (id, cash, fines) VALUES ('${id}', '1500', '[]');`); 9 | } 10 | 11 | async loadUser(player) { 12 | const d = await misc.query(`SELECT * FROM usersMoney WHERE id = '${player.guid}' LIMIT 1`); 13 | player.money = { 14 | cash: d[0].cash, 15 | bank: d[0].bank, 16 | tax: d[0].tax, 17 | fines: JSON.parse(d[0].fines), 18 | } 19 | 20 | player.updateCash = function() { 21 | this.call("cMoney-Update", [this.money.cash]); 22 | } 23 | player.updateCash(); 24 | 25 | player.changeMoney = async function(value) { 26 | if (!misc.isValueNumber(value)) return false; 27 | if (this.money.cash + value < 0) { 28 | this.notify(`~r~${i18n.get('sMoney', 'notEnoughCash', this.lang)}!`); 29 | return false; 30 | } 31 | await misc.query(`UPDATE usersMoney SET cash = cash + ${value} WHERE id = '${this.guid}'`); 32 | this.money.cash += value; 33 | this.updateCash(); 34 | return true; 35 | } 36 | 37 | player.addBankMoney = async function(value, comment) { 38 | if (!misc.isValueNumber(value) || value < 0) return; 39 | await misc.query(`UPDATE usersMoney SET bank = bank + ${value} WHERE id = '${this.guid}' LIMIT 1`); 40 | this.money.bank += value; 41 | this.call("cMoney-SendNotification", [`${i18n.get('sMoney', 'addBankMoney', this.lang)}: ~g~$${value}. ~w~${comment}`]); 42 | } 43 | 44 | player.payTax = async function(value, comment) { 45 | if (!misc.isValueNumber(value) || value < 0) return; 46 | if (value > this.money.tax) return false; 47 | await misc.query(`UPDATE usersMoney SET tax = tax - ${value} WHERE id = '${this.guid}'`); 48 | this.money.tax -= value; 49 | this.call("cMoney-SendNotification", [`${i18n.get('sMoney', 'payTaxOffline', this.lang)}: ~g~$${value}. ~w~${comment}`]); 50 | return true; 51 | } 52 | 53 | player.newFine = function(value, comment) { 54 | if (!misc.isValueNumber(value) || value < 0) return; 55 | const newFine = { 56 | date: new Date().toLocaleString(), 57 | val: value, 58 | txt: comment, 59 | } 60 | misc.query(`UPDATE usersMoney SET fines = '${JSON.stringify(this.money.fines)}' WHERE id = '${this.guid}'`); 61 | this.money.fines.push(newFine); 62 | this.call("cMoney-SendNotification", [`${i18n.get('sMoney', 'newFine', this.lang)}: ~r~$${value}. ~w~${comment}`]); 63 | misc.log.debug(`New fine: ${player.name}, $${value}, ${comment}`); 64 | } 65 | } 66 | 67 | async addBankMoneyOffline(guid, value) { 68 | await misc.query(`UPDATE usersMoney SET bank = bank + ${value} WHERE id = '${guid}' LIMIT 1`); 69 | } 70 | 71 | async payTaxOffline(guid, value) { 72 | const d = await misc.query(`SELECT tax FROM usersMoney WHERE id = '${guid}' LIMIT 1`); 73 | if (!d[0] || value > d[0].tax) return false; 74 | await misc.query(`UPDATE usersMoney SET tax = tax - ${value} WHERE id = '${guid}' LIMIT 1`); 75 | return true; 76 | } 77 | 78 | getNearestATM(playerPosition) { 79 | const atms = mp.blips.toArray(); 80 | let nearestATM = atms[0]; 81 | for (const atm of atms) { 82 | if (atm.name !== "ATM") continue; 83 | if (atm.dist(playerPosition) < nearestATM.dist(playerPosition)) { 84 | nearestATM = atm; 85 | } 86 | } 87 | return nearestATM.position; 88 | } 89 | 90 | } 91 | const moneySingletone = new MoneySingletone(); 92 | module.exports = moneySingletone; 93 | 94 | 95 | mp.events.addCommand({ 96 | 'givecash' : (admin, fullText, id, value) => { 97 | if (admin.adminlvl < 1) return; 98 | const player = mp.players.at(+id); 99 | if (!player) return admin.outputChatBox(`!{200, 0, 0}Player does not exist!`); 100 | player.changeMoney(+value); 101 | admin.outputChatBox(`!{0, 200, 0}You gave $${+value} to ${player.name}!`); 102 | player.outputChatBox(`!{0, 200, 0}${admin.name} gave you $${+value}!`); 103 | misc.log.info(`${admin.name} give ${player.name} $${+value}`); 104 | }, 105 | 106 | }); -------------------------------------------------------------------------------- /app/client/Browsers/ATM/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | background-color: rgba(0, 0, 0, 0.5); 4 | font-family: Segoe UI; 5 | font-weight: 100; 6 | color: #DDD; 7 | text-align: center; 8 | } 9 | 10 | .b3-close { 11 | background: coral; 12 | position: absolute; 13 | right: 0; 14 | top: 0; 15 | width: 6vh; 16 | height: 3vh; 17 | font-size: 2vh; 18 | transition: 0.3s; 19 | cursor: pointer; 20 | z-index: 2; 21 | } 22 | .b3-close:hover { 23 | background: tomato; 24 | } 25 | 26 | .b1-logo { 27 | background: #268F3A; 28 | width: 100%; 29 | position: absolute; 30 | left: 0; 31 | top: 0; 32 | height: 40vh; 33 | z-index: 1; 34 | display: flex; 35 | flex-wrap: wrap; 36 | position: fixed; 37 | transition: 1s; 38 | } 39 | .logoLoading { 40 | height: 100vh; 41 | } 42 | .logo { 43 | margin: auto; 44 | } 45 | 46 | .b1-money { 47 | width: 100%; 48 | background: #DDD; 49 | position: fixed; 50 | left: 0; 51 | top: 40vh; 52 | display: flex; 53 | padding: 2vh 0; 54 | } 55 | .b1-money > div { 56 | width: 100%; 57 | text-align: center; 58 | border-left: 1px solid #777; 59 | } 60 | .b1-money > div:nth-child(1) { 61 | border-left: 1px solid transparent; 62 | } 63 | .b1-summ { 64 | font-size: 3vh; 65 | color: green; 66 | } 67 | .b1-summ:before { 68 | content: "$"; 69 | } 70 | .b1-summ-info { 71 | font-size: 1.5vh; 72 | color: #777; 73 | text-transform: uppercase; 74 | } 75 | 76 | .b2-buttons { 77 | display: flex; 78 | flex-wrap: wrap; 79 | align-items: center; 80 | align-content: center; 81 | width: 100%; 82 | position: absolute; 83 | left: 0; 84 | height: 50vh; 85 | bottom: 0vh; 86 | } 87 | .b2-item { 88 | width: 100%; 89 | background: #268F3A; 90 | text-align: center; 91 | margin: 1vh 20vw; 92 | color: #EEE; 93 | font-size: 2vh; 94 | padding: 2vh 0; 95 | text-transform: uppercase; 96 | transition: 0.3s; 97 | cursor: pointer; 98 | } 99 | .b2-item:hover { 100 | background: green; 101 | color: #ddd; 102 | } 103 | 104 | .b3 { 105 | background-color: rgba(0, 0, 0, 0.8); 106 | width: 75vw; 107 | height: 40vh; 108 | position: absolute; 109 | left: 50vw; 110 | bottom: 5vh; 111 | transform: translateX(-50%); 112 | transition: 0.3s; 113 | } 114 | .b3-vars { 115 | display: flex; 116 | flex-wrap: wrap; 117 | justify-content: space-between; 118 | margin: 3vh; 119 | margin-top: 7vh; 120 | } 121 | .b3-item { 122 | width: 30%; 123 | margin: 0; 124 | margin-bottom: 5vh; 125 | } 126 | .b3-item:before { 127 | content: "$"; 128 | } 129 | .b3-item-input { 130 | width: 65%; 131 | margin: 0; 132 | cursor: default; 133 | position: relative; 134 | text-align: left; 135 | font-size: 3vh; 136 | background: #ccc; 137 | } 138 | .b3-item-input:hover { 139 | background: #ccc; 140 | } 141 | .b3-item-done { 142 | width: 30%; 143 | margin: 0; 144 | background: #ccc; 145 | color: green; 146 | } 147 | .submit-cash { 148 | width: 100%; 149 | height: 95%; 150 | position: absolute; 151 | left: 0%; 152 | top: 0; 153 | outline: none; 154 | background: url('transparent.png'); 155 | border: 0px; 156 | font-family: Segoe UI; 157 | font-weight: 100; 158 | font-size: 3vh; 159 | line-height: 3vh; 160 | color: green; 161 | text-align: center; 162 | } 163 | 164 | .b3-fine { 165 | height: 37vh; 166 | padding-top: 3vh; 167 | background: #DDD; 168 | } 169 | .fine { 170 | display: flex; 171 | flex-wrap: wrap; 172 | align-items: flex-start; 173 | align-content: flex-start; 174 | overflow: auto; 175 | height: 37vh; 176 | } 177 | .fine-item { 178 | display: flex; 179 | align-items: center; 180 | width: 100%; 181 | border-top: 1px solid #777; 182 | padding: 2vh 0; 183 | font-size: 2.5vh; 184 | color: #777; 185 | position: relative; 186 | transition: 0.3s; 187 | } 188 | .fine-item > span { 189 | width: 25%; 190 | } 191 | .fine-item > div { 192 | width: 50%; 193 | } 194 | .fine-item_price { 195 | color: red; 196 | } 197 | .fine-item:hover { 198 | background: #ccc; 199 | } 200 | 201 | ::-webkit-scrollbar { 202 | width: 0; 203 | } -------------------------------------------------------------------------------- /app/client/Browsers/Character/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100%; 3 | font-family: Segoe UI; 4 | font-weight: 100; 5 | font-size: 14pt; 6 | color: #555; 7 | text-align: center; 8 | } 9 | 10 | .b1 { 11 | width: 20vw; 12 | height: 100vh; 13 | background: #DDD; 14 | position: fixed; 15 | top: 0; 16 | right: 0; 17 | } 18 | 19 | .b1-title { 20 | width: 100%; 21 | background: dodgerblue; 22 | color: white; 23 | padding: 3vh 0; 24 | font-size: 2vh; 25 | } 26 | 27 | .fm-b { 28 | display: flex; 29 | font-size: 2.5vh; 30 | } 31 | 32 | .fm-b_arrow { 33 | width: 8vh; 34 | padding-top: 1.0vh; 35 | color: dodgerblue; 36 | transition: 0.3s; 37 | } 38 | 39 | .fm-b_arrow:hover { 40 | background: dodgerblue; 41 | color: white; 42 | } 43 | 44 | .fm-b_main { 45 | width: 100%; 46 | line-height: 1vh; 47 | padding-top: 1.5vh; 48 | color: dodgerblue; 49 | } 50 | 51 | .fm-b_main p { 52 | font-size: 1.2vh; 53 | color: #777; 54 | } 55 | 56 | .fm-input-b { 57 | height: 20.5vh; 58 | position: relative; 59 | background: #ddd; 60 | } 61 | 62 | .fm-input-b input:focus { 63 | outline: none; 64 | } 65 | 66 | .fm-input-b input:before { 67 | content: ""; 68 | background: white; 69 | width: 20vh; 70 | height: 1px; 71 | position: absolute; 72 | top: 50%; 73 | left: 0; 74 | } 75 | 76 | .fm-input-b input[type=range] { 77 | -webkit-appearance: none; 78 | width: 20vh; 79 | transform: rotate(90deg); 80 | position: relative; 81 | top: 2.5vh; 82 | } 83 | 84 | .fm-input-b input[type=range]::-webkit-slider-runnable-track { 85 | height: 15vh; 86 | background: #ddd; 87 | } 88 | 89 | .fm-input-b input[type=range]::-webkit-slider-thumb { 90 | height: 5vh; 91 | width: 5vh; 92 | background: white; 93 | -webkit-appearance: none; 94 | margin-top: 5vh; 95 | border-radius: 50%; 96 | } 97 | 98 | .fm-b_main input:focus { 99 | outline: none; 100 | } 101 | 102 | .fm-b_main input:before { 103 | content: ""; 104 | background: white; 105 | width: 20vh; 106 | height: 1px; 107 | position: absolute; 108 | top: 50%; 109 | left: 0; 110 | } 111 | 112 | .fm-b_main input[type=range] { 113 | -webkit-appearance: none; 114 | width: 20vh; 115 | position: relative; 116 | } 117 | 118 | .fm-b_main input[type=range]::-webkit-slider-runnable-track { 119 | height: 5vh; 120 | background: #ddd; 121 | } 122 | 123 | .fm-b_main input[type=range]::-webkit-slider-thumb { 124 | height: 5vh; 125 | width: 5vh; 126 | background: white; 127 | -webkit-appearance: none; 128 | border-radius: 50%; 129 | } 130 | 131 | .b1-done { 132 | background: forestgreen; 133 | padding: 1vh 0; 134 | position: absolute; 135 | left: 0; 136 | bottom: 0; 137 | transition: 0.3s; 138 | } 139 | 140 | .b1-done:hover { 141 | background: dodgerblue; 142 | } 143 | 144 | .b1-done { 145 | background: forestgreen; 146 | padding: 1vh 0; 147 | position: absolute; 148 | left: 0; 149 | bottom: 0; 150 | transition: 0.3s; 151 | } 152 | 153 | .b1-done:hover { 154 | background: dodgerblue; 155 | } 156 | 157 | 158 | 159 | 160 | .b1-face-item { 161 | height: 4vh; 162 | position: relative; 163 | margin-bottom: 0.5vh; 164 | } 165 | 166 | .b1-face-item > input:focus { 167 | outline: none; 168 | } 169 | 170 | .b1-face-item > input:before { 171 | content: ""; 172 | background: white; 173 | width: 18vw; 174 | height: 1px; 175 | position: absolute; 176 | top: 50%; 177 | left: 0; 178 | } 179 | 180 | .b1-face-item > input[type=range] { 181 | -webkit-appearance: none; 182 | width: 18vw; 183 | position: relative; 184 | } 185 | 186 | .b1-face-item > input[type=range]::-webkit-slider-runnable-track { 187 | height: 2vh; 188 | background: #ddd; 189 | } 190 | 191 | .b1-face-item > input[type=range]::-webkit-slider-thumb { 192 | height: 2vh; 193 | width: 5vh; 194 | background: white; 195 | -webkit-appearance: none; 196 | background: url(range.png) center; 197 | background-size: cover; 198 | } 199 | 200 | .b1-face-item > p { 201 | display: block; 202 | font-size: 1.3vh; 203 | color: #777; 204 | position: absolute; 205 | bottom: -1.0vh; 206 | left: 0; 207 | text-align: center; 208 | width: 100%; 209 | } 210 | -------------------------------------------------------------------------------- /app/server/Factions/Police/Prison/sPrison.js: -------------------------------------------------------------------------------- 1 | const misc = require('../../../sMisc'); 2 | const clothes = require('../../../Character/sClothes'); 3 | const building = require('./sPrisonBuilding'); 4 | 5 | 6 | class Prison { 7 | constructor() { 8 | mp.events.add({ 9 | "playerDeath" : (player, reason, killer) => { 10 | if (!killer || player === killer) return; 11 | killer.addViolation(5, "You killed a civilian"); 12 | }, 13 | 14 | }); 15 | } 16 | 17 | async createNewUser(id) { 18 | await misc.query(`INSERT INTO usersJail (id, violations) VALUES ('${id}', '${JSON.stringify([])}')`); 19 | } 20 | 21 | async loadUser(player) { 22 | const d = await misc.query(`SELECT * FROM usersJail WHERE id = '${player.guid}' LIMIT 1`); 23 | player.jail = { 24 | inside: d[0].inside, 25 | time: d[0].time, 26 | violations: JSON.parse(d[0].violations), 27 | } 28 | 29 | player.isWanted = function() { 30 | if (this.jail.violations.length > 0) return true; 31 | return false; 32 | } 33 | 34 | player.updateWantedLevel = function() { 35 | if (!this.isWanted()) this.call("cPrison-SetWantedLevel", [0]); 36 | else this.call("cPrison-SetWantedLevel", [5]); 37 | } 38 | player.updateWantedLevel(); 39 | 40 | player.addViolation = function(time, comment) { 41 | if (!this.loggedIn) return; 42 | const newViolation = { time, comment }; 43 | this.jail.violations.push(newViolation); 44 | this.call("cPrison-SendNotification", [`${comment} | ${time} min`]); 45 | this.updateWantedLevel(); 46 | misc.log.debug(`${this.name} get new violation: ${comment} | ${time}`); 47 | } 48 | 49 | player.startJail = function() { 50 | if (this.jail.violations.length === 0) return this.notify("You have no violations!"); 51 | player.tp(building.secondEntranceData.inPos); 52 | let jailTime = 0; 53 | for (const violation of this.jail.violations) { 54 | this.outputChatBox(`${violation.text} | ${violation.time} minutes`); 55 | jailTime += violation.time; 56 | } 57 | this.outputChatBox(`Total time: !{225, 0, 0}${jailTime} minutes`); 58 | this.jail.inside = 1; 59 | this.jail.time = jailTime; 60 | this.jail.violations = []; 61 | if (this.model === 1885233650) prison.setManClothes(player); 62 | else prison.setWomanClothes(player); 63 | misc.log.debug(`${this.name} start jail. Time ${jailTime} m`); 64 | } 65 | 66 | player.jailEvent = function() { 67 | if (!this.jail.inside) return; 68 | const dist = this.dist(new mp.Vector3(1689, 2529, 45.5)); 69 | if (dist > 280) return prison.escapeEvent(player); 70 | this.jail.time -= 1; 71 | if (this.jail.time === 0) return this.endJail(); 72 | this.notify(`Remaining time: ~r~${this.jail.time} minutes`); 73 | misc.log.debug(`${this.name} jail remaining time: ${this.jail.time} m`); 74 | } 75 | 76 | player.endJail = function() { 77 | this.position = new mp.Vector3(1792.563, 2593.779, 45.796); 78 | this.heading = 263.45; 79 | this.jail.inside = 0; 80 | this.outputChatBox(`!{0, 225, 0}Now you are free!`); 81 | this.updateWantedLevel(); 82 | clothes.loadPlayerClothes(this); 83 | misc.log.debug(`${this.name} ended jail`); 84 | } 85 | 86 | } 87 | 88 | escapeEvent(player) { 89 | player.addViolation(player.jail.time * 3, 'Escape'); 90 | player.jail.inside = 0; 91 | player.jail.time = 0; 92 | } 93 | 94 | setManClothes(player) { 95 | player.setClothes(11, 5, 0, 0); // tops 96 | player.setClothes(3, 5, 0, 0); 97 | player.setClothes(8, 5, 0, 0); 98 | 99 | player.setClothes(4, 3, 7, 0); // legs 100 | 101 | player.setClothes(6, 5, 0, 0); // shoes 102 | } 103 | 104 | setWomanClothes(player) { 105 | player.setClothes(11, 5, 0, 0); // tops 106 | player.setClothes(3, 4, 0, 0); 107 | player.setClothes(8, 5, 0, 0); 108 | 109 | player.setClothes(4, 3, 15, 0); // legs 110 | 111 | player.setClothes(6, 5, 0, 0); // shoes 112 | } 113 | 114 | savePlayerAccount(player) { 115 | misc.query(`UPDATE usersJail SET inside = '${player.jail.inside}', time = '${player.jail.time}', violations = '${JSON.stringify(player.jail.violations)}' WHERE id = '${player.guid}'`); 116 | } 117 | 118 | } 119 | const prison = new Prison(); 120 | module.exports = prison; 121 | 122 | -------------------------------------------------------------------------------- /app/server/Basic/Vehicles/sVehicle.js: -------------------------------------------------------------------------------- 1 | 2 | const misc = require('../../sMisc'); 3 | const i18n = require('../../sI18n'); 4 | 5 | 6 | class Vehicle { 7 | constructor (d) { 8 | const pos = JSON.parse(d.coord); 9 | const vehicle = mp.vehicles.new(d.model, new mp.Vector3(pos.x, pos.y, pos.z), 10 | { 11 | heading: pos.rot, 12 | dimension: pos.dim, 13 | locked: true, 14 | engine: false, 15 | }); 16 | vehicle.guid = d.id; 17 | vehicle.title = d.title; 18 | vehicle.fuel = d.fuel; 19 | vehicle.fuelTank = d.fuelTank; 20 | vehicle.fuelRate = d.fuelRate; 21 | vehicle.price = d.price; 22 | vehicle.ownerId = d.ownerId; 23 | vehicle.whoCanOpen = JSON.parse(d.whoCanOpen); 24 | vehicle.factionName = d.factionName; 25 | vehicle.windowsOpened = [false, false, false, false]; 26 | vehicle.numberPlate = d.numberPlate; 27 | 28 | const primaryColor = JSON.parse(d.primaryColor); 29 | const secondaryColor = JSON.parse(d.secondaryColor); 30 | vehicle.setColorRGB(primaryColor[0], primaryColor[1], primaryColor[2], secondaryColor[0], secondaryColor[1], secondaryColor[2]); 31 | 32 | vehicle.canOpen = function(player) { 33 | if (player.dimension !== this.dimension) return false; 34 | if (player.faction.name && player.faction.name === this.factionName) return true; 35 | for (const p of this.whoCanOpen) { 36 | if (p !== player.guid) continue; 37 | return true; 38 | } 39 | return false; 40 | } 41 | 42 | vehicle.toggleDoorsLock = function(player) { 43 | if (vehicle.locked) { 44 | this.unlock(); 45 | // player.outputChatBox(`${this.title} !{0, 200, 0}${i18n.get('sVehicle', 'unlocked', player.lang)}`); 46 | player.notifyWithPicture("Info", "", `${this.title} ~g~${i18n.get('sVehicle', 'unlocked', player.lang)}.`, "CHAR_PROPERTY_ARMS_TRAFFICKING"); 47 | } 48 | else { 49 | this.lock(); 50 | // player.outputChatBox(`${this.title} !{200, 0, 0}${i18n.get('sVehicle', 'locked', player.lang)}`); 51 | player.notifyWithPicture("Info", "", `${this.title} ~r~${i18n.get('sVehicle', 'locked', player.lang)}`, "CHAR_PROPERTY_ARMS_TRAFFICKING"); 52 | } 53 | vehicle.locked = !vehicle.locked; 54 | } 55 | 56 | vehicle.lock = function() { 57 | if (this.getOccupants().length === 0) this.blinkLights(); 58 | } 59 | 60 | vehicle.unlock = function() { 61 | if (this.getOccupants().length === 0) { 62 | this.blinkLights(); 63 | setTimeout(() => { 64 | this.blinkLights(); 65 | }, 600); 66 | } 67 | } 68 | 69 | vehicle.blinkLights = function() { 70 | const engineStatus = this.engine; 71 | if (!engineStatus) this.engine = true; 72 | 73 | const players = mp.players.toArray(); 74 | for (const player of players) { 75 | player.call("cVehicle-setLights", [this, 2]); 76 | setTimeout(() => { 77 | player.call("cVehicle-setLights", [this, 0]); 78 | }, 300); 79 | } 80 | 81 | if (!engineStatus) { 82 | setTimeout(() => { 83 | this.engine = engineStatus; 84 | }, 300); 85 | } 86 | } 87 | 88 | vehicle.canRollWindow = function(player, window) { 89 | if (player.isDriver() || player.seat + 1 === window) return true; 90 | return false; 91 | } 92 | 93 | vehicle.toggleWindow = function(player, window) { 94 | if (!this.canRollWindow(player, window)) return; 95 | const windowOpened = this.windowsOpened[window]; 96 | let action; 97 | if (windowOpened) action = "cVehicle-rollUpWindow"; 98 | else action = "cVehicle-rollDownWindow"; 99 | mp.players.forEach((p, id) => { 100 | p.call(action, [this, window]); 101 | }); 102 | vehicle.windowsOpened[window] = !windowOpened; 103 | } 104 | 105 | vehicle.fillUp = async function(litres) { 106 | vehicle.fuel += litres; 107 | if (vehicle.fuel > vehicle.fuelTank) vehicle.fuel = vehicle.fuelTank; 108 | } 109 | 110 | vehicle.sellToGovernment = async function(player) { 111 | if (vehicle.ownerId !== player.guid) return; 112 | player.addBankMoney(this.price / 2, `${i18n.get('sVehicle', 'sellVehicle', player.lang)}`); 113 | await misc.query(`DELETE FROM vehicles WHERE id = ${vehicle.guid} AND ownerId = '${player.guid}' LIMIT 1`); 114 | this.destroy(); 115 | } 116 | 117 | return vehicle; 118 | } 119 | 120 | } 121 | module.exports = Vehicle; 122 | -------------------------------------------------------------------------------- /app/server/Business/sBarberShop.js: -------------------------------------------------------------------------------- 1 | const business = require('./sBusiness'); 2 | const misc = require('../sMisc'); 3 | const headOverlaySingletone = require('../Character/sHeadOverlay'); 4 | const i18n = require('../sI18n'); 5 | 6 | 7 | class ClothingShop extends business { 8 | constructor(d) { 9 | super(d); 10 | this.camData = JSON.parse(d.camData); 11 | } 12 | 13 | setLocalSettings() { 14 | this.blip.model = 71; 15 | this.blip.name = `Barber shop`; 16 | } 17 | 18 | openBuyerMenu(player) { 19 | if (player.vehicle) return; 20 | const d = this.buyerMenuCoord; 21 | d.dim = 0; 22 | player.tp(d); 23 | 24 | let execute 25 | if (player.model === 1885233650) execute = `app.loadMans();`; 26 | else execute = `app.loadWomans();`; 27 | 28 | execute += `app.id = ${this.id};`; 29 | execute += `app.margin = ${this.margin};`; 30 | execute += `app.camRotation = ${player.heading};`; 31 | 32 | player.call("cBarberShop-ShowBuyerMenu", [player.lang, execute, this.camData]); 33 | misc.log.debug(`${player.name} enter a barber shop menu`); 34 | } 35 | 36 | async updateCamData(player) { 37 | const pos = player.position; 38 | const obj = { 39 | x: misc.roundNum(pos.x, 2), 40 | y: misc.roundNum(pos.y, 2), 41 | z: misc.roundNum(pos.z + 0.70, 2), 42 | rz: misc.roundNum(player.heading, 2), 43 | viewangle: 20, 44 | } 45 | const data = JSON.stringify(obj); 46 | await misc.query(`UPDATE barbershop SET camData = '${data}' WHERE id = ${this.id}`); 47 | this.camData = obj; 48 | 49 | player.notify(`~g~${i18n.get('basic', 'success', player.lang)}!`); 50 | } 51 | 52 | async buyThing(player, d) { 53 | const price = this.getPrice(d); 54 | const shopTax = misc.roundNum(price * this.margin / 100); 55 | const endPrice = price + shopTax; 56 | const canBuy = await player.changeMoney(-endPrice); 57 | if (!canBuy) return; 58 | await this.addMoneyToBalance(shopTax); 59 | await headOverlaySingletone.saveHeadOverlay(player, d); 60 | player.notify(`~g~${i18n.get('basic', 'success', player.lang)}!`); 61 | misc.log.debug(`${player.name} bought something in barbershop for $${endPrice}`); 62 | } 63 | 64 | getPrice(d) { 65 | let price; 66 | if (misc.isValueNumber(d.hairStyle)) price = 2500; 67 | else if (misc.isValueNumber(d.hairCol1) && misc.isValueNumber(d.hairCol2)) price = 1500; 68 | else if (misc.isValueNumber(d.browStyle) && misc.isValueNumber(d.browOp)) price = 1000; 69 | else if (misc.isValueNumber(d.beardStyle) && misc.isValueNumber(d.beardOp)) price = 500; 70 | return price; 71 | } 72 | 73 | } 74 | 75 | async function loadBarberShops() { 76 | const d = await misc.query("SELECT * FROM business INNER JOIN barbershop ON business.id = barbershop.id"); 77 | for (let i = 0; i < d.length; i++) { 78 | new ClothingShop(d[i]); 79 | } 80 | } 81 | loadBarberShops(); 82 | 83 | 84 | 85 | mp.events.add({ 86 | "sBarberShop-SetHairStyle" : (player, index) => { 87 | player.setClothes(2, index, 0, 0); 88 | }, 89 | 90 | "sBarberShop-SetHeadOverlay" : (player, obj) => { 91 | const d = JSON.parse(obj); 92 | player.setHeadOverlay(d.id, [d.index, d.opacity, 1, 1]); 93 | }, 94 | 95 | "sBarberShop-BuyThing" : async (player, data) => { 96 | const d = JSON.parse(data); 97 | const shop = business.getBusiness(d.id); 98 | await shop.buyThing(player, d); 99 | player.updateHeadOverlay(); 100 | }, 101 | 102 | "sBarberShop-ReloadHeadOverlay" : (player) => { 103 | player.updateHeadOverlay(); 104 | }, 105 | }); 106 | 107 | mp.events.addCommand({ 108 | 'createbarbershop' : async (player, enteredprice) => { 109 | if (player.adminlvl < 1) return; 110 | const id = business.getCountOfBusinesses() + 1; 111 | const coord = misc.getPlayerCoordJSON(player); 112 | const price = Number(enteredprice.replace(/\D+/g,"")); 113 | const query1 = misc.query(`INSERT INTO business (id, title, coord, price) VALUES ('${id}', 'Barber Shop', '${coord}', '${price}');`); 114 | const query2 = misc.query(`INSERT INTO barbershop (id) VALUES ('${id}');`); 115 | await Promise.all([query1, query2]); 116 | player.outputChatBox("!{#4caf50} Barber shop successfully created!"); 117 | }, 118 | 119 | 'setbscamdata' : async (player, id) => { 120 | if (player.adminlvl < 1) return; 121 | const shop = business.getBusiness(+id); 122 | shop.updateCamData(player); 123 | }, 124 | 125 | }); -------------------------------------------------------------------------------- /app/client/Browsers/Jobs/MariaCollector/mariacollector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
X
8 | 9 |
10 | {{ messageText1 }} 11 |

12 | {{ messageText2 }} 13 |

14 | {{ messageText3 }} 15 |

16 | {{ messageText4 }} 17 |
18 | 19 |
20 |
{{ startText }}
21 |
{{ finishText }}
22 |
23 |
24 | 25 | 26 | 27 | 28 | 106 | -------------------------------------------------------------------------------- /app/client/Browsers/Jobs/OrangeCollector/collector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
X
8 | 9 |
10 | {{ messageText1 }} 11 |

12 | {{ messageText2 }} 13 |

14 | {{ messageText3 }} 15 |

16 | {{ messageText4 }} 17 |
18 | 19 |
20 |
{{ startText }}
21 |
{{ finishText }}
22 |
23 |
24 | 25 | 26 | 27 | 28 | 106 | -------------------------------------------------------------------------------- /app/server/Basic/Auth/sLogin.js: -------------------------------------------------------------------------------- 1 | 2 | const misc = require('../../sMisc'); 3 | const mailer = require('../../sMailer'); 4 | const i18n = require('../../sI18n'); 5 | const playerSingleton = require('../sPlayer'); 6 | const AbstractAuth = require('./sAuthAbstract'); 7 | 8 | 9 | 10 | class LoginSingleton extends AbstractAuth { 11 | async tryLoginWithoutCode(player, obj) { 12 | const data = JSON.parse(obj); 13 | const pass = this.hashPassword(data.pass); 14 | const d = await misc.query(`SELECT id, email, password, socialclub FROM users WHERE email = '${data.email}' LIMIT 1`); 15 | if (!d[0]) { 16 | return this.showError(player, "This account does NOT exist"); 17 | } 18 | if (d[0].socialclub !== player.socialClub) { 19 | // return player.call("cInjectCef", [`app.showCode = true;`]); 20 | } 21 | if (d[0].password !== pass) { 22 | return this.showError(player, `Wrong password!`); 23 | } 24 | else if (this.isAlreadyPlaying(d[0].email)) { 25 | this.showError(player, `You cant log in from two devices!`); 26 | player.loggedIn = false; 27 | return player.kick('Dublicate'); 28 | } 29 | this.loadAccount(player, d[0].id); 30 | } 31 | 32 | isAlreadyPlaying(email) { 33 | const players = mp.players.toArray(); 34 | for (const player of players) { 35 | if (!player.loggedIn) continue; 36 | if (player.email === email) return true; 37 | } 38 | return false; 39 | } 40 | 41 | async loadAccount(player, id) { 42 | delete player.verificationCode; 43 | delete player.verificationDate; 44 | delete player.verificationCodeTries; 45 | 46 | await playerSingleton.loadAccount(player, id); 47 | 48 | player.outputChatBox(`${i18n.get('sLogin', 'annouceSpawnVehicle', player.lang)}`); 49 | player.outputChatBox(`${i18n.get('sLogin', 'annouceGlobalChat', player.lang)}`); 50 | player.outputChatBox(`${i18n.get('sLogin', 'annouceOldUser', player.lang)}`); 51 | player.outputChatBox(`${i18n.get('sLogin', 'annoucePlayerMenu', player.lang)}`); 52 | const onlinePlayers = mp.players.toArray(); 53 | if (onlinePlayers.length < 30) { 54 | for (const p of onlinePlayers) { 55 | p.outputChatBox(`[${misc.getTime()}] ${player.name} ${i18n.get('sLogin', 'connected', p.lang)}`); 56 | } 57 | } 58 | } 59 | 60 | tryGetCodeToLogin(player, email) { 61 | if (!mailer.isEmailValid(email)) { 62 | return this.showError(player, "Email Invalid"); 63 | } 64 | this.trySendCode(player, email); 65 | } 66 | 67 | async tryValidateCodeAndLogIn(player, obj) { 68 | const data = JSON.parse(obj); 69 | const pass = this.hashPassword(data.pass); 70 | if (!this.checkCode(player, data.code)) return; 71 | const d = await misc.query(`SELECT id, email, password FROM users WHERE email = '${data.email}' LIMIT 1`); 72 | if (!d[0]) { 73 | return this.showError(player, "This account does NOT exist"); 74 | } 75 | if (d[0].password !== pass) { 76 | player.call("cInjectCef", [`app.showCode = false; app.enteredCode = "";`]); 77 | return this.showError(player, `Wrong password!`); 78 | } 79 | if (this.isAlreadyPlaying(d[0].email)) { 80 | this.showError(player, `You cant log in from two devices!`); 81 | return player.kick('Dublicate'); 82 | } 83 | this.loadAccount(player, d[0].id); 84 | } 85 | 86 | } 87 | const loginSingleton = new LoginSingleton(); 88 | 89 | mp.events.add({ 90 | "sLogin-TryLoginWithoutCode" : async (player, obj) => { 91 | loginSingleton.tryLoginWithoutCode(player, obj); 92 | }, 93 | 94 | "sLogin-TryGetCodeToLogin" : async (player, email) => { 95 | loginSingleton.tryGetCodeToLogin(player, email); 96 | }, 97 | 98 | "sLogin-TryValidateCodeAndLogIn" : async (player, obj) => { 99 | loginSingleton.tryValidateCodeAndLogIn(player, obj); 100 | }, 101 | 102 | "playerQuit" : (player) => { 103 | if (!player.loggedIn) return; 104 | playerSingleton.saveAccount(player); 105 | const onlinePlayers = mp.players.toArray(); 106 | if (onlinePlayers.length < 30) { 107 | for (const p of onlinePlayers) { 108 | p.outputChatBox(`[${misc.getTime()}] ${player.name} ${i18n.get('sLogin', 'disconnected', p.lang)}`); 109 | } 110 | } 111 | }, 112 | }); -------------------------------------------------------------------------------- /app/server/Basic/Vehicles/sVehiclesData.js: -------------------------------------------------------------------------------- 1 | class VehiclesDataSingleton { 2 | constructor() { 3 | this.vehicles = [ 4 | { model: "peyote", title: "Vapid Peyote", fuelTank: 45, fuelRate: 11, price: 20000, }, 5 | { model: "emperor2", title: "Albany Emperor", fuelTank: 60, fuelRate: 16, price: 20000, }, 6 | { model: "dloader", title: "Bravado Duneloader", fuelTank: 80, fuelRate: 25, price: 20000, }, 7 | { model: "dilettante", title: "Karin Dilettante", fuelTank: 40, fuelRate: 2, price: 25000, }, 8 | { model: "tornado3", title: "Declasse Tornado", fuelTank: 50, fuelRate: 11, price: 25000, }, 9 | { model: "panto", title: "Benefactor Panto", fuelTank: 40, fuelRate: 4, price: 30000, }, 10 | { model: "tornado4", title: "Declasse Tornado", fuelTank: 50, fuelRate: 11, price: 30000, }, 11 | { model: "bfinjection", title: "BF Injection", fuelTank: 45, fuelRate: 8, price: 30000, }, 12 | { model: "issi2", title: "Weeny Issi", fuelTank: 50, fuelRate: 7, price: 40000, }, 13 | { model: "moonbeam", title: "Declasse Moonbeam", fuelTank: 70, fuelRate: 25, price: 40000, }, 14 | { model: "rebel", title: "Karin Rebel", fuelTank: 65, fuelRate: 18, price: 40000, }, 15 | { model: "blista", title: "Dinka Blista", fuelTank: 45, fuelRate: 6, price: 45000, }, 16 | { model: "brioso", title: "Grotti Brioso R/A", fuelTank: 40, fuelRate: 5, price: 50000, }, 17 | { model: "voodoo2", title: "Declasse Voodoo", fuelTank: 55, fuelRate: 16, price: 50000, }, 18 | { model: "prairie", title: "Bollokan Prairie", fuelTank: 55, fuelRate: 9, price: 50000, }, 19 | { model: "rhapsody", title: "DeClasse Rhapsody", fuelTank: 50, fuelRate: 7, price: 55000, }, 20 | { model: "rebel2", title: "Karin Rebel", fuelTank: 65, fuelRate: 17, price: 55000, }, 21 | { model: "regina", title: "Dundreary Regina", fuelTank: 45, fuelRate: 7, price: 60000, }, 22 | { model: "bifta", title: "BF Bifta", fuelTank: 40, fuelRate: 6, price: 65000, }, 23 | { model: "emperor", title: "Albany Emperor", fuelTank: 60, fuelRate: 15, price: 65000, }, 24 | { model: "ingot", title: "Vulcar Ingot", fuelTank: 60, fuelRate: 9, price: 70000, }, 25 | { model: "pigalle", title: "Lampadati Pigalle", fuelTank: 55, fuelRate: 12, price: 70000, }, 26 | { model: "tornado", title: "Declasse Tornado", fuelTank: 50, fuelRate: 10, price: 70000, }, 27 | { model: "slamvan", title: "Vapid Slamvan", fuelTank: 60, fuelRate: 20, price: 80000, }, 28 | { model: "blade", title: "Vapid Blade", fuelTank: 65, fuelRate: 20, price: 80000, }, 29 | { model: "tornado2", title: "Declasse Tornado", fuelTank: 50, fuelRate: 10, price: 80000, }, 30 | { model: "tornado5", title: "Declasse Tornado Custom", fuelTank: 55, fuelRate: 10, price: 90000, }, 31 | { model: "buccaneer", title: "Albany Buccaneer", fuelTank: 75, fuelRate: 35, price: 130000, }, 32 | { model: "surge", title: "Cheval Surge", fuelTank: 20, fuelRate: 3, price: 130000, }, 33 | { model: "serrano", title: "Benefactor Serrano", fuelTank: 50, fuelRate: 12, price: 135000, }, 34 | { model: "glendale", title: "Benefactor Glendale", fuelTank: 60, fuelRate: 10, price: 135000, }, 35 | { model: "faction", title: "Willard Faction", fuelTank: 70, fuelRate: 25, price: 140000, }, 36 | { model: "asea", title: "DeClasse Asea", fuelTank: 45, fuelRate: 8.5, price: 140000, }, 37 | { model: "radi", title: "Vapid Radius", fuelTank: 50, fuelRate: 13, price: 145000, }, 38 | { model: "chino", title: "Vapid Chino", fuelTank: 75, fuelRate: 35, price: 150000, }, 39 | { model: "voodoo", title: "Declasse Voodoo Custom", fuelTank: 55, fuelRate: 15, price: 150000, }, 40 | { model: "asterope", title: "Karin Asterope", fuelTank: 45, fuelRate: 9, price: 150000, }, 41 | { model: "primo", title: "Albany Primo", fuelTank: 50, fuelRate: 9, price: 150000, }, 42 | { model: "manana", title: "Albany Manana", fuelTank: 60, fuelRate: 14, price: 150000, }, 43 | 44 | { model: "mule", title: "Maibatsu Mule", fuelTank: 160, fuelRate: 20, price: 25000, }, 45 | { model: "mule2", title: "Maibatsu Mule 2", fuelTank: 200, fuelRate: 25, price: 50000, }, 46 | { model: "mule3", title: "Maibatsu Mule 3", fuelTank: 200, fuelRate: 28, price: 75000, }, 47 | { model: "mule4", title: "Maibatsu Mule Custom", fuelTank: 250, fuelRate: 30, price: 150000, }, 48 | 49 | ]; 50 | } 51 | 52 | getPrice(model) { 53 | for (let i = 0; i < this.vehicles.length; i++) { 54 | if (model !== this.vehicles[i].model) continue; 55 | return this.vehicles[i].price; 56 | } 57 | return false; 58 | } 59 | 60 | getData(model) { 61 | for (let i = 0; i < this.vehicles.length; i++) { 62 | if (model !== this.vehicles[i].model) continue; 63 | return this.vehicles[i]; 64 | } 65 | return false; 66 | } 67 | 68 | } 69 | const vehiclesDataSingleton = new VehiclesDataSingleton(); 70 | module.exports = vehiclesDataSingleton; -------------------------------------------------------------------------------- /app/server/Basic/Auth/sRegister.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const misc = require('../../sMisc'); 4 | const mailer = require('../../sMailer'); 5 | const playerSingleton = require('../sPlayer'); 6 | const AbstractAuth = require('./sAuthAbstract'); 7 | const moneySingleton = require('../Money/sMoney'); 8 | const characterSingleton = require('../../Character/sCharacterCreator'); 9 | const clothesSingleton = require('../../Character/sClothes'); 10 | const headOverlaySingleton = require('../../Character/sHeadOverlay'); 11 | const faction = require('../../Factions/sFaction'); 12 | const prison = require('../../Factions/Police/Prison/sPrison'); 13 | 14 | 15 | class RegiserSingleton extends AbstractAuth { 16 | 17 | async tryGetCodeToRegister(player, email) { 18 | if (!mailer.isEmailValid(email)) { 19 | return this.showError(player, "Email Invalid"); 20 | } 21 | const d = await misc.query(`SELECT email FROM users WHERE email = '${email}' LIMIT 1`); 22 | if (d[0]) { 23 | return this.showError(player, "This email already exists"); 24 | } 25 | // this.trySendCode(player, email); 26 | player.call("cInjectCef", [`app.setMailChecked();`]); 27 | } 28 | 29 | trySendCode(player, email) { 30 | player.email = email; 31 | if (!player.verificationDate) { 32 | return this.sendCode(player, email); 33 | } 34 | const lastGetCodeTime = ((new Date().getTime() - player.verificationDate) / 1000).toFixed(); 35 | if (lastGetCodeTime < 60) { 36 | return this.showError(player, `Wait ${60 - lastGetCodeTime} seconds`); 37 | 38 | } 39 | this.sendCode(player, email); 40 | } 41 | 42 | tryValidateCode(player, code) { 43 | if (!this.checkCode(player, code)) return; 44 | player.call("cInjectCef", [`app.setMailChecked();`]); 45 | } 46 | 47 | async checkUsername(player, obj) { 48 | const data = JSON.parse(obj); 49 | if (!data.firstName || !data.lastName) { 50 | return this.showError(player, "You cant own empty username"); 51 | } 52 | const d = await misc.query(`SELECT firstName, lastName FROM users WHERE firstName = '${data.firstName}' AND lastName = '${data.lastName}' LIMIT 1`); 53 | if (d[0]) { 54 | return this.showError(player, "This nickname already exists"); 55 | } 56 | player.call("cInjectCef", [`app.setNameAvailable();`]); 57 | } 58 | 59 | async tryCreateAccount(player, obj) { 60 | const data = JSON.parse(obj); 61 | if (!mailer.isEmailValid(data.email)) { 62 | return this.showError(player, "Email Invalid"); 63 | } 64 | const d = await misc.query(`SELECT email FROM users WHERE email = '${data.email}' LIMIT 1`); 65 | if (d[0]) { 66 | return this.showError(player, "Something wrong. Try again"); 67 | } 68 | this.createAccount(player, data); 69 | } 70 | 71 | async createAccount(player, d) { 72 | const pass = this.hashPassword(d.pass); 73 | await playerSingleton.createNewUser(player, d.email, d.firstName, d.lastName, pass); 74 | const data = await misc.query(`SELECT id FROM users ORDER BY id DESC LIMIT 1`); 75 | const q1 = moneySingleton.createNewUser(data[0].id); 76 | const q2 = characterSingleton.createNewUser(data[0].id); 77 | const q3 = clothesSingleton.createNewUser(data[0].id); 78 | const q4 = headOverlaySingleton.createNewUser(data[0].id); 79 | const q5 = faction.createNewUser(data[0].id); 80 | const q6 = prison.createNewUser(data[0].id); 81 | 82 | await Promise.all([q1, q2, q3, q4, q5, q6]); 83 | 84 | const mail = { 85 | from: `${mailer.getMailAdress()}`, 86 | to: `${d.email}`, 87 | subject: `Success registration.`, 88 | text: `Hello! Thank you for registration. Here is info about your account, in case you will forget it: FirstName: ${d.firstName} LastName: ${d.lastName} Password: ${d.pass}`, 89 | html: ` Hello!
90 | Thank you for registration.
91 | Here is info about your account, in case you will forget it:
92 | FirstName: ${d.firstName}
93 | LastName: ${d.lastName}
94 | Password: ${d.pass}
`, 95 | } 96 | // mailer.sendMail(mail); 97 | player.call("cInjectCef", [`app.showInfo('Success! Now you can log in.');`]); 98 | } 99 | 100 | } 101 | const regiserSingleton = new RegiserSingleton(); 102 | 103 | mp.events.add({ 104 | "playerReady" : async (player) => { 105 | player.spawn(new mp.Vector3(3222, 5376, 20)); 106 | player.dimension = 1001; 107 | playerSingleton.loadPlayerTemplate(player); 108 | player.call("cLogin-ShowLoginWindow"); 109 | }, 110 | 111 | "sRegister-TryGetCodeToRegister" : async (player, email) => { 112 | regiserSingleton.tryGetCodeToRegister(player, email); 113 | }, 114 | 115 | "sRegister-TryValidateEmailWithCode" : async (player, code) => { 116 | regiserSingleton.tryValidateCode(player, code); 117 | }, 118 | 119 | "sRegister-CheckUsername" : async (player, obj) => { 120 | regiserSingleton.checkUsername(player, obj); 121 | }, 122 | 123 | "sRegister-CreateAccount" : async (player, obj) => { 124 | regiserSingleton.tryCreateAccount(player, obj); 125 | }, 126 | 127 | }); -------------------------------------------------------------------------------- /app/client/Browsers/Character/first.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |
Character Creator
8 |
9 |
<
10 |
11 | {{ genderNames[gender] }} 12 |

{{ i18n.gender }}

13 |
14 |
>
15 |
16 | 17 |
18 |
<
19 |
20 | {{ fatherNames[father] }} 21 |

{{ i18n.father }}

22 |
23 |
>
24 |
25 | 26 |
27 | 28 |
29 | 30 |
31 |
<
32 |
33 | {{ motherNames[mother] }} 34 |

{{ i18n.mother }}

35 |
36 |
>
37 |
38 | 39 |
40 |
41 | 42 |

{{ i18n.skinCol }}

43 |
44 |
45 | 46 |
47 |
48 | 49 |

{{ i18n.rot }}

50 |
51 |
52 | 53 |
{{ i18n.next }}
54 |
55 | 56 | 57 | 58 | 59 | 184 | -------------------------------------------------------------------------------- /app/client/Browsers/Login/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | Get code 17 | 18 | 19 | 20 | 21 | 22 | Check username 23 | Confirm username 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Create account 32 | 33 | {{ errorMessage }} 34 | {{ infoMessage }} 35 | Already have account? Log In 36 | 37 | 38 | 39 | 40 | 41 | 42 | --------------------------------------------------------------------------------