1
0
Fork 0
forked from Simnation/Main
This commit is contained in:
Nordi98 2025-07-20 20:44:58 +02:00
parent 29c786ff04
commit 6442ff325b
35 changed files with 376 additions and 1949 deletions

View file

@ -1,17 +0,0 @@
# 💬 Dialog
This is a Dialog script where you can talk to specific peds you spawn in config or in other scripts. <br>
## ❓Installation
Pretty simple install given the ui is already built for you. Just drag the root folder into your servers resources folder and make sure to ensure the folder inside of your server.cfg.
## ❗ Dependencies
This resouce is very light on depenedacies. If you do not plan to use this script to spawn peds and would rather spawn them via you own script there are 0 dependencies. Otherwise you will need [ox_target](https://github.com/overextended/ox_target) or [qb-target](https://github.com/qbcore-framework/qb-target)
## Other Info
[**Discord**](https://discord.gg/peYKn8CxHG) <br>
[**Preview**](https://youtu.be/0JWGxLMnOic) <br>
[**Forums**](https://forum.cfx.re/t/free-npc-dialog/5200606) <br>
[**Documentation**](https://st4lth.gitbook.io/st4lth/dialog) <br>
![Picture](https://i.imgur.com/4llTxBH.jpeg)

View file

@ -1,37 +0,0 @@
-- Replace the existing onResourceStart event handler with this:
AddEventHandler('onResourceStart', function(resourceName)
if (GetCurrentResourceName() == resourceName) then
-- Use next() instead of # to check if the table has any entries
if next(Config.peds) ~= nil then
print("Resource started, spawning peds")
SpawnPeds()
else
print("Resource started, but Config.peds is empty")
end
end
end)
-- Also update the framework loading event handler:
if Config.FrameworkLoadinEvent ~= '' then
RegisterNetEvent(Config.FrameworkLoadinEvent, function()
print("Framework loading event triggered")
if next(Config.peds) ~= nil then
print("Spawning peds after framework loaded")
SpawnPeds()
else
print("Framework loaded, but Config.peds is empty")
end
end)
end
-- Add a debug command to help troubleshoot
RegisterCommand('debugpeds', function()
print("Debug peds command triggered")
local count = 0
for k, v in pairs(Config.peds) do
count = count + 1
print("Found ped: " .. k)
end
print("Total peds in config: " .. count)
SpawnPeds()
end, false)

View file

@ -1,148 +0,0 @@
local loadModel = function(modelHash)
if not IsModelValid(modelHash) then
print(modelHash..' Is not a valid model')
return
end
RequestModel(modelHash)
RequestCollisionForModel(modelHash)
while not HasModelLoaded(modelHash) or not HasCollisionForModelLoaded(modelHash) do
Wait(10)
end
end
local getClosestPed = function(coords, max)
local all = GetGamePool('CPed')
local closest, closestCoords
max = max or 2.0
for i = 1, #all do
local ped = all[i]
if IsPedAPlayer(ped) then
return
end
local pedCoords = GetEntityCoords(ped)
local distance = #(coords - pedCoords)
if distance < max then
max = distance
closest = ped
closestCoords = pedCoords
end
end
return closest, closestCoords
end
local loadanimDict = function(animDict)
if not DoesAnimDictExist(animDict) then
print(animDict..' Is not a valid animation dict')
return
end
RequestAnimDict(animDict)
while not HasAnimDictLoaded(animDict) do
Wait(10)
end
end
local playAnimation = function(animData)
if not IsEntityPlayingAnim(animData.ped, animData.dict, animData.lib, 3) then
if not animData.flag then animData.flag = 49 end
loadanimDict(animData.dict)
TaskPlayAnim(animData.ped, animData.dict, animData.lib, 2.0, -1.0, -1, animData.flag, 0, 0, 0, 0)
end
end
SpawnPeds = function()
for v, k in pairs(Config.peds) do
Peds[v] = k
SpawnPed(v, k)
end
end
SpawnPed = function(id, data)
local ped, pedDist = getClosestPed(data.coords)
if data.dict then
dict = data.dict
else
dict = "missbigscore2aig_6"
end
if data.lib then
lib = data.lib
else
lib = "wait_loop"
end
if DoesEntityExist(ped) and pedDist <= 1.2 then
DeletePed(ped)
end
loadModel(data.model)
Peds[id].ped = CreatePed(5, GetHashKey(data.model), data.coords.x, data.coords.y, data.coords.z - 1.0, data.heading, false, false)
SetEntityHeading(Peds[id].ped, data.heading)
SetPedCombatAttributes(Peds[id].ped, 46, true)
SetPedFleeAttributes(Peds[id].ped, 0, 0)
SetBlockingOfNonTemporaryEvents(Peds[id].ped, true)
SetEntityAsMissionEntity(Peds[id].ped, true, true)
FreezeEntityPosition(Peds[id].ped, true)
SetEntityInvincible(Peds[id].ped, true)
SetPedDiesWhenInjured(Peds[id].ped, false)
SetPedHearingRange(Peds[id].ped, 1.0)
SetPedAlertness(Peds[id].ped, 0)
if data.type ~= 'scenario' then
playAnimation({
ped = Peds[id].ped,
dict = dict,
lib = lib,
flag = 1
})
else
TaskStartScenarioInPlace(Peds[id].ped, data.anim, 0, false)
end
opts = {
label = data.label,
icon = data.icon,
action = function()
if data.data then
OpenDialog(Peds[id].ped, data.data)
else
if data.server then
TriggerServerEvent(data.event, Peds[id].ped)
else
TriggerEvent(data.event, Peds[id].ped)
end
end
end
}
if Config.Target == 'qb' then
exports['qb-target']:AddTargetEntity(Peds[id].ped, {
options = { opts },
distance = 2.0
})
elseif Config.Target == 'ox' then
exports.ox_target:addLocalEntity(Peds[id].ped, {opts}) -- Use opts directly
else
print("^1[ERROR] Invalid Target Config! Check your Config.Target setting.^0")
end
end
SetInvisible = function()
while Open do
SetEntityLocallyInvisible(PlayerPedId())
Wait(5)
end
end

View file

@ -1,60 +0,0 @@
Config = {
Target = 'qb',
FrameworkLoadinEvent = 'QBCore:Client:OnPlayerLoaded',
peds = {
['hafenarbeiter'] = {
label = 'Rede mit dem Hafenarbeiter',
icon = 'fa-solid fa-hard-hat',
model = "S_M_M_DockWork_01",
coords = vector3(1243.29, -3196.67, 6.03),
heading = 90,
data = {
firstname = 'Kalle',
lastname = 'Kutter',
text = "Moin... du wirkst nich wie jemand, der hier offiziell was abholen will.",
buttons = {
{
text = "Kommt drauf an, was es hier so gibt...",
data = {
text = "Hehehe... naja, sagen wir mal so: Manche Container stehen nachts ein bisschen... unbeaufsichtigt rum.",
buttons = {
{
text = "Ach ja? Und dann?",
data = {
text = "Na, wenn einer wüsste, wie man da *rein* kommt... bräuchte er sicher was mit Kraft oder Strom, verstehste?",
buttons = {
{
text = "Klar. Ich versteh schon.",
data = {
text = "Gut. Dann hab ich dir ja nix gesagt, oder?",
buttons = {
{
text = "Du hast mich nie gesehen.",
close = true
}
}
}
},
{
text = "Klingt mir zu heiß...",
close = true
}
}
}
},
{
text = "Ich glaub, ich hab mich verlaufen...",
close = true
}
}
}
},
{
text = "Nur mal umschauen, Chef.",
close = true
}
}
}
}
} -- schließt `peds`
} -- schließt `Config`

View file

@ -1,24 +0,0 @@
fx_version 'cerulean'
game 'gta5'
author 'ST4lTH'
description 'Dialog'
version '1.0.0'
client_scripts {
'client/*.lua',
}
shared_scripts {
'config.lua',
}
files {
'web/dist/index.html',
'web/dist/**/*',
}
lua54 'yes'
ui_page 'web/dist/index.html'
--ui_page 'http://localhost:3000/' -- Dev

View file

@ -1,4 +0,0 @@
> 1%
last 2 versions
not dead
not ie 11

View file

@ -1,5 +0,0 @@
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true

View file

@ -1,22 +0,0 @@
.DS_Store
node_modules
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

View file

@ -1,44 +0,0 @@
# default
## Project setup
```
# yarn
yarn
# npm
npm install
# pnpm
pnpm install
```
### Compiles and hot-reloads for development
```
# yarn
yarn dev
# npm
npm run dev
# pnpm
pnpm dev
```
### Compiles and minifies for production
```
# yarn
yarn build
# npm
npm run build
# pnpm
pnpm build
```
### Customize configuration
See [Configuration Reference](https://vitejs.dev/config/).

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="./favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Welcome to Vuetify 3</title>
<script type="module" crossorigin src="./assets/index-8c3f21c3.js"></script>
<link rel="stylesheet" href="./assets/index-a3506647.css">
</head>
<body>
<div id="app"></div>
</body>
</html>

View file

@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Welcome to Vuetify 3</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View file

@ -1,19 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,22 +0,0 @@
{
"name": "web",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@mdi/font": "7.0.96",
"roboto-fontface": "*",
"vue": "^3.2.0",
"vuetify": "^3.0.0",
"webfontloader": "^1.0.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.0.0",
"sass": "^1.69.7",
"vite": "^4.2.0",
"vite-plugin-vuetify": "^1.0.0"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,165 +0,0 @@
<template>
<v-app style="background: transparent!important;">
<v-fade-transition>
<div class="dialog-bg" v-if="show">
<v-card color="bg" class="card ">
<v-card-title class="title">
<div>
<span class="font-weight-bold mr-2">
{{ data.firstname }}
</span>
<span class="font-weight-light">
{{ data.lastname }}
</span>
</div>
<div style="flex-grow: 1;">
</div>
<div class="note" v-if="data.rep">
{{ data.rep }} Rep
</div>
<div class="note" v-if="data.type">
{{ data.type }}
</div>
</v-card-title>
<div class="text-dialog">
<div class="text bg-grey-darken-4 rounded">
{{ data.text }}
</div>
<div class="buttons">
<div class="button" v-for="(item, index) in data.buttons" @keyup.index="click(item)" @click="click(item)">
<div class="number">
<p>
{{ index+1 }}
</p>
</div>
{{ item.text }}
</div>
</div>
</div>
</v-card>
</div>
</v-fade-transition>
</v-app>
</template>
<script>
export default {
name: 'App',
components: {
},
data: () => ({
show: false,
data: {
firstname: 'John',
lastname: 'Doe',
text: 'Lorem ipsum dolor sit amet',
type: '',
rep: '',
buttons: [
{ text: 'Lorem ipsum dolor sit amet' }
]
}
}),
methods: {
click(data){
post('click', data, (resp) => {
if (resp == 'close') {
this.show = false;
this.data = null;
}
})
},
},
mounted() {
this.escapeListener = window.addEventListener("keyup", (event) => {
if (!this.show) {
return
}
if (event.keyCode || 49 && event.keyCode || 51 && event.keyCode || 52 && event.keyCode || 53) {
if ( this.data.buttons[event.keyCode - 49] ) {
this.click(this.data.buttons[event.keyCode - 49])
}
}
});
this.messageListener = window.addEventListener("message", (event) => {
const item = event.data || event.detail; //'detail' is for debugging via browsers
if (item.type == 'New') {
this.data = item.data
this.show = true
} else if (item.type == 'Continue') {
this.data.text = item.data.text
this.data.buttons = item.data.buttons
} else if (item.type == 'Set') {
this.data = item.data
this.show = true
} else if (item.type == 'Close') {
this.show = false;
this.data = null;
}
});
},
}
const resource = 'dialog';
const post = (event, data, cb) => {
if (event) {
fetch(`https://${resource}/${event}`, {
method: "POST",
headers: {
"Content-Type": "application/json; charset=UTF-8",
},
body: JSON.stringify(data || {}),
})
.then((resp) => resp.json())
.then((resp) => {
if (cb) {
cb(resp);
}
});
}
};
</script>
<style>
html {
overflow: hidden!important;
}
:root {
color-scheme: light !important;
}
#app {
background: transparent!important;
background-color: transparent!important;
}
/* width */
::-webkit-scrollbar {
width: 0px;
height: 0px;
}
/* Track */
::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #888;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #555;
}
</style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -1,6 +0,0 @@
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M261.126 140.65L164.624 307.732L256.001 466L377.028 256.5L498.001 47H315.192L261.126 140.65Z" fill="#1697F6"/>
<path d="M135.027 256.5L141.365 267.518L231.64 111.178L268.731 47H256H14L135.027 256.5Z" fill="#AEDDFF"/>
<path d="M315.191 47C360.935 197.446 256 466 256 466L164.624 307.732L315.191 47Z" fill="#1867C0"/>
<path d="M268.731 47C76.0026 47 141.366 267.518 141.366 267.518L268.731 47Z" fill="#7BC6FF"/>
</svg>

Before

Width:  |  Height:  |  Size: 526 B

View file

@ -1,68 +0,0 @@
.dialog-bg {
position: absolute;
width: 100%;
height: 100%;
background: radial-gradient(circle, rgba(133,133,133,0) 41%, rgba(1,214,193,0.3491771708683473) 100%);
}
.card {
position: absolute!important;
right: 0;
left: 0;
margin: auto !important;
width: fit-content;
bottom: 10vh;
.title {
display: flex;
align-items: center;
.note {
margin-left: 10px;
height: fit-content;
font-size: 15px;
background-color: rgba(0, 255, 255, 0.681);
border-radius: 5px;
color: #0e0e0e;
padding: 8px;
line-height: 6px;
}
}
.text-dialog {
margin: 2px 10px 10px 10px;
.text {
width: 610px;
padding: 5px 10px;
margin-bottom: 10px;
background: radial-gradient(circle, rgb(58, 58, 58) 0%, rgb(36, 36, 36) 100%);
}
.buttons {
display: grid;
grid-template-columns: 302px 302px;
grid-row: auto auto;
grid-column-gap: 7px;
grid-row-gap: 7px;
.button {
display: flex;
padding: 3px;
border-radius: 4px;
font-size: 12px;
align-items: center;
background-color: rgb(36, 36, 36);
.number {
margin-right: 8px;
width: 23px;
height: 23px;
border-radius: 4px;
background-color: rgb(0, 255, 255);
p {
color: #414041;
text-align: center;
font-weight: bold;
font-size: 15px;
}
}
}
}
}
}

View file

@ -1,23 +0,0 @@
/**
* main.js
*
* Bootstraps Vuetify and other plugins then mounts the App`
*/
// Components
import './assets/styles/style.scss'
import App from './App.vue'
// Composables
import { createApp } from 'vue'
// Plugins
import { registerPlugins } from '@/plugins'
const app = createApp(App)
registerPlugins(app)
app.mount('#app')

View file

@ -1,14 +0,0 @@
/**
* plugins/index.js
*
* Automatically included in `./src/main.js`
*/
// Plugins
import { loadFonts } from './webfontloader'
import vuetify from './vuetify'
export function registerPlugins (app) {
loadFonts()
app.use(vuetify)
}

View file

@ -1,28 +0,0 @@
/**
* plugins/vuetify.js
*
* Framework documentation: https://vuetifyjs.com`
*/
// Styles
import '@mdi/font/css/materialdesignicons.css'
import 'vuetify/styles'
// Composables
import { createVuetify } from 'vuetify'
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
export default createVuetify({
theme: {
defaultTheme: 'dark',
themes: {
dark: {
colors: {
primary: '#1867C0',
secondary: '#5CBBF6',
bg: '#121212'
},
},
},
},
})

View file

@ -1,15 +0,0 @@
/**
* plugins/webfontloader.js
*
* webfontloader documentation: https://github.com/typekit/webfontloader
*/
export async function loadFonts () {
const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader')
webFontLoader.load({
google: {
families: ['Roboto:100,300,400,500,700,900&display=swap'],
},
})
}

View file

@ -1,42 +0,0 @@
// Plugins
import vue from '@vitejs/plugin-vue'
import vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
// Utilities
import { defineConfig } from 'vite'
import { fileURLToPath, URL } from 'node:url'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue({
template: { transformAssetUrls }
}),
// https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin
vuetify({
autoImport: true,
}),
],
define: { 'process.env': {} },
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
extensions: [
'.js',
'.json',
'.jsx',
'.mjs',
'.ts',
'.tsx',
'.vue',
],
},
server: {
port: 3000,
},
build: {
outDir: "./dist",
},
base: "",
})

View file

@ -0,0 +1,142 @@
local QBCore = exports['qb-core']:GetCoreObject()
local spawnedNPCs = {}
-- NPC Spawn Function
local function SpawnNPC(npcId, npcData)
local model = GetHashKey(npcData.model)
RequestModel(model)
while not HasModelLoaded(model) do
Wait(1)
end
local npc = CreatePed(4, model, npcData.coords.x, npcData.coords.y, npcData.coords.z - 1.0, npcData.coords.w, false, true)
SetEntityHeading(npc, npcData.coords.w)
FreezeEntityPosition(npc, true)
SetEntityInvincible(npc, true)
SetBlockingOfNonTemporaryEvents(npc, true)
-- QB-Target hinzufügen
exports['qb-target']:AddTargetEntity(npc, {
options = {
{
type = 'client',
event = 'npc-dialog:client:openDialog',
icon = npcData.targetIcon,
label = npcData.targetLabel,
npcId = npcId
}
},
distance = 2.0
})
spawnedNPCs[npcId] = npc
SetModelAsNoLongerNeeded(model)
end
-- Alle NPCs spawnen
CreateThread(function()
for npcId, npcData in pairs(Config.NPCs) do
SpawnNPC(npcId, npcData)
end
end)
-- Rekursive Funktion zum Erstellen der Dialog-Optionen
local function CreateDialogOptions(options, npcId, parentId)
local contextOptions = {}
for i, option in ipairs(options) do
local optionData = {
title = option.title,
description = option.description,
icon = option.icon,
}
if option.info then
-- Direkte Info anzeigen
optionData.onSelect = function()
lib.alertDialog({
header = option.title,
content = option.info,
centered = true,
cancel = true,
labels = {
cancel = 'Zurück'
}
})
end
elseif option.response then
-- Weiteres Dialog-Level
optionData.onSelect = function()
local contextId = 'npc_dialog_' .. npcId .. '_' .. i .. '_' .. (parentId or 'main')
local responseOptions = CreateDialogOptions(option.response.options, npcId, contextId)
-- Zurück-Option hinzufügen
table.insert(responseOptions, {
title = "← Zurück",
description = "Zum vorherigen Dialog",
icon = 'arrow-left',
onSelect = function()
if parentId then
lib.showContext(parentId)
else
lib.showContext('npc_dialog_' .. npcId)
end
end
})
lib.registerContext({
id = contextId,
title = option.response.title,
options = responseOptions
})
lib.showContext(contextId)
end
end
table.insert(contextOptions, optionData)
end
return contextOptions
end
-- Dialog öffnen
RegisterNetEvent('npc-dialog:client:openDialog', function(data)
local npcId = data.npcId
local npcData = Config.NPCs[npcId]
if not npcData then return end
local options = CreateDialogOptions(npcData.dialog.options, npcId)
-- Verlassen Option hinzufügen
table.insert(options, {
title = "Verlassen",
description = "Dialog beenden",
icon = 'times',
onSelect = function()
-- Dialog schließt sich automatisch
end
})
lib.registerContext({
id = 'npc_dialog_' .. npcId,
title = npcData.dialog.title,
options = options
})
lib.showContext('npc_dialog_' .. npcId)
end)
-- Cleanup beim Resource Stop
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
for _, npc in pairs(spawnedNPCs) do
if DoesEntityExist(npc) then
DeleteEntity(npc)
end
end
end)

View file

@ -0,0 +1,213 @@
Config = {}
Config.NPCs = {
['kalle_kutter'] = {
model = 's_m_m_dockwork_01',
coords = vector4(1234.56, -3210.45, 5.9, 90.0),
name = "Kalle Kutter",
targetLabel = 'Red mit dem Hafenarbeiter',
targetIcon = 'fa-solid fa-hard-hat',
dialog = {
title = "Kalle Kutter - Hafenarbeiter",
description = "Moin... du wirkst nich wie jemand, der hier offiziell was abholen will.",
options = {
{
title = "Kommt drauf an, was es hier so gibt...",
description = "Neugierig nachfragen",
icon = 'eye',
response = {
title = "Kalle Kutter",
description = "Hehehe... naja, sagen wir mal so: Manche Container & Trailer stehen nachts ein bisschen... unbeaufsichtigt rum.",
options = {
{
title = "Ach ja? Und dann?",
description = "Weiterfragen",
icon = 'question',
response = {
title = "Kalle Kutter",
description = "Na, wenn einer wüsste, wie man da *rein* kommt... bräuchte er sicher was mit Kraft oder Strom, verstehste?",
options = {
{
title = "Klar. Ich versteh schon.",
description = "Verstehen zeigen",
icon = 'handshake',
response = {
title = "Kalle Kutter",
description = "Gut. Dann hab ich dir ja nix gesagt, oder?",
options = {
{
title = "Du hast mich nie gesehen.",
description = "Verschwiegenheit zusichern",
icon = 'eye-slash',
info = "Kalle nickt zufrieden und wendet sich wieder seiner Arbeit zu.\n\n*Du hast einen Hinweis auf nächtliche Aktivitäten am Hafen erhalten...*"
}
}
}
},
{
title = "Klingt mir zu heiß...",
description = "Ablehnen",
icon = 'times',
info = "Kalle zuckt mit den Schultern.\n\n'Deine Entscheidung, Kumpel. Aber vergiss unser Gespräch.'"
}
}
}
},
{
title = "Ich glaub, ich hab mich verlaufen...",
description = "Rückzieher machen",
icon = 'walking',
info = "Kalle mustert dich misstrauisch.\n\n'Ja... das glaub ich auch. Verschwinde besser.'"
}
}
}
},
{
title = "Nur mal umschauen, Chef.",
description = "Unschuldig tun",
icon = 'smile',
info = "Kalle brummt etwas Unverständliches und wendet sich ab.\n\n'Dann schau zu, dass du wieder wegkommst...'"
}
}
}
},
['maria_helper'] = {
model = 'a_f_y_business_01',
coords = vector4(-1037.23, -2738.94, 20.17, 330.0),
name = "Maria die Helferin",
targetLabel = 'Mit Maria sprechen',
targetIcon = 'fas fa-info-circle',
dialog = {
title = "Maria die Helferin",
description = "Hallo Schatz! Du siehst aus als könntest du Hilfe gebrauchen. Lass uns reden!",
options = {
{
title = "Hi Maria! Kannst du mir helfen?",
description = "Höflich um Hilfe bitten",
icon = 'hands-helping',
response = {
title = "Maria die Helferin",
description = "Natürlich helfe ich dir gerne! Dafür bin ich ja da. Ich liebe es, neuen Leuten zu helfen sich hier zurechtzufinden. Was bereitet dir Kopfzerbrechen?",
options = {
{
title = "Ich verstehe das Jobsystem nicht",
description = "Nach Jobs fragen",
icon = 'briefcase',
response = {
title = "Maria die Helferin",
description = "Ach so! Das Jobsystem kann am Anfang verwirrend sein. Aber keine Sorge, ich erkläre es dir gerne. Was möchtest du wissen?",
options = {
{
title = "Wie finde ich einen Job?",
description = "Jobsuche",
icon = 'search',
info = "Jobs finden in Los Santos:\n\n1. Gehe zum Rathaus (Stadtzentrum)\n2. Nutze den Job-Computer\n3. Wähle einen verfügbaren Job\n4. Folge den Anweisungen\n5. Verdiene dein erstes Geld!\n\nEinfache Jobs: Taxi, Müll, Lieferungen\nSchwere Jobs: Polizei, Medic, Mechaniker"
},
{
title = "Welcher Job ist für Anfänger?",
description = "Anfänger-Jobs",
icon = 'play',
response = {
title = "Maria die Helferin",
description = "Für Anfänger empfehle ich immer die einfachen Jobs. Sie bringen zwar nicht so viel Geld, aber du lernst die Stadt kennen und es ist stressfrei.",
options = {
{
title = "Taxi fahren",
description = "Als Taxifahrer arbeiten",
icon = 'taxi',
info = "Taxi Job - Perfekt für Anfänger:\n\n• Lerne die ganze Stadt kennen\n• Triff verschiedene Leute\n• Flexibel - arbeite wann du willst\n• Gutes Geld für Anfänger\n• Kein Stress\n\nHole dir ein Taxi am Taxi-Depot und leg los!"
},
{
title = "Müll sammeln",
description = "Müllmann werden",
icon = 'trash',
info = "Müll Job - Einfach und entspannt:\n\n• Fahre durch die Stadt\n• Sammle Müll an verschiedenen Punkten\n• Regelmäßiges Einkommen\n• Keine besonderen Fähigkeiten nötig\n• Kannst Musik hören dabei\n\nNicht glamourös, aber ehrliche Arbeit!"
}
}
}
}
}
}
},
{
title = "Ich habe Probleme mit anderen Spielern",
description = "Soziale Probleme",
icon = 'users',
response = {
title = "Maria die Helferin",
description = "Oh nein, das tut mir leid zu hören! Leider gibt es überall schwarze Schafe. Aber lass dich nicht entmutigen, die meisten hier sind wirklich nett. Was ist passiert?",
options = {
{
title = "Jemand war unhöflich zu mir",
description = "Über Unhöflichkeit beschweren",
icon = 'frown',
info = "Umgang mit unhöflichen Spielern:\n\n• Bleib ruhig und professionell\n• Lass dich nicht provozieren\n• Nutze /report bei Regelverstößen\n• Blockiere toxische Spieler\n• Suche dir nette Leute als Freunde\n\nDie meisten Spieler sind freundlich - lass dir nicht die Laune verderben!"
},
{
title = "Ich wurde grundlos angegriffen",
description = "RDM/VDM Problem",
icon = 'exclamation-triangle',
info = "Bei Regelbrüchen (RDM/VDM):\n\n• Sofort /report [Spieler-ID] [Grund]\n• Screenshots/Videos als Beweis\n• Nicht zurück angreifen\n• Admins werden sich darum kümmern\n• Solche Spieler werden bestraft\n\nDeine Sicherheit ist wichtig - melde jeden Regelbruch!"
}
}
}
}
}
}
},
{
title = "Wie ist das Leben hier so?",
description = "Smalltalk über die Stadt",
icon = 'comment',
response = {
title = "Maria die Helferin",
description = "Oh, das Leben hier ist aufregend! Jeden Tag passiert etwas Neues. Ich liebe diese Stadt, auch wenn sie manchmal chaotisch ist. Was denkst du denn bisher darüber?",
options = {
{
title = "Es ist überwältigend",
description = "Sich überfordert fühlen",
icon = 'dizzy',
response = {
title = "Maria die Helferin",
description = "Das kann ich total verstehen! Als ich hierher kam, war ich auch völlig überfordert. So viele Menschen, so viel Lärm, so viele Möglichkeiten. Aber weißt du was? Das wird besser!",
options = {
{
title = "Wie hast du dich eingelebt?",
description = "Nach Erfahrungen fragen",
icon = 'question',
info = "Marias Eingewöhnungs-Tipps:\n\n• Nimm dir Zeit - kein Stress\n• Suche dir 2-3 feste Orte als 'Basis'\n• Lerne jeden Tag eine neue Straße\n• Sprich mit Leuten - die meisten helfen gern\n• Hab Geduld mit dir selbst\n\n'Rom wurde auch nicht an einem Tag erbaut!'"
},
{
title = "Wird es wirklich einfacher?",
description = "Bestätigung suchen",
icon = 'heart',
info = "Marias aufmunternde Worte:\n\n'Ja, das verspreche ich dir! In ein paar Wochen wirst du dich wie zu Hause fühlen. Du wirst deine Lieblingsorte haben, nette Leute kennen und die Stadt lieben.\n\nJeder hat mal klein angefangen. Gib nicht auf!'"
}
}
}
},
{
title = "Es gefällt mir sehr gut!",
description = "Begeisterung zeigen",
icon = 'smile',
response = {
title = "Maria die Helferin",
description = "Das freut mich so sehr zu hören! Du hast die richtige Einstellung. Mit so einer positiven Art wirst du hier viele Freunde finden und erfolgreich sein!",
options = {
{
title = "Was liebst du am meisten hier?",
description = "Nach Marias Lieblingssachen fragen",
icon = 'heart',
info = "Was Maria an Los Santos liebt:\n\n• Die Vielfalt der Menschen\n• Jeden Tag neue Abenteuer\n• Die wunderschönen Sonnenuntergänge\n• Die Möglichkeit, alles zu werden\n• Die Freundschaften die entstehen\n• Das Gefühl von Freiheit\n\n'Diese Stadt hat eine besondere Magie!'"
}
}
}
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,21 @@
fx_version 'cerulean'
game 'gta5'
author 'YourName'
description 'Simple NPC Info Dialog System'
version '1.0.0'
shared_scripts {
'@ox_lib/init.lua',
'config.lua'
}
client_scripts {
'client/client.lua'
}
dependencies {
'qb-core',
'qb-target',
'ox_lib'
}