-- ===================================================================== -- Aether Jobs — Jobdefinitionen, Arbeitsorte und Auftragsgenerator -- -- Jeder Arbeitsschritt hat einen ECHTEN Ort in der Welt. Der Spieler -- muss hinfahren; der Server prueft die Entfernung, bevor er bezahlt. -- ===================================================================== Jobs = Jobs or {} Jobs.XP_PRO_LEVEL = 120 -- Erfahrung bis zum naechsten Level Jobs.MAX_LEVEL = 20 -- Hoechstlevel je Job -- Wie nah man am Arbeitsort stehen muss (Meter) Jobs.ORT_RADIUS = 4.0 -- Reichweite zum Jobcenter (Meter) Jobs.INTERAKTION_WEITE = 2.0 -- Standort des Jobcenters (Innenstadt) Jobs.JOBCENTER = vector3(-265.0, -963.5, 31.2) -- --------------------------------------------------------------------- -- Abgabeorte im Stadtgebiet -- Werden fuer Zustellungen, Taxifahrten und Frachten zufaellig gewaehlt. -- --------------------------------------------------------------------- Jobs.Abgabeorte = { { name = 'Legion Square', pos = vector3(195.9, -933.2, 30.7) }, { name = 'Vinewood Blvd', pos = vector3(297.4, 180.6, 104.4) }, { name = 'Vespucci Beach', pos = vector3(-1233.6, -1490.3, 4.4) }, { name = 'Grove Street', pos = vector3(105.8, -1940.0, 21.3) }, { name = 'Sandy Shores', pos = vector3(1961.0, 3740.5, 32.3) }, { name = 'Del Perro', pos = vector3(-1305.6, -394.2, 36.7) }, { name = 'Mirror Park', pos = vector3(1136.5, -982.3, 46.0) }, { name = 'Downtown', pos = vector3(-247.5, -883.4, 31.2) }, { name = 'Rockford Hills', pos = vector3(-1108.5, -455.2, 36.2) }, { name = 'Paleto Bay', pos = vector3(-127.0, 6386.0, 31.5) }, } -- Zufallsdaten fuer Fang und Erz Jobs.Fische = { { n = 'alter Stiefel', w = 5 }, { n = 'Hering', w = 55 }, { n = 'Makrele', w = 85 }, { n = 'Wolfsbarsch', w = 130 }, { n = 'Lachs', w = 190 }, { n = 'Thunfisch', w = 340 }, } Jobs.Erze = { { n = 'Kohle', w = 45 }, { n = 'Eisenerz', w = 80 }, { n = 'Kupfererz', w = 120 }, { n = 'Silbererz', w = 220 }, { n = 'Goldnugget', w = 480 }, } -- --------------------------------------------------------------------- -- Jobs -- -- Je Schritt: -- text Aufgabenbeschreibung -- btn Beschriftung der Aktion -- ort fester vector3 ODER 'zufaellig' (aus Jobs.Abgabeorte) -- ortName Anzeigename bei festem Ort -- dauer Wie lange die Handlung dauert (ms) -- szenario GTA-Szenario, das dabei gespielt wird (optional) -- -- fahrzeug: Dienstfahrzeug, das beim Antreten gestellt wird -- --------------------------------------------------------------------- Jobs.Liste = { { id = 'liefer', name = 'Lieferdienst', icon = '📦', kat = 'Service', farbe = '#4aa8ff', beschreibung = 'Pakete am Depot laden und im Stadtgebiet ausliefern.', lohnVon = 120, lohnBis = 180, xp = 30, fahrzeug = { modell = 'boxville2', spawn = vector4(-424.9, -2789.5, 6.0, 90.0) }, schritte = { { text = 'Fahre zum Depot und lade das Paket ein.', btn = '📦 Paket laden', ort = vector3(-419.5, -2792.3, 6.0), ortName = 'Depot Terminal', dauer = 5000, szenario = 'PROP_HUMAN_BUM_BIN' }, { text = 'Liefere das Paket beim Kunden aus.', btn = '✅ Zustellen', ort = 'zufaellig', dauer = 4000, szenario = 'WORLD_HUMAN_CLIPBOARD' }, }, }, { id = 'muell', name = 'Müllabfuhr', icon = '🗑️', kat = 'Service', farbe = '#7bc96f', beschreibung = 'Mülltonnen einsammeln und am Wertstoffhof entladen.', lohnVon = 90, lohnBis = 140, xp = 26, fahrzeug = { modell = 'trash', spawn = vector4(-343.1, -1560.2, 25.5, 270.0) }, schritte = { { text = 'Fahre die Route ab und sammle die Tonnen ein.', btn = '🗑️ Tonnen leeren', ort = 'zufaellig', dauer = 6000, szenario = 'PROP_HUMAN_BUM_BIN' }, { text = 'Entlade am Wertstoffhof.', btn = '♻️ Entladen', ort = vector3(-351.6, -1546.1, 25.2), ortName = 'Wertstoffhof', dauer = 5000, szenario = 'WORLD_HUMAN_CLIPBOARD' }, }, }, { id = 'taxi', name = 'Taxi', icon = '🚕', kat = 'Transport', farbe = '#facc15', beschreibung = 'Fahrgäste abholen und ans Ziel bringen.', lohnVon = 80, lohnBis = 250, xp = 22, fahrzeug = { modell = 'taxi', spawn = vector4(895.8, -179.1, 74.7, 240.0) }, schritte = { { text = 'Hole den Fahrgast ab.', btn = '🧍 Fahrgast aufnehmen', ort = 'zufaellig', dauer = 3000 }, { text = 'Bringe den Fahrgast ans Ziel.', btn = '🏁 Fahrgast absetzen', ort = 'zufaellig', dauer = 3000 }, }, }, { id = 'trucker', name = 'Trucker', icon = '🚛', kat = 'Transport', farbe = '#f0684a', beschreibung = 'Fracht über Land transportieren — gut bezahlt.', lohnVon = 420, lohnBis = 680, xp = 45, fahrzeug = { modell = 'mule3', spawn = vector4(151.6, -3211.0, 5.9, 90.0) }, schritte = { { text = 'Beladung am Lagerhaus vorbereiten.', btn = '📦 Beladen', ort = vector3(158.7, -3212.5, 5.9), ortName = 'Hafenlager', dauer = 8000, szenario = 'WORLD_HUMAN_CLIPBOARD' }, { text = 'Fahre die Fracht zum Zielort.', btn = '🛣️ Ausliefern', ort = 'zufaellig', dauer = 6000, szenario = 'WORLD_HUMAN_CLIPBOARD' }, }, }, { id = 'tankwart', name = 'Tankwart', icon = '⛽', kat = 'Service', farbe = '#f5a742', beschreibung = 'Kunden an der Zapfsäule bedienen und abkassieren.', lohnVon = 100, lohnBis = 160, xp = 24, schritte = { { text = 'Bediene die Zapfsäule für den Kunden.', btn = '⛽ Betanken', ort = vector3(265.1, -1261.3, 29.3), ortName = 'Tankstelle Strawberry', dauer = 6000, szenario = 'WORLD_HUMAN_VEHICLE_MECHANIC' }, { text = 'Kassiere an der Kasse.', btn = '💳 Kassieren', ort = vector3(263.9, -1256.4, 29.6), ortName = 'Kasse', dauer = 3000, szenario = 'WORLD_HUMAN_CLIPBOARD' }, }, }, { id = 'angeln', name = 'Angeln', icon = '🎣', kat = 'Natur', farbe = '#3fc7e0', beschreibung = 'Am Pier angeln und den Fang am Stand verkaufen.', lohnVon = 5, lohnBis = 340, xp = 18, schritte = { { text = 'Wirf die Angel aus und warte auf einen Biss.', btn = '🎣 Angeln', ort = vector3(-1850.1, -1245.9, 8.6), ortName = 'Pier Vespucci', dauer = 12000, szenario = 'WORLD_HUMAN_STAND_FISHING' }, { text = 'Verkaufe den Fang am Fischstand.', btn = '💰 Verkaufen', ort = vector3(-1817.5, -1193.6, 14.3), ortName = 'Fischstand', dauer = 3000, szenario = 'WORLD_HUMAN_CLIPBOARD' }, }, }, { id = 'holz', name = 'Holzfäller', icon = '🪓', kat = 'Natur', farbe = '#a3703f', beschreibung = 'Bäume fällen und die Stämme zum Sägewerk bringen.', lohnVon = 105, lohnBis = 280, xp = 28, schritte = { { text = 'Fälle den markierten Baum.', btn = '🪓 Baum fällen', ort = vector3(-552.6, 5324.9, 73.6), ortName = 'Wald bei Paleto', dauer = 10000, szenario = 'WORLD_HUMAN_HAMMERING' }, { text = 'Bringe die Stämme zum Sägewerk.', btn = '🪵 Abliefern', ort = vector3(-501.3, 5305.5, 79.5), ortName = 'Sägewerk', dauer = 5000, szenario = 'WORLD_HUMAN_CLIPBOARD' }, }, }, { id = 'bergbau', name = 'Bergbau', icon = '⛏️', kat = 'Natur', farbe = '#9a9aa0', beschreibung = 'Erz in der Mine abbauen, einschmelzen und verkaufen.', lohnVon = 45, lohnBis = 480, xp = 30, schritte = { { text = 'Baue das Erz in der Mine ab.', btn = '⛏️ Erz abbauen', ort = vector3(2949.6, 2792.2, 40.9), ortName = 'Mine Sandy Shores', dauer = 12000, szenario = 'WORLD_HUMAN_HAMMERING' }, { text = 'Schmelze das Erz ein und verkaufe es.', btn = '🔥 Einschmelzen', ort = vector3(1109.4, -2007.9, 31.0), ortName = 'Schmelze Cypress Flats', dauer = 8000, szenario = 'WORLD_HUMAN_WELDING' }, }, }, } --- Liefert die Definition eines Jobs --- @param jobId string Job-Kennung --- @return table|nil function Jobs.Hole(jobId) for _, job in ipairs(Jobs.Liste) do if job.id == jobId then return job end end return nil end local function zufall(a, b) return math.random(a, b) end local function waehle(liste) return liste[math.random(#liste)] end --- Loest die Orte aller Schritte eines Auftrags zu echten Koordinaten auf. --- Wird nur serverseitig aufgerufen. --- @param job table Jobdefinition --- @return table Liste { position = {x,y,z}, name = ... } je Schritt local function LoeseOrteAuf(job) local orte = {} local bereitsGewaehlt = nil for _, schritt in ipairs(job.schritte) do if schritt.ort == 'zufaellig' then -- Nicht zweimal denselben Ort im selben Auftrag verwenden local ziel repeat ziel = waehle(Jobs.Abgabeorte) until not bereitsGewaehlt or ziel.name ~= bereitsGewaehlt or #Jobs.Abgabeorte < 2 bereitsGewaehlt = ziel.name orte[#orte + 1] = { position = { x = ziel.pos.x, y = ziel.pos.y, z = ziel.pos.z }, name = ziel.name, } else orte[#orte + 1] = { position = { x = schritt.ort.x, y = schritt.ort.y, z = schritt.ort.z }, name = schritt.ortName or 'Arbeitsort', } end end return orte end --- Erzeugt einen Auftrag fuer einen Job. --- Wird ausschliesslich serverseitig aufgerufen — Lohn und Orte duerfen --- nie vom Client kommen. --- @param jobId string Job-Kennung --- @param level number Aktuelles Level des Spielers --- @return table|nil Auftrag { ziel, geld, xp, info?, fang?, orte } function Jobs.ErzeugeAuftrag(jobId, level) local job = Jobs.Hole(jobId) if not job then return nil end local bonus = 1 + (math.max(1, level) - 1) * 0.05 local orte = LoeseOrteAuf(job) local auftrag if jobId == 'taxi' then auftrag = { ziel = orte[1].name .. ' → ' .. orte[2].name, geld = math.floor(zufall(80, 250) * bonus + 0.5), xp = job.xp, } elseif jobId == 'angeln' then local fisch = waehle(Jobs.Fische) auftrag = { ziel = 'Pier Vespucci', geld = math.floor(fisch.w * (1 + (level - 1) * 0.06) + 0.5), xp = job.xp, fang = fisch.n, } elseif jobId == 'holz' then local anzahl = zufall(3, 8) auftrag = { ziel = 'Wald bei Paleto', geld = math.floor(anzahl * 35 * bonus + 0.5), xp = job.xp, fang = anzahl .. ' Stämme', } elseif jobId == 'bergbau' then local erz = waehle(Jobs.Erze) local anzahl = zufall(2, 6) auftrag = { ziel = 'Mine Sandy Shores', geld = math.floor(erz.w * anzahl * 0.5 * bonus + 0.5), xp = job.xp, fang = anzahl .. '× ' .. erz.n, } else local vorsatz = { liefer = 'Zustellung: ', muell = 'Route: ', trucker = 'Fracht nach ', tankwart = 'Schicht an der ', } -- Beim Standardauftrag benennt der letzte Ort das Ziel auftrag = { ziel = (vorsatz[jobId] or '') .. orte[#orte].name, geld = math.floor(zufall(job.lohnVon, job.lohnBis) * bonus + 0.5), xp = job.xp, } end auftrag.orte = orte return auftrag end