· 7 years ago · Jan 31, 2019, 09:44 PM
1#define FILTERSCRIPT
2#include <a_samp>
3#include <evf> // by Emmet_ - http://forum.sa-mp.com/showthread.php?t=486060
4#include <izcmd> // by Yashas - http://forum.sa-mp.com/showthread.php?t=576114
5#include <progress2> // by [HLF]Southclaw - http://forum.sa-mp.com/showthread.php?t=537468
6#include <sqlitei> // by Slice - http://forum.sa-mp.com/showthread.php?t=303682
7#include <sscanf2> // by Y_Less - http://forum.sa-mp.com/showthread.php?t=602923
8#include <streamer> // by Incognito - http://forum.sa-mp.com/showthread.php?t=102865
9#include <YSI\y_iterate> // by Y_Less - http://forum.sa-mp.com/showthread.php?t=570884
10
11#define MAX_TREES (100) // tree limit
12#define MAX_LOGS (300) // dropped log limit
13#define MAX_BUYERS (20) // log buyer limit
14
15#define CUTTING_TIME (8) // required seconds to cut a tree down (Default: 8)
16#define LOG_LIMIT (10) // how many logs a player can load to a bobcat (if you change this, don't forget to modify LogAttachOffsets array) (Default: 10)
17#define ATTACH_INDEX (7) // for setplayerattachedobject (Default: 7)
18#define TREE_RESPAWN (300) // required seconds to respawn a tree (Default: 300)
19#define LOG_LIFETIME (120) // life time of a dropped log, in seconds (Default: 120)
20#define LOG_PRICE (50) // price of a log (Default: 50)
21#define CSAW_PRICE (500) // price of a chainsaw (Default: 500)
22
23enum E_TREE
24{
25 // loaded from db
26 Float: treeX,
27 Float: treeY,
28 Float: treeZ,
29 Float: treeRX,
30 Float: treeRY,
31 Float: treeRZ,
32 // temp
33 treeLogs,
34 treeSeconds,
35 bool: treeGettingCut,
36 treeObjID,
37 Text3D: treeLabel,
38 treeTimer
39}
40
41enum E_LOG
42{
43 // temp
44 logDroppedBy[MAX_PLAYER_NAME],
45 logSeconds,
46 logObjID,
47 logTimer,
48 Text3D: logLabel
49}
50
51enum E_BUYER
52{
53 // loaded from db
54 buyerSkin,
55 Float: buyerX,
56 Float: buyerY,
57 Float: buyerZ,
58 Float: buyerA,
59 // temp
60 buyerActorID,
61 Text3D: buyerLabel
62}
63
64new
65 TreeData[MAX_TREES][E_TREE],
66 LogData[MAX_LOGS][E_LOG],
67 BuyerData[MAX_BUYERS][E_BUYER];
68
69new
70 Iterator: Trees<MAX_TREES>,
71 Iterator: Logs<MAX_LOGS>,
72 Iterator: Buyers<MAX_BUYERS>;
73
74new
75 LogObjects[MAX_VEHICLES][LOG_LIMIT];
76
77new
78 CuttingTreeID[MAX_PLAYERS] = {-1, ...},
79 CuttingTimer[MAX_PLAYERS] = {-1, ...},
80 PlayerBar: CuttingBar[MAX_PLAYERS] = {INVALID_PLAYER_BAR_ID, ...},
81 bool: CarryingLog[MAX_PLAYERS],
82 EditingTreeID[MAX_PLAYERS] = {-1, ...};
83
84new
85 Float: LogAttachOffsets[LOG_LIMIT][4] = {
86 {-0.223, -1.089, -0.230, -90.399},
87 {-0.056, -1.091, -0.230, 90.399},
88 {0.116, -1.092, -0.230, -90.399},
89 {0.293, -1.088, -0.230, 90.399},
90 {-0.123, -1.089, -0.099, -90.399},
91 {0.043, -1.090, -0.099, 90.399},
92 {0.216, -1.092, -0.099, -90.399},
93 {-0.033, -1.090, 0.029, -90.399},
94 {0.153, -1.089, 0.029, 90.399},
95 {0.066, -1.091, 0.150, -90.399}
96 };
97
98new
99 DB: ScriptDBHandle;
100
101new
102 DBStatement: LoadTrees,
103 DBStatement: AddTree,
104 DBStatement: UpdateTree,
105 DBStatement: RemoveTree;
106
107new
108 DBStatement: LoadBuyers,
109 DBStatement: AddBuyer,
110 DBStatement: UpdateBuyer,
111 DBStatement: RemoveBuyer;
112
113SetPlayerLookAt(playerid, Float:x, Float:y)
114{
115 // somewhere on samp forums, couldn't find the source
116 new Float:Px, Float:Py, Float: Pa;
117 GetPlayerPos(playerid, Px, Py, Pa);
118 Pa = floatabs(atan((y-Py)/(x-Px)));
119 if (x <= Px && y >= Py) Pa = floatsub(180, Pa);
120 else if (x < Px && y < Py) Pa = floatadd(Pa, 180);
121 else if (x >= Px && y <= Py) Pa = floatsub(360.0, Pa);
122 Pa = floatsub(Pa, 90.0);
123 if (Pa >= 360.0) Pa = floatsub(Pa, 360.0);
124 SetPlayerFacingAngle(playerid, Pa);
125}
126
127ConvertToMinutes(time)
128{
129 // http://forum.sa-mp.com/showpost.php?p=3223897&postcount=11
130 new string[15];//-2000000000:00 could happen, so make the string 15 chars to avoid any errors
131 format(string, sizeof(string), "%02d:%02d", time / 60, time % 60);
132 return string;
133}
134
135GetClosestTree(playerid, Float: range = 2.0)
136{
137 new id = -1, Float: dist = range, Float: tempdist;
138 foreach(new i : Trees)
139 {
140 tempdist = GetPlayerDistanceFromPoint(playerid, TreeData[i][treeX], TreeData[i][treeY], TreeData[i][treeZ]);
141
142 if(tempdist > range) continue;
143 if(tempdist <= dist)
144 {
145 dist = tempdist;
146 id = i;
147 }
148 }
149
150 return id;
151}
152
153GetClosestLog(playerid, Float: range = 2.0)
154{
155 new id = -1, Float: dist = range, Float: tempdist, Float: pos[3];
156 foreach(new i : Logs)
157 {
158 GetDynamicObjectPos(LogData[i][logObjID], pos[0], pos[1], pos[2]);
159 tempdist = GetPlayerDistanceFromPoint(playerid, pos[0], pos[1], pos[2]);
160
161 if(tempdist > range) continue;
162 if(tempdist <= dist)
163 {
164 dist = tempdist;
165 id = i;
166 }
167 }
168
169 return id;
170}
171
172IsPlayerNearALogBuyer(playerid)
173{
174 foreach(new i : Buyers)
175 {
176 if(IsPlayerInRangeOfPoint(playerid, 2.0, BuyerData[i][buyerX], BuyerData[i][buyerY], BuyerData[i][buyerZ])) return 1;
177 }
178
179 return 0;
180}
181
182Player_Init(playerid)
183{
184 CuttingTreeID[playerid] = -1;
185 CuttingTimer[playerid] = -1;
186 CarryingLog[playerid] = false;
187 EditingTreeID[playerid] = -1;
188
189 CuttingBar[playerid] = CreatePlayerProgressBar(playerid, 498.0, 104.0, 113.0, 6.2, 0x61381BFF, CUTTING_TIME, 0);
190 ApplyAnimation(playerid, "CHAINSAW", "null", 0.0, 0, 0, 0, 0, 0, 0);
191 ApplyAnimation(playerid, "CARRY", "null", 0.0, 0, 0, 0, 0, 0, 0);
192 return 1;
193}
194
195Player_ResetCutting(playerid)
196{
197 if(!IsPlayerConnected(playerid) || CuttingTreeID[playerid] == -1) return 0;
198 new id = CuttingTreeID[playerid];
199 TreeData[id][treeGettingCut] = false;
200 if(TreeData[id][treeSeconds] < 1) Streamer_SetIntData(STREAMER_TYPE_3D_TEXT_LABEL, TreeData[id][treeLabel], E_STREAMER_COLOR, 0x2ECC71FF);
201
202 ClearAnimations(playerid);
203 TogglePlayerControllable(playerid, 1);
204 CuttingTreeID[playerid] = -1;
205
206 if(CuttingTimer[playerid] != -1)
207 {
208 KillTimer(CuttingTimer[playerid]);
209 CuttingTimer[playerid] = -1;
210 }
211
212 SetPlayerProgressBarValue(playerid, CuttingBar[playerid], 0.0);
213 HidePlayerProgressBar(playerid, CuttingBar[playerid]);
214 return 1;
215}
216
217Player_GiveLog(playerid)
218{
219 if(!IsPlayerConnected(playerid)) return 0;
220 CarryingLog[playerid] = true;
221 SetPlayerSpecialAction(playerid, SPECIAL_ACTION_CARRY);
222 SetPlayerAttachedObject(playerid, ATTACH_INDEX, 19793, 6, 0.077999, 0.043999, -0.170999, -13.799953, 79.70, 0.0);
223
224 SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}You can press {F1C40F}~k~~CONVERSATION_NO~ {FFFFFF}to drop your log.");
225 return 1;
226}
227
228Player_DropLog(playerid, death_drop = 0)
229{
230 if(!IsPlayerConnected(playerid) || !CarryingLog[playerid]) return 0;
231 new id = Iter_Free(Logs);
232 if(id != -1)
233 {
234 new Float: x, Float: y, Float: z, Float: a, label[128];
235 GetPlayerPos(playerid, x, y, z);
236 GetPlayerFacingAngle(playerid, a);
237 GetPlayerName(playerid, LogData[id][logDroppedBy], MAX_PLAYER_NAME);
238
239 if(!death_drop)
240 {
241 x += (1.0 * floatsin(-a, degrees));
242 y += (1.0 * floatcos(-a, degrees));
243
244 ApplyAnimation(playerid, "CARRY", "putdwn05", 4.1, 0, 1, 1, 0, 0, 1);
245 }
246
247 LogData[id][logSeconds] = LOG_LIFETIME;
248 LogData[id][logObjID] = CreateDynamicObject(19793, x, y, z - 0.9, 0.0, 0.0, a);
249
250 format(label, sizeof(label), "Log (%d)\n\n{FFFFFF}Dropped By {F1C40F}%s\n{FFFFFF}%s\nUse {F1C40F}/log take {FFFFFF}to take it.", id, LogData[id][logDroppedBy], ConvertToMinutes(LOG_LIFETIME));
251 LogData[id][logLabel] = CreateDynamic3DTextLabel(label, 0xF1C40FFF, x, y, z - 0.7, 5.0, .testlos = 1);
252
253 LogData[id][logTimer] = SetTimerEx("RemoveLog", 1000, true, "i", id);
254 Iter_Add(Logs, id);
255 }
256
257 Player_RemoveLog(playerid);
258 return 1;
259}
260
261Player_RemoveLog(playerid)
262{
263 if(!IsPlayerConnected(playerid) || !CarryingLog[playerid]) return 0;
264 RemovePlayerAttachedObject(playerid, ATTACH_INDEX);
265 SetPlayerSpecialAction(playerid, SPECIAL_ACTION_NONE);
266 CarryingLog[playerid] = false;
267 return 1;
268}
269
270Vehicle_LogCount(vehicleid)
271{
272 if(GetVehicleModel(vehicleid) == 0) return 0;
273 new count;
274 for(new i; i < LOG_LIMIT; i++) if(IsValidDynamicObject(LogObjects[vehicleid][i])) count++;
275 return count;
276}
277
278Vehicle_RemoveLogs(vehicleid)
279{
280 if(GetVehicleModel(vehicleid) == 0) return 0;
281 for(new i; i < LOG_LIMIT; i++)
282 {
283 if(IsValidDynamicObject(LogObjects[vehicleid][i]))
284 {
285 DestroyDynamicObject(LogObjects[vehicleid][i]);
286 LogObjects[vehicleid][i] = -1;
287 }
288 }
289
290 return 1;
291}
292
293Tree_BeingEdited(id)
294{
295 if(!Iter_Contains(Trees, id)) return 0;
296 foreach(new i : Player) if(EditingTreeID[i] == id) return 1;
297 return 0;
298}
299
300Tree_UpdateLogLabel(id)
301{
302 if(!Iter_Contains(Trees, id)) return 0;
303 new label[96];
304
305 if(TreeData[id][treeLogs] > 0) {
306 format(label, sizeof(label), "Tree (%d)\n\n{FFFFFF}Logs: {F1C40F}%d\n{FFFFFF}Use {F1C40F}/log takefromtree {FFFFFF}to take a log.", id, TreeData[id][treeLogs]);
307 UpdateDynamic3DTextLabelText(TreeData[id][treeLabel], 0xE74C3CFF, label);
308 }else{
309 TreeData[id][treeTimer] = SetTimerEx("RespawnTree", 1000, true, "i", id);
310
311 format(label, sizeof(label), "Tree (%d)\n\n{FFFFFF}%s", id, ConvertToMinutes(TreeData[id][treeSeconds]));
312 UpdateDynamic3DTextLabelText(TreeData[id][treeLabel], 0xE74C3CFF, label);
313 }
314
315 return 1;
316}
317
318public OnFilterScriptInit()
319{
320 print(" [Lumberjack] Initializing...");
321
322 // assign default values to variables
323 for(new i; i < MAX_TREES; i++)
324 {
325 TreeData[i][treeObjID] = TreeData[i][treeTimer] = -1;
326 TreeData[i][treeLabel] = Text3D: -1;
327 }
328
329 for(new i; i < MAX_LOGS; i++)
330 {
331 LogData[i][logObjID] = LogData[i][logTimer] = -1;
332 LogData[i][logLabel] = Text3D: -1;
333 }
334
335 for(new i; i < MAX_BUYERS; i++)
336 {
337 BuyerData[i][buyerActorID] = -1;
338 BuyerData[i][buyerLabel] = Text3D: -1;
339 }
340
341 for(new i; i < MAX_VEHICLES; i++) for(new x; x < LOG_LIMIT; x++) LogObjects[i][x] = -1;
342
343 foreach(new i : Player) Player_Init(i);
344
345 // open database & create tables
346 ScriptDBHandle = db_open("lumberjack.db");
347 db_query(ScriptDBHandle, "CREATE TABLE IF NOT EXISTS trees (ID INTEGER, PosX FLOAT, PosY FLOAT, PosZ FLOAT, RotX FLOAT, RotY FLOAT, RotZ FLOAT)");
348 db_query(ScriptDBHandle, "CREATE TABLE IF NOT EXISTS buyers (ID INTEGER, Skin INTEGER, PosX FLOAT, PosY FLOAT, PosZ FLOAT, PosA FLOAT)");
349
350 // prepare tree queries
351 LoadTrees = db_prepare(ScriptDBHandle, "SELECT * FROM trees");
352 AddTree = db_prepare(ScriptDBHandle, "INSERT INTO trees (ID, PosX, PosY, PosZ, RotX, RotY, RotZ) VALUES (?, ?, ?, ?, ?, ?, ?)");
353 UpdateTree = db_prepare(ScriptDBHandle, "UPDATE trees SET PosX=?, PosY=?, PosZ=?, RotX=?, RotY=?, RotZ=? WHERE ID=?");
354 RemoveTree = db_prepare(ScriptDBHandle, "DELETE FROM trees WHERE ID=?");
355
356 // prepare buyer queries
357 LoadBuyers = db_prepare(ScriptDBHandle, "SELECT * FROM buyers");
358 AddBuyer = db_prepare(ScriptDBHandle, "INSERT INTO buyers (ID, Skin, PosX, PosY, PosZ, PosA) VALUES (?, ?, ?, ?, ?, ?)");
359 UpdateBuyer = db_prepare(ScriptDBHandle, "UPDATE buyers SET Skin=?, PosX=?, PosY=?, PosZ=?, PosA=? WHERE ID=?");
360 RemoveBuyer = db_prepare(ScriptDBHandle, "DELETE FROM buyers WHERE ID=?");
361
362 // load trees
363 new id, Float: pos[3], Float: rot[3];
364 stmt_bind_result_field(LoadTrees, 0, DB::TYPE_INTEGER, id);
365 stmt_bind_result_field(LoadTrees, 1, DB::TYPE_FLOAT, pos[0]);
366 stmt_bind_result_field(LoadTrees, 2, DB::TYPE_FLOAT, pos[1]);
367 stmt_bind_result_field(LoadTrees, 3, DB::TYPE_FLOAT, pos[2]);
368 stmt_bind_result_field(LoadTrees, 4, DB::TYPE_FLOAT, rot[0]);
369 stmt_bind_result_field(LoadTrees, 5, DB::TYPE_FLOAT, rot[1]);
370 stmt_bind_result_field(LoadTrees, 6, DB::TYPE_FLOAT, rot[2]);
371
372 if(stmt_execute(LoadTrees))
373 {
374 print(" [Lumberjack] Loading trees...");
375
376 new label[96];
377 while(stmt_fetch_row(LoadTrees))
378 {
379 TreeData[id][treeX] = pos[0];
380 TreeData[id][treeY] = pos[1];
381 TreeData[id][treeZ] = pos[2];
382 TreeData[id][treeRX] = rot[0];
383 TreeData[id][treeRY] = rot[1];
384 TreeData[id][treeRZ] = rot[2];
385
386 TreeData[id][treeObjID] = CreateDynamicObject(657, TreeData[id][treeX], TreeData[id][treeY], TreeData[id][treeZ], TreeData[id][treeRX], TreeData[id][treeRY], TreeData[id][treeRZ]);
387
388 format(label, sizeof(label), "Tree (%d)\n\n{FFFFFF}Press {F1C40F}~k~~CONVERSATION_NO~ {FFFFFF}to cut down.", id);
389 TreeData[id][treeLabel] = CreateDynamic3DTextLabel(label, 0x2ECC71FF, TreeData[id][treeX], TreeData[id][treeY], TreeData[id][treeZ] + 1.5, 5.0);
390
391 Iter_Add(Trees, id);
392 }
393
394 printf(" [Lumberjack] Loaded %d trees.", Iter_Count(Trees));
395 }
396
397 // load buyers
398 new skin, Float: bpos[4];
399 stmt_bind_result_field(LoadBuyers, 0, DB::TYPE_INTEGER, id);
400 stmt_bind_result_field(LoadBuyers, 1, DB::TYPE_INTEGER, skin);
401 stmt_bind_result_field(LoadBuyers, 2, DB::TYPE_FLOAT, bpos[0]);
402 stmt_bind_result_field(LoadBuyers, 3, DB::TYPE_FLOAT, bpos[1]);
403 stmt_bind_result_field(LoadBuyers, 4, DB::TYPE_FLOAT, bpos[2]);
404 stmt_bind_result_field(LoadBuyers, 5, DB::TYPE_FLOAT, bpos[3]);
405
406 if(stmt_execute(LoadBuyers))
407 {
408 print(" [Lumberjack] Loading buyers...");
409
410 new label[172];
411 while(stmt_fetch_row(LoadBuyers))
412 {
413 BuyerData[id][buyerSkin] = skin;
414 BuyerData[id][buyerX] = bpos[0];
415 BuyerData[id][buyerY] = bpos[1];
416 BuyerData[id][buyerZ] = bpos[2];
417 BuyerData[id][buyerA] = bpos[3];
418
419 BuyerData[id][buyerActorID] = CreateActor(BuyerData[id][buyerSkin], BuyerData[id][buyerX], BuyerData[id][buyerY], BuyerData[id][buyerZ], BuyerData[id][buyerA]);
420 SetActorInvulnerable(BuyerData[id][buyerActorID], 1);
421
422 format(label, sizeof(label), "Log Buyer (%d)\n\n{FFFFFF}Use {F1C40F}/chainsaw {FFFFFF}to buy a chainsaw for {2ECC71}$%d.\n{FFFFFF}Use {F1C40F}/log sell {FFFFFF}to sell a log for {2ECC71}$%d.", id, CSAW_PRICE, LOG_PRICE);
423 BuyerData[id][buyerLabel] = CreateDynamic3DTextLabel(label, 0xF1C40FFF, BuyerData[id][buyerX], BuyerData[id][buyerY], BuyerData[id][buyerZ] + 0.25, 5.0, .testlos = 1);
424
425 Iter_Add(Buyers, id);
426 }
427
428 printf(" [Lumberjack] Loaded %d buyers.", Iter_Count(Buyers));
429 }
430
431 return 1;
432}
433
434public OnFilterScriptExit()
435{
436 foreach(new i : Player)
437 {
438 Player_ResetCutting(i);
439 Player_RemoveLog(i);
440
441 DestroyPlayerProgressBar(i, CuttingBar[i]);
442 }
443
444 foreach(new i : Buyers) DestroyActor(BuyerData[i][buyerActorID]);
445
446 db_close(ScriptDBHandle);
447 print(" [Lumberjack] Unloaded.");
448 return 1;
449}
450
451public OnPlayerConnect(playerid)
452{
453 Player_Init(playerid);
454 return 1;
455}
456
457public OnPlayerDisconnect(playerid, reason)
458{
459 Player_ResetCutting(playerid);
460 Player_RemoveLog(playerid);
461
462 EditingTreeID[playerid] = -1;
463 return 1;
464}
465
466public OnVehicleSpawn(vehicleid)
467{
468 Vehicle_RemoveLogs(vehicleid);
469 return 1;
470}
471
472public OnVehicleDeath(vehicleid, killerid)
473{
474 Vehicle_RemoveLogs(vehicleid);
475 return 1;
476}
477
478public OnPlayerDeath(playerid, killerid, reason)
479{
480 Player_ResetCutting(playerid);
481 Player_DropLog(playerid, 1);
482 return 1;
483}
484
485public OnPlayerStateChange(playerid, newstate, oldstate)
486{
487 if(newstate != PLAYER_STATE_WASTED)
488 {
489 Player_ResetCutting(playerid);
490 Player_RemoveLog(playerid);
491 }
492
493 return 1;
494}
495
496public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
497{
498 if(GetPlayerState(playerid) == PLAYER_STATE_ONFOOT && (newkeys & KEY_NO))
499 {
500 if(CarryingLog[playerid]) return Player_DropLog(playerid);
501
502 if(GetPlayerWeapon(playerid) == WEAPON_CHAINSAW && CuttingTreeID[playerid] == -1 && !CarryingLog[playerid])
503 {
504 new id = GetClosestTree(playerid);
505
506 if(id != -1)
507 {
508 if(!Tree_BeingEdited(id) && !TreeData[id][treeGettingCut] && TreeData[id][treeSeconds] < 1)
509 {
510 SetPlayerLookAt(playerid, TreeData[id][treeX], TreeData[id][treeY]);
511
512 Streamer_SetIntData(STREAMER_TYPE_3D_TEXT_LABEL, TreeData[id][treeLabel], E_STREAMER_COLOR, 0xE74C3CFF);
513 CuttingTimer[playerid] = SetTimerEx("CutTree", 1000, true, "i", playerid);
514 CuttingTreeID[playerid] = id;
515 SetPlayerProgressBarValue(playerid, CuttingBar[playerid], 0.0);
516 ShowPlayerProgressBar(playerid, CuttingBar[playerid]);
517 TogglePlayerControllable(playerid, 0);
518 SetPlayerArmedWeapon(playerid, WEAPON_CHAINSAW);
519 ApplyAnimation(playerid, "CHAINSAW", "WEAPON_csaw", 4.1, 1, 0, 0, 1, 0, 1);
520
521 TreeData[id][treeGettingCut] = true;
522 }
523 }
524 }
525 }
526
527 return 1;
528}
529
530public OnPlayerEditDynamicObject(playerid, STREAMER_TAG_OBJECT objectid, response, Float:x, Float:y, Float:z, Float:rx, Float:ry, Float:rz)
531{
532 if(EditingTreeID[playerid] != -1 && Iter_Contains(Trees, EditingTreeID[playerid]))
533 {
534 if(response == EDIT_RESPONSE_FINAL)
535 {
536 new id = EditingTreeID[playerid];
537 TreeData[id][treeX] = x;
538 TreeData[id][treeY] = y;
539 TreeData[id][treeZ] = z;
540 TreeData[id][treeRX] = rx;
541 TreeData[id][treeRY] = ry;
542 TreeData[id][treeRZ] = rz;
543
544 SetDynamicObjectPos(objectid, TreeData[id][treeX], TreeData[id][treeY], TreeData[id][treeZ]);
545 SetDynamicObjectRot(objectid, TreeData[id][treeRX], TreeData[id][treeRY], TreeData[id][treeRZ]);
546
547 Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, TreeData[id][treeLabel], E_STREAMER_X, TreeData[id][treeX]);
548 Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, TreeData[id][treeLabel], E_STREAMER_Y, TreeData[id][treeY]);
549 Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, TreeData[id][treeLabel], E_STREAMER_Z, TreeData[id][treeZ] + 1.5);
550
551 stmt_bind_value(UpdateTree, 0, DB::TYPE_FLOAT, TreeData[id][treeX]);
552 stmt_bind_value(UpdateTree, 1, DB::TYPE_FLOAT, TreeData[id][treeY]);
553 stmt_bind_value(UpdateTree, 2, DB::TYPE_FLOAT, TreeData[id][treeZ]);
554 stmt_bind_value(UpdateTree, 3, DB::TYPE_FLOAT, TreeData[id][treeRX]);
555 stmt_bind_value(UpdateTree, 4, DB::TYPE_FLOAT, TreeData[id][treeRY]);
556 stmt_bind_value(UpdateTree, 5, DB::TYPE_FLOAT, TreeData[id][treeRZ]);
557 stmt_bind_value(UpdateTree, 6, DB::TYPE_INTEGER, id);
558 stmt_execute(UpdateTree);
559
560 EditingTreeID[playerid] = -1;
561 }
562
563 if(response == EDIT_RESPONSE_CANCEL)
564 {
565 new id = EditingTreeID[playerid];
566 SetDynamicObjectPos(objectid, TreeData[id][treeX], TreeData[id][treeY], TreeData[id][treeZ]);
567 SetDynamicObjectRot(objectid, TreeData[id][treeRX], TreeData[id][treeRY], TreeData[id][treeRZ]);
568 EditingTreeID[playerid] = -1;
569 }
570 }
571
572 return 1;
573}
574
575forward CutTree(playerid);
576public CutTree(playerid)
577{
578 if(CuttingTreeID[playerid] != -1)
579 {
580 new id = CuttingTreeID[playerid], Float: value = GetPlayerProgressBarValue(playerid, CuttingBar[playerid]) + 1.0;
581
582 if(value >= CUTTING_TIME) {
583 Player_ResetCutting(playerid);
584 MoveDynamicObject(TreeData[id][treeObjID], TreeData[id][treeX], TreeData[id][treeY], TreeData[id][treeZ] + 0.03, 0.025, TreeData[id][treeRX], TreeData[id][treeRY] - 80.0, TreeData[id][treeRZ]);
585
586 TreeData[id][treeLogs] = 5;
587 TreeData[id][treeSeconds] = TREE_RESPAWN;
588 Tree_UpdateLogLabel(id);
589 }else{
590 SetPlayerProgressBarValue(playerid, CuttingBar[playerid], value);
591 }
592 }
593
594 return 1;
595}
596
597forward RespawnTree(id);
598public RespawnTree(id)
599{
600 new label[96];
601 if(TreeData[id][treeSeconds] > 1) {
602 TreeData[id][treeSeconds]--;
603
604 format(label, sizeof(label), "Tree (%d)\n\n{FFFFFF}%s", id, ConvertToMinutes(TreeData[id][treeSeconds]));
605 UpdateDynamic3DTextLabelText(TreeData[id][treeLabel], 0xE74C3CFF, label);
606 }else if(TreeData[id][treeSeconds] == 1) {
607 KillTimer(TreeData[id][treeTimer]);
608
609 TreeData[id][treeLogs] = 0;
610 TreeData[id][treeSeconds] = 0;
611 TreeData[id][treeTimer] = -1;
612
613 SetDynamicObjectPos(TreeData[id][treeObjID], TreeData[id][treeX], TreeData[id][treeY], TreeData[id][treeZ]);
614 SetDynamicObjectRot(TreeData[id][treeObjID], TreeData[id][treeRX], TreeData[id][treeRY], TreeData[id][treeRZ]);
615
616 format(label, sizeof(label), "Tree (%d)\n\n{FFFFFF}Press {F1C40F}~k~~CONVERSATION_NO~ {FFFFFF}to cut down.", id);
617 UpdateDynamic3DTextLabelText(TreeData[id][treeLabel], 0x2ECC71FF, label);
618 }
619
620 return 1;
621}
622
623forward RemoveLog(id);
624public RemoveLog(id)
625{
626 if(!Iter_Count(Logs, id)) return 1;
627
628 if(LogData[id][logSeconds] > 1) {
629 LogData[id][logSeconds]--;
630
631 new label[128];
632 format(label, sizeof(label), "Log (%d)\n\n{FFFFFF}Dropped By {F1C40F}%s\n{FFFFFF}%s\nUse {F1C40F}/log take {FFFFFF}to take it.", id, LogData[id][logDroppedBy], ConvertToMinutes(LogData[id][logSeconds]));
633 UpdateDynamic3DTextLabelText(LogData[id][logLabel], 0xF1C40FFF, label);
634 }else if(LogData[id][logSeconds] == 1) {
635 KillTimer(LogData[id][logTimer]);
636 DestroyDynamicObject(LogData[id][logObjID]);
637 DestroyDynamic3DTextLabel(LogData[id][logLabel]);
638
639 LogData[id][logTimer] = -1;
640 LogData[id][logObjID] = -1;
641 LogData[id][logLabel] = Text3D: -1;
642
643 Iter_Remove(Logs, id);
644 }
645
646 return 1;
647}
648
649// Player Commands
650CMD:chainsaw(playerid, params[])
651{
652 if(IsPlayerInAnyVehicle(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't use this command in a vehicle.");
653 if(!IsPlayerNearALogBuyer(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near a Log Buyer.");
654 if(GetPlayerMoney(playerid) < CSAW_PRICE) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You don't have enough money.");
655 GivePlayerMoney(playerid, -CSAW_PRICE);
656 GivePlayerWeapon(playerid, WEAPON_CHAINSAW, 1);
657
658 new string[64];
659 format(string, sizeof(string), "LUMBERJACK: {FFFFFF}Bought a chainsaw for {2ECC71}$%d.", CSAW_PRICE);
660 SendClientMessage(playerid, 0x3498DBFF, string);
661 return 1;
662}
663
664CMD:log(playerid, params[])
665{
666 if(IsPlayerInAnyVehicle(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't use this command in a vehicle.");
667 if(isnull(params)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/log [load/take/takefromcar/takefromtree/sell]");
668
669 if(!strcmp(params, "load", true)) {
670 // loading to a bobcat
671 if(!CarryingLog[playerid]) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not carrying a log.");
672 new id = GetNearestVehicle(playerid);
673 if(GetVehicleModel(id) != 422) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near a Bobcat.");
674 new Float: x, Float: y, Float: z;
675 GetVehicleBoot(id, x, y, z);
676 if(!IsPlayerInRangeOfPoint(playerid, 3.0, x, y, z)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near a Bobcat's back.");
677 if(Vehicle_LogCount(id) >= LOG_LIMIT) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You can't load any more logs to this vehicle.");
678 for(new i; i < LOG_LIMIT; i++)
679 {
680 if(!IsValidDynamicObject(LogObjects[id][i]))
681 {
682 LogObjects[id][i] = CreateDynamicObject(19793, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
683 AttachDynamicObjectToVehicle(LogObjects[id][i], id, LogAttachOffsets[i][0], LogAttachOffsets[i][1], LogAttachOffsets[i][2], 0.0, 0.0, LogAttachOffsets[i][3]);
684 break;
685 }
686 }
687
688 Streamer_Update(playerid);
689 Player_RemoveLog(playerid);
690 SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}Loaded a log.");
691 // done
692 }else if(!strcmp(params, "take")) {
693 // taking from ground
694 if(CarryingLog[playerid]) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're already carrying a log.");
695 new id = GetClosestLog(playerid);
696 if(id == -1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near a log.");
697 LogData[id][logSeconds] = 1;
698 RemoveLog(id);
699
700 Player_GiveLog(playerid);
701 SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}You've taken a log from ground.");
702 // done
703 }else if(!strcmp(params, "takefromcar")) {
704 // taking from a bobcat
705 if(CarryingLog[playerid]) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're already carrying a log.");
706 new id = GetNearestVehicle(playerid);
707 if(GetVehicleModel(id) != 422) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near a Bobcat.");
708 new Float: x, Float: y, Float: z;
709 GetVehicleBoot(id, x, y, z);
710 if(!IsPlayerInRangeOfPoint(playerid, 3.0, x, y, z)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near a Bobcat's back.");
711 if(Vehicle_LogCount(id) < 1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}This Bobcat doesn't have any logs.");
712 for(new i = (LOG_LIMIT - 1); i >= 0; i--)
713 {
714 if(IsValidDynamicObject(LogObjects[id][i]))
715 {
716 DestroyDynamicObject(LogObjects[id][i]);
717 LogObjects[id][i] = -1;
718 break;
719 }
720 }
721
722 Streamer_Update(playerid);
723 Player_GiveLog(playerid);
724 SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}You've taken a log from the Bobcat.");
725 // done
726 }else if(!strcmp(params, "takefromtree")) {
727 // taking from a cut tree
728 if(CarryingLog[playerid]) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're already carrying a log.");
729 new id = GetClosestTree(playerid);
730 if(id == -1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near a tree.");
731 if(TreeData[id][treeSeconds] < 1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}This tree isn't cut.");
732 if(TreeData[id][treeLogs] < 1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}This tree doesn't have any logs.");
733 TreeData[id][treeLogs]--;
734 Tree_UpdateLogLabel(id);
735
736 Player_GiveLog(playerid);
737 SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}You've taken a log from the cut tree.");
738 // done
739 }else if(!strcmp(params, "sell")) {
740 // selling a log
741 if(!CarryingLog[playerid]) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not carrying a log.");
742 if(!IsPlayerNearALogBuyer(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near a Log Buyer.");
743 Player_RemoveLog(playerid);
744 GivePlayerMoney(playerid, LOG_PRICE);
745
746 new string[64];
747 format(string, sizeof(string), "LUMBERJACK: {FFFFFF}Sold a log for {2ECC71}$%d.", LOG_PRICE);
748 SendClientMessage(playerid, 0x3498DBFF, string);
749 // done
750 }
751
752 return 1;
753}
754
755// Admin Commands - Trees
756CMD:createtree(playerid, params[])
757{
758 if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
759 new id = Iter_Free(Trees);
760 if(id == -1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Can't add any more trees.");
761 new Float: x, Float: y, Float: z, Float: a;
762 GetPlayerPos(playerid, x, y, z);
763 GetPlayerFacingAngle(playerid, a);
764 x += (3.0 * floatsin(-a, degrees));
765 y += (3.0 * floatcos(-a, degrees));
766 z -= 1.0;
767
768 TreeData[id][treeX] = x;
769 TreeData[id][treeY] = y;
770 TreeData[id][treeZ] = z;
771 TreeData[id][treeRX] = TreeData[id][treeRY] = TreeData[id][treeRZ] = 0.0;
772
773 TreeData[id][treeObjID] = CreateDynamicObject(657, TreeData[id][treeX], TreeData[id][treeY], TreeData[id][treeZ], TreeData[id][treeRX], TreeData[id][treeRY], TreeData[id][treeRZ]);
774
775 new label[96];
776 format(label, sizeof(label), "Tree (%d)\n\n{FFFFFF}Press {F1C40F}~k~~CONVERSATION_NO~ {FFFFFF}to cut down.", id);
777 TreeData[id][treeLabel] = CreateDynamic3DTextLabel(label, 0x2ECC71FF, TreeData[id][treeX], TreeData[id][treeY], TreeData[id][treeZ] + 1.5, 5.0);
778 Iter_Add(Trees, id);
779
780 stmt_bind_value(AddTree, 0, DB::TYPE_INTEGER, id);
781 stmt_bind_value(AddTree, 1, DB::TYPE_FLOAT, TreeData[id][treeX]);
782 stmt_bind_value(AddTree, 2, DB::TYPE_FLOAT, TreeData[id][treeY]);
783 stmt_bind_value(AddTree, 3, DB::TYPE_FLOAT, TreeData[id][treeZ]);
784 stmt_bind_value(AddTree, 4, DB::TYPE_FLOAT, TreeData[id][treeRX]);
785 stmt_bind_value(AddTree, 5, DB::TYPE_FLOAT, TreeData[id][treeRY]);
786 stmt_bind_value(AddTree, 6, DB::TYPE_FLOAT, TreeData[id][treeRZ]);
787
788 if(stmt_execute(AddTree))
789 {
790 EditingTreeID[playerid] = id;
791 EditDynamicObject(playerid, TreeData[id][treeObjID]);
792
793 SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}Tree created.");
794 SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}You can edit it right now, or cancel editing and edit it some other time.");
795 }
796
797 return 1;
798}
799
800CMD:edittree(playerid, params[])
801{
802 if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
803 if(EditingTreeID[playerid] != -1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're already editing a tree.");
804 new id;
805 if(sscanf(params, "i", id)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/edittree [tree id]");
806 if(!Iter_Contains(Trees, id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid ID.");
807 if(TreeData[id][treeGettingCut]) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Can't edit specified tree because its getting cut down.");
808 if(!IsPlayerInRangeOfPoint(playerid, 30.0, TreeData[id][treeX], TreeData[id][treeY], TreeData[id][treeZ])) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}You're not near the tree you want to edit.");
809 EditingTreeID[playerid] = id;
810 EditDynamicObject(playerid, TreeData[id][treeObjID]);
811 return 1;
812}
813
814CMD:removetree(playerid, params[])
815{
816 if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
817 new id;
818 if(sscanf(params, "i", id)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/removetree [tree id]");
819 if(!Iter_Contains(Trees, id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid ID.");
820 if(TreeData[id][treeGettingCut]) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Can't remove specified tree because its getting cut down.");
821 if(Tree_BeingEdited(id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Can't remove specified tree because its being edited.");
822 DestroyDynamicObject(TreeData[id][treeObjID]);
823 DestroyDynamic3DTextLabel(TreeData[id][treeLabel]);
824 if(TreeData[id][treeTimer] != -1) KillTimer(TreeData[id][treeTimer]);
825
826 TreeData[id][treeLogs] = TreeData[id][treeSeconds] = 0;
827 TreeData[id][treeObjID] = TreeData[id][treeTimer] = -1;
828 TreeData[id][treeLabel] = Text3D: -1;
829 Iter_Remove(Trees, id);
830
831 stmt_bind_value(RemoveTree, 0, DB::TYPE_INTEGER, id);
832 if(stmt_execute(RemoveTree)) SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}Tree removed.");
833 return 1;
834}
835
836// Admin Commands - Log Buyers
837CMD:createbuyer(playerid, params[])
838{
839 if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
840 new skin;
841 if(sscanf(params, "i", skin)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/createbuyer [skin id]");
842 if(!(0 <= skin <= 311)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid skin ID.");
843 new id = Iter_Free(Buyers);
844 if(id == -1) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Can't add any more log buyers.");
845 GetPlayerPos(playerid, BuyerData[id][buyerX], BuyerData[id][buyerY], BuyerData[id][buyerZ]);
846 GetPlayerFacingAngle(playerid, BuyerData[id][buyerA]);
847
848 BuyerData[id][buyerActorID] = CreateActor(skin, BuyerData[id][buyerX], BuyerData[id][buyerY], BuyerData[id][buyerZ], BuyerData[id][buyerA]);
849 SetActorInvulnerable(BuyerData[id][buyerActorID], 1);
850
851 new label[172];
852 format(label, sizeof(label), "Log Buyer (%d)\n\n{FFFFFF}Use {F1C40F}/chainsaw {FFFFFF}to buy a chainsaw for {2ECC71}$%d.\n{FFFFFF}Use {F1C40F}/log sell {FFFFFF}to sell a log for {2ECC71}$%d.", id, CSAW_PRICE, LOG_PRICE);
853 BuyerData[id][buyerLabel] = CreateDynamic3DTextLabel(label, 0xF1C40FFF, BuyerData[id][buyerX], BuyerData[id][buyerY], BuyerData[id][buyerZ] + 0.25, 5.0, .testlos = 1);
854 Iter_Add(Buyers, id);
855
856 stmt_bind_value(AddBuyer, 0, DB::TYPE_INTEGER, id);
857 stmt_bind_value(AddBuyer, 1, DB::TYPE_INTEGER, skin);
858 stmt_bind_value(AddBuyer, 2, DB::TYPE_FLOAT, BuyerData[id][buyerX]);
859 stmt_bind_value(AddBuyer, 3, DB::TYPE_FLOAT, BuyerData[id][buyerY]);
860 stmt_bind_value(AddBuyer, 4, DB::TYPE_FLOAT, BuyerData[id][buyerZ]);
861 stmt_bind_value(AddBuyer, 5, DB::TYPE_FLOAT, BuyerData[id][buyerA]);
862
863 if(stmt_execute(AddBuyer))
864 {
865 SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}Buyer created.");
866 SetPlayerPos(playerid, BuyerData[id][buyerX] + (1.5 * floatsin(-BuyerData[id][buyerA], degrees)), BuyerData[id][buyerY] + (1.5 * floatcos(-BuyerData[id][buyerA], degrees)), BuyerData[id][buyerZ]);
867 }
868
869 return 1;
870}
871
872CMD:setbuyerskin(playerid, params[])
873{
874 if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
875 new id, skin;
876 if(sscanf(params, "ii", id, skin)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/setbuyerskin [buyer id] [skin id]");
877 if(!Iter_Contains(Buyers, id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid ID.");
878 if(!(0 <= skin <= 311)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid skin ID.");
879 BuyerData[id][buyerSkin] = skin;
880
881 DestroyActor(BuyerData[id][buyerActorID]);
882 BuyerData[id][buyerActorID] = CreateActor(skin, BuyerData[id][buyerX], BuyerData[id][buyerY], BuyerData[id][buyerZ], BuyerData[id][buyerA]);
883 SetActorInvulnerable(BuyerData[id][buyerActorID], 1);
884
885 stmt_bind_value(UpdateBuyer, 0, DB::TYPE_INTEGER, skin);
886 stmt_bind_value(UpdateBuyer, 1, DB::TYPE_FLOAT, BuyerData[id][buyerX]);
887 stmt_bind_value(UpdateBuyer, 2, DB::TYPE_FLOAT, BuyerData[id][buyerY]);
888 stmt_bind_value(UpdateBuyer, 3, DB::TYPE_FLOAT, BuyerData[id][buyerZ]);
889 stmt_bind_value(UpdateBuyer, 4, DB::TYPE_FLOAT, BuyerData[id][buyerA]);
890 stmt_bind_value(UpdateBuyer, 5, DB::TYPE_INTEGER, id);
891
892 if(stmt_execute(UpdateBuyer)) SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}Buyer updated.");
893 return 1;
894}
895
896CMD:setbuyerpos(playerid, params[])
897{
898 if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
899 new id;
900 if(sscanf(params, "i", id)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/setbuyerpos [buyer id]");
901 if(!Iter_Contains(Buyers, id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid ID.");
902 GetPlayerPos(playerid, BuyerData[id][buyerX], BuyerData[id][buyerY], BuyerData[id][buyerZ]);
903 GetPlayerFacingAngle(playerid, BuyerData[id][buyerA]);
904
905 DestroyActor(BuyerData[id][buyerActorID]);
906 BuyerData[id][buyerActorID] = CreateActor(BuyerData[id][buyerSkin], BuyerData[id][buyerX], BuyerData[id][buyerY], BuyerData[id][buyerZ], BuyerData[id][buyerA]);
907 SetActorInvulnerable(BuyerData[id][buyerActorID], 1);
908
909 Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, BuyerData[id][buyerLabel], E_STREAMER_X, BuyerData[id][buyerX]);
910 Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, BuyerData[id][buyerLabel], E_STREAMER_Y, BuyerData[id][buyerY]);
911 Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, BuyerData[id][buyerLabel], E_STREAMER_Z, BuyerData[id][buyerZ] + 0.25);
912
913 stmt_bind_value(UpdateBuyer, 0, DB::TYPE_INTEGER, BuyerData[id][buyerSkin]);
914 stmt_bind_value(UpdateBuyer, 1, DB::TYPE_FLOAT, BuyerData[id][buyerX]);
915 stmt_bind_value(UpdateBuyer, 2, DB::TYPE_FLOAT, BuyerData[id][buyerY]);
916 stmt_bind_value(UpdateBuyer, 3, DB::TYPE_FLOAT, BuyerData[id][buyerZ]);
917 stmt_bind_value(UpdateBuyer, 4, DB::TYPE_FLOAT, BuyerData[id][buyerA]);
918 stmt_bind_value(UpdateBuyer, 5, DB::TYPE_INTEGER, id);
919
920 if(stmt_execute(UpdateBuyer))
921 {
922 SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}Buyer updated.");
923 SetPlayerPos(playerid, BuyerData[id][buyerX] + (1.5 * floatsin(-BuyerData[id][buyerA], degrees)), BuyerData[id][buyerY] + (1.5 * floatcos(-BuyerData[id][buyerA], degrees)), BuyerData[id][buyerZ]);
924 }
925
926 return 1;
927}
928
929CMD:removebuyer(playerid, params[])
930{
931 if(!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Only RCON admins can use this command.");
932 new id;
933 if(sscanf(params, "i", id)) return SendClientMessage(playerid, 0xE88732FF, "SYNTAX: {FFFFFF}/removebuyer [buyer id]");
934 if(!Iter_Contains(Buyers, id)) return SendClientMessage(playerid, 0xE74C3CFF, "ERROR: {FFFFFF}Invalid ID.");
935 DestroyActor(BuyerData[id][buyerActorID]);
936 DestroyDynamic3DTextLabel(BuyerData[id][buyerLabel]);
937
938 BuyerData[id][buyerActorID] = -1;
939 BuyerData[id][buyerLabel] = Text3D: -1;
940 Iter_Remove(Buyers, id);
941
942 stmt_bind_value(RemoveBuyer, 0, DB::TYPE_INTEGER, id);
943 if(stmt_execute(RemoveBuyer)) SendClientMessage(playerid, 0x3498DBFF, "LUMBERJACK: {FFFFFF}Buyer removed.");
944 return 1;
945}