Всем привет, на работке в свободное время решил написать небольшую работу для этого замечательного ресурса. Может кому будет интересно, плюс если кто сможет, небольшое код-ревью дать, буду рад критике и советам.
Работа на чистом nodeJs + vue.
Начнём с серверной части, "рассадим" деревья по точкам:
Создадим массив с id-идентификатором дерева(понадобится в дальнейшем), и координатами(место "посадки" дерева)
Теперь деревья нужно "рассадить", логичнее всего это делать при загрузке сервера, поэтому используем событие "packagesLoaded":
Для установки деревьев выбрал метод перебора созданного раннее массива allTrees - forEach, в котором создаём дерево для каждого объекта в массиве, а также назначаем каждому дереву собственный id и задаём собственный вариабл с этим же id для его использования уже на клиентской части.
Для визуального восприятия добавим надпись для каждого дерева "Нажмите [Е]".
Ну и немного работы с консолью в конце.
Получилось прекрасно..
Далее, чтобы начать взаимодействовать с этими деревьями нужно перейти в клиентскую часть.
Чтобы устроиться на работу, нужно для начала найти компанию(в данном случае npc), которая даст вам эту самую работу.
Создадим массив обектов(в данном случае, он один), зададим в нём координаты npc, его направление(heading), имя и модель
Почему массив объектов, потому что в дальнейшем можно будет с лёгкостью добавить ещё одного npc.
Далее расставим npc всё тем же методом forEach, добавим под него колшейп, дадим класс этому колшейпу, чтобы в дальнейшем определять, что мы стоим перед этим самым npc, повесим над ним пару надписей и добавим блип на карту:
Вот он... Красавчик стоит, в шлёпках да в футболке, заправленной в трусы, а на футболке той топор нарисован, значит лесоруб, верно?
Теперь для того. чтобы взаимодействовать с данным npc, нужно использовать метод playerEnterColshape, также потребуется метод бинда кнопки mp.keys.bind, при нажатии на которую начнётся то самое взаимодействие.
Также потребуется отвязывать кнопку Е, сделаем это методом playerExitColshape
Сама функция, отвечающая за показ диалога с Логаном:
Для того, чтобы проверять открыт ли диалог с данным npc или нет, создадим переменную lumberjackOpened и назначим ей значение сразу false (мы же не хотим по пустякам вызывать CEF на клиенте?)
По быстренькому накидаем страничку с диалогом:
css:
js
Теперь при нажатии на кнопку Е, рядом с npc появится небольшое простое окно:
Готово, осталось прописать события, которые мы вызываем при нажатии кнопок.
Закрыть:
Принять работу, тут уже потребуется взаимодействие с серверной частью:
Перейдём обратно в серверную часть и пропишем событие server:startLumberjack.
То самое событие lumberJackStatus
Для того, чтобы понимать, где дерево и когда можно использовать кнопку Е для рубки, нужно научиться определять, что перед персонажем стоит нужный нам объект(а дерево - это объект). Для этого используем событие render и метод testCapsule.
Теперь пропишем функцию useHatch, которую мы биндили ранее:
Перейдём на серверную часть и пропишем событие server:startHatching
animActive на клиенте:
Осталось лишь купить топор, и можно даже поработать! Для этого вернёмся на клиент и пропишем событие buyHatchet для созданной ранее кнопки
Перейдём обратно на сервер и пропишем там вызываемое событие:
но для начала пропишем фиксированную сумму за топор
Для того, чтобы задать свойства персонажу, такие как деньги и кол-во брёвен, используем событие playerReady
Отлично, топор можно купить, даже деревья им рубить, осталось только научиться продавать брёвна
Для этого мы возвращаемся на клиент и прописываем там событие sellLogs:
Переходим на сервер:
Вот и всё, наработались, теперь осталось уволиться
Для этого опять возвращаемся на клиент и прописываем событие stopLumberjack:
Сервер:
Вот и всё, вы уволены, можно пойти попить пивка
Работа готова, вроде всё на месте
Спасибо за внимание!
Работа на чистом nodeJs + vue.
Начнём с серверной части, "рассадим" деревья по точкам:
Создадим массив с id-идентификатором дерева(понадобится в дальнейшем), и координатами(место "посадки" дерева)
JavaScript:
const allTrees = [
{
id: 1,
x: 8.031051635742188,
y: 10.031051635742188,
z: 70.78174591064453,
},
{
id: 2,
x: 48.031051635742188,
y: 10.031051635742188,
z: 70.78174591064453,
},
{
id: 3,
x: 38.031051635742188,
y: 10.031051635742188,
z: 70.78174591064453,
},
{
id: 4,
x: 28.031051635742188,
y: 10.031051635742188,
z: 70.78174591064453,
},
{
id: 5,
x: 18.031051635742188,
y: 10.031051635742188,
z: 70.78174591064453,
},
];
JavaScript:
mp.events.add("packagesLoaded", () => {
console.log(`[INFO] Работа дровосека загружена`);
allTrees.forEach((tree) => {
const trees = mp.objects.new(
"prop_tree_fallen_pine_01",
new mp.Vector3(tree.x, tree.y, tree.z - 1),
{
dimension: 0,
}
);
trees.treeId = tree.id;
trees.setVariable("treeId", trees.treeId);
trees.treesLabel = mp.labels.new(
"Нажмите [Е]",
new mp.Vector3(tree.x, tree.y + 1.1, tree.z),
{
los: false,
font: 0,
drawDistance: 20,
color: [70, 130, 180, 170],
dimension: 0,
}
);
});
console.log(`[INFO] Деревьев посажено: ${allTrees.length}`);
});
Для визуального восприятия добавим надпись для каждого дерева "Нажмите [Е]".
Ну и немного работы с консолью в конце.
Получилось прекрасно..
Далее, чтобы начать взаимодействовать с этими деревьями нужно перейти в клиентскую часть.
Чтобы устроиться на работу, нужно для начала найти компанию(в данном случае npc), которая даст вам эту самую работу.
Создадим массив обектов(в данном случае, он один), зададим в нём координаты npc, его направление(heading), имя и модель
Почему массив объектов, потому что в дальнейшем можно будет с лёгкостью добавить ещё одного npc.
JavaScript:
const lumberjackNpc = [
{
x: 18.32457160949707,
y: 26.563514709472656,
z: 70.61154174804688,
heading: "159.72958374023438",
name: "Логан",
model: "a_m_m_salton_02",
},
];
JavaScript:
lumberjackNpc.forEach((lumberjack) => {
const lumberjackBot = mp.peds.new(
mp.game.joaat(lumberjack.model),
new mp.Vector3(lumberjack.x, lumberjack.y, lumberjack.z),
lumberjack.heading,
0
);
const lumberjackStartColshape = mp.colshapes.newSphere(
parseFloat(lumberjack.x),
parseFloat(lumberjack.y),
parseFloat(lumberjack.z),
2,
0
);
lumberjackStartColshape.class = "lumberjack";
mp.labels.new(
lumberjack.name,
{
x: lumberjackBot.position.x,
y: lumberjackBot.position.y,
z: lumberjackBot.position.z + 0.9,
},
{ los: true, font: 0, drawDistance: 20, color: [70, 130, 180, 170] }
);
mp.labels.new(
"[Работа: Лесоруб]",
{
x: lumberjackBot.position.x,
y: lumberjackBot.position.y,
z: lumberjackBot.position.z + 1.2,
},
{ los: true, font: 0, drawDistance: 20, color: [255, 255, 255, 170] }
);
const lumberjackBlip = mp.blips.new(
792,
new mp.Vector3(lumberjackBot.position.x, lumberjackBot.position.y, 0),
{
name: "Лесоруб",
scale: 0.7,
color: 5,
shortRange: true,
dimension: 0,
}
);
});
Теперь для того. чтобы взаимодействовать с данным npc, нужно использовать метод playerEnterColshape, также потребуется метод бинда кнопки mp.keys.bind, при нажатии на которую начнётся то самое взаимодействие.
JavaScript:
mp.events.add("playerEnterColshape", (shape) => {
if (shape.class == "lumberjack") { // проверяем, на том ли мы колшейпе
mp.keys.bind(0x45, true, openLumberjack); // биндим кнопку
}
});
JavaScript:
mp.events.add("playerExitColshape", (shape) => {
if (shape.class == "lumberjack") {
mp.keys.unbind(0x45, true, openLumberjack);
}
});
Сама функция, отвечающая за показ диалога с Логаном:
JavaScript:
function openLumberjack() {
if (lumberjackOpened) return;
browser.execute("windows.lumberjack.lumberjackOpened = true");
mp.gui.cursor.show(true, true);
lumberjackOpened = true;
mp.players.local.freezePosition(true);
}
JavaScript:
let lumberjackOpened = false;
По быстренькому накидаем страничку с диалогом:
HTML:
<template>
<div v-if="lumberjackOpened" id="lumberjack" class="lumberjack">
<h1>Логан</h1>
<p>
Приветствую, для работы потребуется только топор и немного терпения,
дерзай!
</p>
<div class="buttons">
<button v-if="!lumberjackStatus" @click="startLumberjack">
Принять работу
</button>
<button v-if="lumberjackStatus" @click="stopLumberjack">Уволиться</button>
<button @click="buyHatchet">Купить топор</button>
<button @click="sell">Продать брёвна</button>
<button @click="close">Закрыть</button>
</div>
</div>
</template>
<script src="./js/Lumberjack.js"></script>
<style src="./css/Lumberjack.css"></style>
css:
CSS:
.lumberjack {
display: block;
width: 700px;
height: 600px;
box-shadow: 1px black;
border: none;
margin: auto;
margin-top: 100px;
border-radius: 5px;
background-color: rgba(0, 0, 0, 0.512);
text-align: center;
color: white;
font-size: 1.3em;
font-family: 'Courier New', Courier, monospace;
}
.lumberjack p {
border-top: 1px solid white;
border-bottom: 1px solid white;
padding: 10px;
background: #00000085;
}
.buttons {
display: inline-grid;
}
.buttons button {
margin: auto;
margin-top:5px;
width: 150px;
height: 40px;
border-radius: 5px;
border: none;
transition: 0.2s;
background-color:rgba(20, 20, 20, 0.547);
color: white;
}
.buttons button:hover{
background-color:grey;
}
js
JavaScript:
export default {
name: "Lumberjack",
data() {
return {
lumberjackOpened: false,
lumberjackStatus: false
};
},
methods: {
sell() {
window.mp.trigger("sellLogs");
},
stopLumberjack() {
window.mp.trigger("stopLumberjack");
},
startLumberjack() {
window.mp.trigger("startLumberjack");
},
buyHatchet() {
window.mp.trigger("buyHatchet");
},
close() {
this.lumberjackOpened = false;
window.mp.trigger("closeLumberjack");
}
}
};
Готово, осталось прописать события, которые мы вызываем при нажатии кнопок.
Закрыть:
JavaScript:
mp.events.add("closeLumberjack", () => {
closeLumberjack(); // вызываем функцию закрытия окна
});
function closeLumberjack() { // сама функция
lumberjackOpened = false;
mp.gui.cursor.show(false, false);
mp.players.local.freezePosition(false);
}
Принять работу, тут уже потребуется взаимодействие с серверной частью:
Код:
mp.events.add("startLumberjack", () => {
mp.events.callRemote("server:startLumberjack"); // вызываем событие на сервере
});
JavaScript:
mp.events.add("server:startLumberjack", (player) => {
if (player.worker) return player.notify("Вы уже начали работу лесоруба"); // простая проверка на то, работает ли уже персонаж
player.worker = true; // даём персонажу свойство, по которому определять, работает ли персонаж или нет
player.notify("Вы устроились лесорубом"); // оповещаем персонажа
player.setVariable("lumberjack", true); // вешаем на персонажа вариабл, чтобы использовать его на клиенте, для проверки, дровосек ли персонаж
player.call("lumberJackStatus", [true]); // вызов эвента, который отключает кнопку "Принять работу" и включает кнопку "Уволиться" в диалоге с Логаном
});
JavaScript:
mp.events.add("lumberJackStatus", (status) => { // прилетает с сервера status = true или false
if (status) { // если статус true
browser.execute(`windows.lumberjack.lumberjackStatus = true`); // прячем кнопку "Принять работу" и показываем "Уволиться"
setTimeout(() => {
mp.keys.bind(0x45, true, useHatch); // биндим кнопку Е, для рубки дерева (не придумал ничего лучше, как биндить кнопку после 5 секунд, чтобы успеть отойти от npc, т.к при выходе из колшейпа, отвязывается кнопка Е, может кто подскажет решение получше :D
}, 5000);
} else { // если статус false
browser.execute(`windows.lumberjack.lumberjackStatus = false`); // прячем кнопку "Уволиться" и показываем "Принять работу"
mp.keys.unbind(0x45, true, useHatch); // отвязываем кнопку Е
}
});
JavaScript:
mp.events.add("render", () => {
if (mp.players.local.getVariable("lumberjack")) {
const startPosition = mp.players.local.getBoneCoords(12844, 0, 0, 0); // используем голову персонажа как начало "отрезка" поиска
const endPosition = mp.players.local.getBoneCoords(12844, 0, 1, 0); // конец отрезка примерно на 1 метр, от головы персонажа
const hitData = mp.raycasting.testCapsule( // "рисуем" невидимый отрезок
startPosition,
endPosition,
0.5,
mp.players.local
);
if (hitData && hitData.entity.type == "object") { // определяем, что отрезок попал на объект
if (hitData.entity.getVariable("treeId")) { // определяем по заданному ранее вариаблу, что у объекта есть treeId, а значит это дерево
mp.players.local.hatchReady = true; // задаём персонажу свойство, обозначающее, что он готов рубить дерево
mp.players.local.treeFor = hitData.entity.getVariable("treeId"); // сохраняем id дерева, для его поиска на сервере
}
} else {
mp.players.local.hatchReady = false; // если луч не коснулся объекта
}
}
});
Теперь пропишем функцию useHatch, которую мы биндили ранее:
JavaScript:
function useHatch() {
if (mp.players.local.hatchReady && mp.players.local.treeFor) { // проверяем свойства, прописанные ранее
mp.events.callRemote("server:startHatching", mp.players.local.treeFor); // вызываем событие на сервере для рубки, вместе с id дерева
}
}
JavaScript:
mp.events.add("server:startHatching", (player, id) => {
if (!player.worker) return player.notify("Вы не устроились на работу"); // проверка на устроился ли персонаж
if (!id) return; // ещё одна проверка, вдруг id затерялся
if (!player.hasHatchet) return player.notify("У вас нет топора"); // проверка на куплен ли топор, вернёмся к этому позже
player.playAnimation( // проигрывание анимации(да, я использовал анимацию ножа.., но самый терпеливый найдёт анимацию топора)
"melee@knife@streamed_core",
"knife_short_range_0",
1,
49
);
player.call("animActive", [true]); // вызываем событие, которое блокирует движение во время анимации
setTimeout(() => { // таймаут на 3 с, после которого выполнятся команды ниже
if (player) { // если игрок всё ещё на сервере, защита от вылета краша сервера
player.logs += 1; // засчитываем + 1 бревно персонажу
player.outputChatBox(`У вас ${player.logs} брёвен`); // вывод в чат кол-во брёвен
player.stopAnimation(); // остановка анимации
player.call("animActive", [false]); // вызываем событие, которое разрешает движение персонажу
}
mp.objects.forEach((object) => { // проходимся по массиву с созданными объектами
if (object.getVariable("treeId") == id) { // ищем по вариабле, установленной ранее, объект с id, который прилетел с клиента
object.treesLabel.destroy(); // уничтожаем надпись
object.destroy(); // уничтожаем сам объект(дерево)
}
});
}, 3000);
setTimeout(() => { // таймаут на повторную "посадку" дерева
const fi = allTrees.findIndex((tree) => tree.id == id); // ищем индекс дерева в массиве, по полученному id
const tr = mp.objects.new(
"prop_tree_fallen_pine_01",
new mp.Vector3(allTrees[fi].x, allTrees[fi].y, allTrees[fi].z - 1), // создаём дерево из массива объектов, используя найденный индекс выше
{
dimension: 0,
}
);
tr.treeId = allTrees[fi].id; // всё тоже самое, как и при packagesLoaded
tr.setVariable("treeId", tr.treeId);
tr.treesLabel = mp.labels.new(
"Нажмите [Е]",
new mp.Vector3(allTrees[fi].x, allTrees[fi].y + 1.1, allTrees[fi].z),
{
los: false,
font: 0,
drawDistance: 20,
color: [70, 130, 180, 170],
dimension: 0,
}
);
}, 20000);
});
JavaScript:
mp.events.add("animActive", (data) => {
if (data) {
mp.players.local.freezePosition(true);
} else {
mp.players.local.freezePosition(false);
}
});
Осталось лишь купить топор, и можно даже поработать! Для этого вернёмся на клиент и пропишем событие buyHatchet для созданной ранее кнопки
JavaScript:
mp.events.add("buyHatchet", () => {
mp.events.callRemote("server:buyHatchet");
});
но для начала пропишем фиксированную сумму за топор
JavaScript:
const hatchetPrice = 300;
JavaScript:
mp.events.add("server:buyHatchet", (player) => {
if (player.hasHatchet) return player.notify("У вас уже есть топор"); // проверка на купленный топор
if (player.money < hatchetPrice) // проверка на требуемое кол-во денег у персонажа
return player.notify("У вас не хватает денег для покупки");
player.giveWeapon(mp.joaat("weapon_hatchet"), 0); // выдача топора
player.money -= hatchetPrice; // вычет суммы из денег персонажа за топор
player.hasHatchet = true; // Свойство, определяющее наличие топора у персонажа
player.notify("Вы купили топор"); // уведомление
});
JavaScript:
mp.events.add("playerReady", (player) => {
player.logs = 0;
player.money = 300;
});
Для этого мы возвращаемся на клиент и прописываем там событие sellLogs:
JavaScript:
mp.events.add("sellLogs", () => {
mp.events.callRemote("server:sellLogs");
});
JavaScript:
mp.events.add("server:sellLogs", (player) => {
if (player.logs <= 0) return player.notify("У вас нет бревён для продажи"); // проверка на брёвна
const sum = player.logs * 300; // рассчитавыем сумму, которую дадим за n-ое кол-во брёвен
player.money += sum; // плюсуем полученную сумму к уже имеющимся деньгам
player.outputChatBox(
`Вы продали ${player.logs} брёвен за ${sum}$. У вас ${player.money}$` // выводим сообщение в чат
);
player.logs = 0; // очищаем кол-во брёвен, ведь мы их продали
});
Для этого опять возвращаемся на клиент и прописываем событие stopLumberjack:
JavaScript:
mp.events.add("stopLumberjack", () => {
mp.events.callRemote("server:stopLumberjack");
});
Сервер:
JavaScript:
mp.events.add("server:stopLumberjack", (player) => {
if (player.worker) { // проверка на устройство
player.worker = false; // свойства устройства на работу переходит в состояние false, что позволит ещё раз принять работу
player.notify("Вы уволились"); // уведомление
player.call("lumberJackStatus", [false]); // прячем, показываем кнопки
} else {
return player.notify("Вы не начали работу лесоруба"); // на всякий случай, если что-то пойдёт не так
}
});
Работа готова, вроде всё на месте
Спасибо за внимание!
JavaScript:
let lumberjackOpened = false;
const lumberjackNpc = [
{
id: 0,
x: 18.32457160949707,
y: 26.563514709472656,
z: 70.61154174804688,
heading: "159.72958374023438",
name: "Логан",
model: "a_m_m_salton_02",
},
];
lumberjackNpc.forEach((lumberjack) => {
const lumberjackBot = mp.peds.new(
mp.game.joaat(lumberjack.model),
new mp.Vector3(lumberjack.x, lumberjack.y, lumberjack.z),
lumberjack.heading,
0
);
const lumberjackStartColshape = mp.colshapes.newSphere(
parseFloat(lumberjack.x),
parseFloat(lumberjack.y),
parseFloat(lumberjack.z),
2,
0
);
lumberjackStartColshape.class = "lumberjack";
mp.labels.new(
lumberjack.name,
{
x: lumberjackBot.position.x,
y: lumberjackBot.position.y,
z: lumberjackBot.position.z + 0.9,
},
{ los: true, font: 0, drawDistance: 20, color: [70, 130, 180, 170] }
);
mp.labels.new(
"[Работа: Лесоруб]",
{
x: lumberjackBot.position.x,
y: lumberjackBot.position.y,
z: lumberjackBot.position.z + 1.2,
},
{ los: true, font: 0, drawDistance: 20, color: [255, 255, 255, 170] }
);
const lumberjackBlip = mp.blips.new(
792,
new mp.Vector3(lumberjackBot.position.x, lumberjackBot.position.y, 0),
{
name: "Лесоруб",
scale: 0.7,
color: 5,
shortRange: true,
dimension: 0,
}
);
});
mp.events.add("sellLogs", () => {
mp.events.callRemote("server:sellLogs");
});
mp.events.add("startLumberjack", () => {
mp.events.callRemote("server:startLumberjack");
});
mp.events.add("stopLumberjack", () => {
mp.events.callRemote("server:stopLumberjack");
});
mp.events.add("buyHatchet", () => {
mp.events.callRemote("server:buyHatchet");
});
mp.events.add("closeLumberjack", () => {
closeLumberjack();
});
mp.events.add("playerEnterColshape", (shape) => {
if (shape.class == "lumberjack") {
mp.keys.bind(0x45, true, openLumberjack);
}
});
mp.events.add("playerExitColshape", (shape) => {
if (shape.class == "lumberjack") {
mp.keys.unbind(0x45, true, openLumberjack);
}
});
mp.events.add("lumberJackStatus", (status) => {
if (status) {
browser.execute(`windows.lumberjack.lumberjackStatus = true`);
setTimeout(() => {
mp.keys.bind(0x45, true, useHatch);
}, 5000);
} else {
browser.execute(`windows.lumberjack.lumberjackStatus = false`);
mp.keys.unbind(0x45, true, useHatch);
}
});
mp.events.add("animActive", (data) => {
if (data) {
mp.players.local.freezePosition(true);
} else {
mp.players.local.freezePosition(false);
}
});
mp.events.add("render", () => {
if (mp.players.local.getVariable("lumberjack")) {
const startPosition = mp.players.local.getBoneCoords(12844, 0, 0, 0);
const endPosition = mp.players.local.getBoneCoords(12844, 0, 1, 0);
const hitData = mp.raycasting.testCapsule(
startPosition,
endPosition,
0.5,
mp.players.local
);
if (hitData && hitData.entity.type == "object") {
if (hitData.entity.getVariable("treeId")) {
mp.players.local.hatchReady = true;
mp.players.local.treeFor = hitData.entity.getVariable("treeId");
}
} else {
mp.players.local.hatchReady = false;
}
}
});
function closeLumberjack() {
lumberjackOpened = false;
mp.gui.cursor.show(false, false);
mp.players.local.freezePosition(false);
}
function openLumberjack() {
if (lumberjackOpened) return;
browser.execute("windows.lumberjack.lumberjackOpened = true");
mp.gui.cursor.show(true, true);
lumberjackOpened = true;
mp.players.local.freezePosition(true);
}
function useHatch() {
if (mp.players.local.hatchReady && mp.players.local.treeFor) {
mp.events.callRemote("server:startHatching", mp.players.local.treeFor);
}
}
JavaScript:
const hatchetPrice = 300;
const allTrees = [
{
id: 1,
x: 8.031051635742188,
y: 10.031051635742188,
z: 70.78174591064453,
},
{
id: 2,
x: 48.031051635742188,
y: 10.031051635742188,
z: 70.78174591064453,
},
{
id: 3,
x: 38.031051635742188,
y: 10.031051635742188,
z: 70.78174591064453,
},
{
id: 4,
x: 28.031051635742188,
y: 10.031051635742188,
z: 70.78174591064453,
},
{
id: 5,
x: 18.031051635742188,
y: 10.031051635742188,
z: 70.78174591064453,
},
];
mp.events.add("packagesLoaded", () => {
console.log(`[INFO] Работа дровосека загружена`);
allTrees.forEach((tree) => {
const trees = mp.objects.new(
"prop_tree_fallen_pine_01",
new mp.Vector3(tree.x, tree.y, tree.z - 1),
{
dimension: 0,
}
);
trees.treeId = tree.id;
trees.setVariable("treeId", trees.treeId);
trees.treesLabel = mp.labels.new(
"Нажмите [Е]",
new mp.Vector3(tree.x, tree.y + 1.1, tree.z),
{
los: false,
font: 0,
drawDistance: 20,
color: [70, 130, 180, 170],
dimension: 0,
}
);
});
console.log(`[INFO] Деревьев посажено: ${allTrees.length}`);
});
mp.events.add("playerReady", (player) => {
player.logs = 0;
player.money = 300;
});
mp.events.add("server:buyHatchet", (player) => {
if (player.hasHatchet) return player.notify("У вас уже есть топор");
if (player.money < hatchetPrice)
return player.notify("У вас не хватает денег для покупки");
player.giveWeapon(mp.joaat("weapon_hatchet"), 0);
player.money -= hatchetPrice;
player.hasHatchet = true;
player.notify("Вы купили топор");
});
mp.events.add("server:sellLogs", (player) => {
if (player.logs <= 0) return player.notify("У вас нет бревён для продажи");
const sum = player.logs * 300;
player.money += sum;
player.outputChatBox(
`Вы продали ${player.logs} брёвен за ${sum}$. У вас ${player.money}$`
);
player.logs = 0;
});
mp.events.add("server:startHatching", (player, id) => {
if (!player.worker) return player.notify("Вы не устроились на работу");
if (!id) return;
if (!player.hasHatchet) return player.notify("У вас нет топора");
player.playAnimation(
"melee@knife@streamed_core",
"knife_short_range_0",
1,
49
);
player.call("animActive", [true]);
setTimeout(() => {
if (player) {
player.logs += 1;
player.outputChatBox(`У вас ${player.logs} брёвен`);
player.stopAnimation();
player.call("animActive", [false]);
}
mp.objects.forEach((object) => {
if (object.getVariable("treeId") == id) {
object.treesLabel.destroy();
object.destroy();
}
});
}, 3000);
setTimeout(() => {
const fi = allTrees.findIndex((tree) => tree.id == id);
const tr = mp.objects.new(
"prop_tree_fallen_pine_01",
new mp.Vector3(allTrees[fi].x, allTrees[fi].y, allTrees[fi].z - 1),
{
dimension: 0,
}
);
tr.treeId = allTrees[fi].id;
tr.setVariable("treeId", tr.treeId);
tr.treesLabel = mp.labels.new(
"Нажмите [Е]",
new mp.Vector3(allTrees[fi].x, allTrees[fi].y + 1.1, allTrees[fi].z),
{
los: false,
font: 0,
drawDistance: 20,
color: [70, 130, 180, 170],
dimension: 0,
}
);
}, 20000);
});
mp.events.add("server:startLumberjack", (player) => {
if (player.worker) return player.notify("Вы уже начали работу лесоруба");
player.worker = true;
player.notify("Вы устроились лесорубом");
player.setVariable("lumberjack", true);
player.call("lumberJackStatus", [true]);
});
mp.events.add("server:stopLumberjack", (player) => {
if (player.worker) {
player.worker = false;
player.notify("Вы уволились");
player.call("lumberJackStatus", [false]);
} else {
return player.notify("Вы не начали работу лесоруба");
}
});