· 7 years ago · Feb 24, 2019, 06:44 PM
1
2-- this module describe the home system (experimental, a lot can happen and not being handled)
3
4local lang = vRP.lang
5local cfg = module("cfg/homes")
6
7-- sql
8
9MySQL.createCommand("vRP/home_tables", [[
10CREATE TABLE IF NOT EXISTS vrp_user_homes(
11 user_id INTEGER,
12 home VARCHAR(255),
13 number INTEGER,
14 CONSTRAINT pk_user_homes PRIMARY KEY(user_id),
15 CONSTRAINT fk_user_homes_users FOREIGN KEY(user_id) REFERENCES vrp_users(id) ON DELETE CASCADE,
16 UNIQUE(home,number)
17);
18]])
19
20MySQL.createCommand("vRP/get_address","SELECT home, number FROM vrp_user_homes WHERE user_id = @user_id")
21MySQL.createCommand("vRP/get_home_owner","SELECT user_id FROM vrp_user_homes WHERE home = @home AND number = @number")
22MySQL.createCommand("vRP/rm_address","DELETE FROM vrp_user_homes WHERE user_id = @user_id")
23MySQL.createCommand("vRP/set_address","REPLACE INTO vrp_user_homes(user_id,home,number) VALUES(@user_id,@home,@number)")
24
25-- init
26MySQL.execute("vRP/home_tables")
27
28-- api
29
30local components = {}
31
32-- cbreturn user address (home and number) or nil
33function vRP.getUserAddress(user_id, cbr)
34 local task = Task(cbr)
35
36 MySQL.query("vRP/get_address", {user_id = user_id}, function(rows,affected)
37 task({rows[1]})
38 end)
39end
40
41-- set user address
42function vRP.setUserAddress(user_id,home,number)
43 MySQL.execute("vRP/set_address", {user_id = user_id, home = home, number = number})
44end
45
46-- remove user address
47function vRP.removeUserAddress(user_id)
48 MySQL.execute("vRP/rm_address", {user_id = user_id})
49end
50
51-- cbreturn user_id or nil
52function vRP.getUserByAddress(home,number,cbr)
53 local task = Task(cbr)
54
55 MySQL.query("vRP/get_home_owner", {home = home, number = number}, function(rows, affected)
56 if #rows > 0 then
57 task({rows[1].user_id})
58 else
59 task()
60 end
61 end)
62end
63
64-- find a free address number to buy
65-- cbreturn number or nil if no numbers availables
66function vRP.findFreeNumber(home,max,cbr)
67 local task = Task(cbr)
68
69 local i = 1
70 local function search()
71 vRP.getUserByAddress(home,i,function(user_id)
72 if user_id == nil then -- found
73 task({i})
74 else -- not found
75 i = i+1
76 if i <= max then -- continue search
77 search()
78 else -- global not found
79 task()
80 end
81 end
82 end)
83 end
84
85 search()
86end
87
88-- define home component
89-- name: unique component id
90-- oncreate(owner_id, slot_type, slot_id, cid, config, x, y, z, player)
91-- ondestroy(owner_id, slot_type, slot_id, cid, config, x, y, z, player)
92function vRP.defHomeComponent(name, oncreate, ondestroy)
93 components[name] = {oncreate,ondestroy}
94end
95
96-- SLOTS
97
98-- used (or not) slots
99local uslots = {}
100for k,v in pairs(cfg.slot_types) do
101 uslots[k] = {}
102 for l,w in pairs(v) do
103 uslots[k][l] = {used=false}
104 end
105end
106
107-- return slot id or nil if no slot available
108local function allocateSlot(stype)
109 local slots = cfg.slot_types[stype]
110 if slots then
111 local _uslots = uslots[stype]
112 -- search the first unused slot
113 for k,v in pairs(slots) do
114 if _uslots[k] and not _uslots[k].used then
115 _uslots[k].used = true -- set as used
116 return k -- return slot id
117 end
118 end
119 end
120
121 return nil
122end
123
124-- free a slot
125local function freeSlot(stype, id)
126 local slots = cfg.slot_types[stype]
127 if slots then
128 uslots[stype][id] = {used = false} -- reset as unused
129 end
130end
131
132-- get in use address slot (not very optimized yet)
133-- return slot_type, slot_id or nil,nil
134local function getAddressSlot(home_name,number)
135 for k,v in pairs(uslots) do
136 for l,w in pairs(v) do
137 if w.home_name == home_name and tostring(w.home_number) == tostring(number) then
138 return k,l
139 end
140 end
141 end
142
143 return nil,nil
144end
145
146-- builds
147
148local function is_empty(table)
149 for k,v in pairs(table) do
150 return false
151 end
152
153 return true
154end
155
156-- leave slot
157local function leave_slot(user_id,player,stype,sid) -- called when a player leave a slot
158 print(user_id.." leave slot "..stype.." "..sid)
159 local slot = uslots[stype][sid]
160 local home = cfg.homes[slot.home_name]
161
162 -- record if inside a home slot
163 local tmp = vRP.getUserTmpTable(user_id)
164 if tmp then
165 tmp.home_stype = nil
166 tmp.home_sid = nil
167 end
168
169 -- teleport to home entry point (outside)
170 vRPclient.teleport(player, home.entry_point) -- already an array of params (x,y,z)
171
172 -- uncount player
173 slot.players[user_id] = nil
174
175 -- destroy loaded components and special entry component
176 for k,v in pairs(cfg.slot_types[stype][sid]) do
177 local name,x,y,z = table.unpack(v)
178
179 if name == "entry" then
180 -- remove marker/area
181 local nid = "vRP:home:slot"..stype..sid
182 vRPclient.removeNamedMarker(player,{nid})
183 vRP.removeArea(player,nid)
184 else
185 local component = components[v[1]]
186 if component then
187 -- ondestroy(owner_id, slot_type, slot_id, cid, config, x, y, z, player)
188 component[2](slot.owner_id, stype, sid, k, v._config or {}, x, y, z, player)
189 end
190 end
191 end
192
193 if is_empty(slot.players) then -- free the slot
194 print("free slot "..stype.." "..sid)
195 freeSlot(stype,sid)
196 end
197end
198
199-- enter slot
200local function enter_slot(user_id,player,stype,sid) -- called when a player enter a slot
201 print(user_id.." enter slot "..stype.." "..sid)
202 local slot = uslots[stype][sid]
203 local home = cfg.homes[slot.home_name]
204
205 -- record inside a home slot
206 local tmp = vRP.getUserTmpTable(user_id)
207 if tmp then
208 tmp.home_stype = stype
209 tmp.home_sid = sid
210 end
211
212 -- count
213 slot.players[user_id] = player
214
215 -- build the slot entry menu
216 local menu = {name=slot.home_name,css={top="75px",header_color="rgba(0,255,125,0.75)"}}
217 menu[lang.home.slot.leave.title()] = {function(player,choice) -- add leave choice
218 leave_slot(user_id,player,stype,sid)
219 end}
220
221 vRP.getUserAddress(user_id, function(address)
222 -- check if owner
223 if address ~= nil and address.home == slot.home_name and tostring(address.number) == slot.home_number then
224 menu[lang.home.slot.ejectall.title()] = {function(player,choice) -- add eject all choice
225 -- copy players before calling leave for each (iteration while removing)
226 local copy = {}
227 for k,v in pairs(slot.players) do
228 copy[k] = v
229 end
230
231 for k,v in pairs(copy) do
232 leave_slot(k,v,stype,sid)
233 end
234 end,lang.home.slot.ejectall.description()}
235 end
236
237 -- build the slot entry menu marker/area
238
239 local function entry_enter(player,area)
240 vRP.openMenu(player,menu)
241 end
242
243 local function entry_leave(player,area)
244 vRP.closeMenu(player)
245 end
246
247 -- build components and special entry component
248 for k,v in pairs(cfg.slot_types[stype][sid]) do
249 local name,x,y,z = table.unpack(v)
250
251 if name == "entry" then
252 -- teleport to the slot entry point
253 vRPclient.teleport(player, {x,y,z}) -- already an array of params (x,y,z)
254
255 local nid = "vRP:home:slot"..stype..sid
256 vRPclient.setNamedMarker(player,{nid,x,y,z-1,0.7,0.7,0.5,0,255,125,125,150})
257 vRP.setArea(player,nid,x,y,z,1,1.5,entry_enter,entry_leave)
258 else -- load regular component
259 local component = components[v[1]]
260 if component then
261 -- oncreate(owner_id, slot_type, slot_id, cid, config, x, y, z, player)
262 component[1](slot.owner_id, stype, sid, k, v._config or {}, x, y, z, player)
263 end
264 end
265 end
266 end)
267end
268
269-- access a home by address
270-- cbreturn true on success
271function vRP.accessHome(user_id, home, number, cbr)
272 local task = Task(cbr)
273
274 local _home = cfg.homes[home]
275 local stype,slotid = getAddressSlot(home,number) -- get current address slot
276 local player = vRP.getUserSource(user_id)
277
278 vRP.getUserByAddress(home,number, function(owner_id)
279 if _home ~= nil and player ~= nil then
280 if stype == nil then -- allocate a new slot
281 stype = _home.slot
282 slotid = allocateSlot(_home.slot)
283
284 if slotid ~= nil then -- allocated, set slot home infos
285 local slot = uslots[stype][slotid]
286 slot.home_name = home
287 slot.home_number = number
288 slot.owner_id = owner_id
289 slot.players = {} -- map user_id => player
290 end
291 end
292
293 if slotid ~= nil then -- slot available
294 enter_slot(user_id,player,stype,slotid)
295 task({true})
296 end
297 end
298 end)
299end
300
301-- build the home entry menu
302local function build_entry_menu(user_id, home_name)
303 local home = cfg.homes[home_name]
304 local menu = {name=home_name,css={top="75px",header_color="rgba(0,255,125,0.75)"}}
305
306 -- intercom, used to enter in a home
307 menu[lang.home.intercom.title()] = {function(player,choice)
308 vRP.prompt(player, lang.home.intercom.prompt(), "", function(player,number)
309 number = parseInt(number)
310 vRP.getUserByAddress(home_name,number,function(huser_id)
311 if huser_id ~= nil then
312 if huser_id == user_id then -- identify owner (direct home access)
313 vRP.accessHome(user_id, home_name, number, function(ok)
314 if not ok then
315 vRPclient.notify(player,{lang.home.intercom.not_available()})
316 end
317 end)
318 else -- try to access home by asking owner
319 local hplayer = vRP.getUserSource(huser_id)
320 if hplayer ~= nil then
321 vRP.prompt(player,lang.home.intercom.prompt_who(),"",function(player,who)
322 vRPclient.notify(player,{lang.home.intercom.asked()})
323 -- request owner to open the door
324 vRP.request(hplayer, lang.home.intercom.request({who}), 30, function(hplayer,ok)
325 if ok then
326 vRP.accessHome(user_id, home_name, number)
327 else
328 vRPclient.notify(player,{lang.home.intercom.refused()})
329 end
330 end)
331 end)
332 else
333 vRPclient.notify(player,{lang.home.intercom.refused()})
334 end
335 end
336 else
337 vRPclient.notify(player,{lang.common.not_found()})
338 end
339 end)
340 end)
341 end,lang.home.intercom.description()}
342
343 menu[lang.home.buy.title()] = {function(player,choice)
344 vRP.getUserAddress(user_id, function(address)
345 if address == nil then -- check if not already have a home
346 vRP.findFreeNumber(home_name, home.max, function(number)
347 if number ~= nil then
348 if vRP.tryPayment(user_id, home.buy_price) then
349 -- bought, set address
350 vRP.setUserAddress(user_id, home_name, number)
351
352 vRPclient.notify(player,{lang.home.buy.bought()})
353 else
354 vRPclient.notify(player,{lang.money.not_enough()})
355 end
356 else
357 vRPclient.notify(player,{lang.home.buy.full()})
358 end
359 end)
360 else
361 vRPclient.notify(player,{lang.home.buy.have_home()})
362 end
363 end)
364 end, lang.home.buy.description({home.buy_price})}
365
366 menu[lang.home.sell.title()] = {function(player,choice)
367 vRP.getUserAddress(user_id, function(address)
368 if address ~= nil and address.home == home_name then -- check if already have a home
369 -- "Deseja vender a casa? Digite sim ou nao:"
370
371 vRP.prompt(player,"Deseja vender a casa? Digite sim ou nao:","",function(player,cbd)
372
373 if cbd == "sim" then
374 vRP.removeUserAddress(user_id)
375 vRPclient.notify(player,{"~r~Aguarde, estamos procurando compradores..."})
376 Wait(5000)
377 vRPclient.notify(player,{"~r~A sua casa foi comprada pelo prefeito!"})
378 Wait(2000)
379 vRP.giveMoney(user_id, home.sell_price)
380 else
381 vRPclient.notify(player,{"~r~A casa não foi vendida"})
382 end
383
384 end)
385
386 else
387 vRPclient.notify(player,{lang.home.sell.no_home()})
388 end
389 end)
390 end, lang.home.sell.description({home.sell_price})}
391
392 return menu
393end
394
395-- build homes entry points
396local function build_client_homes(source)
397 local user_id = vRP.getUserId(source)
398 if user_id ~= nil then
399 for k,v in pairs(cfg.homes) do
400 local x,y,z = table.unpack(v.entry_point)
401
402 local function entry_enter(player,area)
403 local user_id = vRP.getUserId(player)
404 if user_id ~= nil and vRP.hasPermissions(user_id,v.permissions or {}) then
405 vRP.openMenu(source,build_entry_menu(user_id, k))
406 end
407 end
408
409 local function entry_leave(player,area)
410 vRP.closeMenu(player)
411 end
412
413 vRPclient.addBlip(source,{x,y,z,v.blipid,v.blipcolor,k})
414 vRPclient.addMarker(source,{x,y,z-1,0.7,0.7,0.5,0,255,125,125,150})
415
416 vRP.setArea(source,"vRP:home"..k,x,y,z,1,1.5,entry_enter,entry_leave)
417 end
418 end
419end
420
421AddEventHandler("vRP:playerSpawn",function(user_id, source, first_spawn)
422 if first_spawn then -- first spawn, build homes
423 build_client_homes(source)
424 else -- death, leave home if inside one
425 -- leave slot if inside one
426 local tmp = vRP.getUserTmpTable(user_id)
427 if tmp and tmp.home_stype then
428 leave_slot(user_id, source, tmp.home_stype, tmp.home_sid)
429 end
430 end
431end)
432
433AddEventHandler("vRP:playerLeave",function(user_id, player)
434 -- leave slot if inside one
435 local tmp = vRP.getUserTmpTable(user_id)
436 if tmp and tmp.home_stype then
437 leave_slot(user_id, player, tmp.home_stype, tmp.home_sid)
438 end
439end)