remove unneeded function
[minetest-xpmod:xp.git] / init.lua
1 -- Copyright 2012 Andrew Engelbrecht.
2 -- This file is licensed under the WTFPL.
3
4 ----------------------------------
5
6 -- Some variables you can change:
7 ----------------------------------
8
9 -- How often (in seconds) xplevels file saves
10 xp_save_delta = 10
11 -- Max level a player can become; 0 means max level of 0.
12 xp_max_level = 32
13
14 ----------------------------------
15
16 -- some mod variables:
17 ----------------------------------
18
19 xp_modver = "0.0.3"
20 xp_ffver = 1
21 xp_ffversupport = {1}
22 xp_file_name = "xplevels"
23
24 xp_xplevels_file = minetest.get_worldpath() .. "/" .. xp_file_name
25 xp_xplevels = {}
26 xp_playert = {}
27 -- dynamically updated to note changes since last save to file:
28 xp_changed = false
29 -- automatically populated with list of xp needed to gain next level
30 xp_lptable = nil
31 -- create a global random seed
32 xp_prand = PseudoRandom(os.time())
33
34 ----------------------------------
35
36 -- function definitions:
37 ----------------------------------
38
39 dofile(minetest.get_modpath('xp').."/filehandling.lua")
40
41
42 -- returns true if player has ever logged into the server
43 -- it's different than minetest.is_player() since it accepts the name as a string,
44 -- and because you don't need to run a function that returns a player obj only for *online* players.
45 local function xp_is_player(name)
46     local file
47
48     if xp_playert[name] == nil then
49         file = io.open(minetest.get_worldpath().."/players/"..name, "r")
50         if file ~= nil then
51             xp_playert[name] = true
52             io.close(file)
53         else
54             xp_playert[name] = false
55         end
56     end
57
58     return xp_playert[name]
59 end
60
61 -- returns the number of xp required to gain the next level.
62 -- pass it the player's current level.
63 local function xp_lastpoint(level)
64     local i
65
66     if xp_lptable == nil then
67         xp_lptable = {}
68         for i = 0,xp_max_level - 1 do
69             xp_lptable[i] = (i + 1)^2 * 10
70         end
71     end
72     return xp_lptable[level]
73 end
74
75 -- initializes a player's experience points and level for a given skill
76 local function xp_initpxp(name, skill)
77     if xp_xplevels[name] == nil then
78         xp_xplevels[name] = {}
79     end
80     if xp_xplevels[name][skill] == nil then
81         xp_xplevels[name][skill] = {xp = 0, level = 0}
82     end
83 end
84
85 -- gives npoints additional xp points for the player in the specified skillset
86 local function xp_gainxp(name, skill, npoints)
87     local stat, appnd, gainedlevel
88
89     if npoints < 0 then
90         return "negative"
91     end
92
93     if xp_is_player(name) == false then
94         return "non-player"
95     end
96
97     xp_initpxp(name, skill)
98     stat = xp_xplevels[name][skill]
99
100     if stat.level >= xp_max_level then
101         return "level-max"
102     end
103
104     -- the file needs to be updated in the future:
105     xp_changed = true
106
107     gainedlevel = false
108     while npoints > 0 do
109
110         if stat.xp + npoints < xp_lastpoint(stat.level) then
111             stat.xp = stat.xp + npoints
112             if gainedlevel == true then
113                 break
114             else
115                 return "gained-xp"
116             end
117         else
118             npoints = npoints - (xp_lastpoint(stat.level) - stat.xp)
119
120             stat.level = stat.level + 1
121             stat.xp = 0
122
123             if stat.level >= xp_max_level then
124                 appnd = " (max)"
125                 break
126             else
127                 appnd = ""
128             end
129
130             gainedlevel = true
131         end
132     end
133     minetest.chat_send_player(name, "Welcome to level "..stat.level.." in "..skill.."!"..appnd)
134     minetest.log("action", "xp mod: "..name.." gained level "..stat.level.." in "..skill..appnd..".")
135
136     return "gained-level"
137 end
138
139 -- prints the level and xp for a player in the specified skillset.
140 -- if no skillset is selected, then stats for every skillset are printed.
141 -- output goes to the player's chat console.
142 local function xp_printxp(name, skill)
143     local i,v,stat,appnd
144
145     if xp_xplevels[name] == nil then
146         minetest.chat_send_player(name, "You have no experience in anything.")
147         return
148     end
149
150     if skill == "" then
151         for i,v in pairs(xp_xplevels[name]) do
152             if i ~= "" then
153                 xp_printxp(name, i)
154             end
155         end
156     else
157         stat = xp_xplevels[name][skill]
158         if stat == nil then
159             minetest.chat_send_player(name, "You don't have experience in '"..skill.."'. (check your spelling.)")
160         else
161             xp_initpxp(name, skill)
162
163             if stat.level >= xp_max_level then
164                 appnd = ". (max)"
165             else
166                 appnd = ", plus "..stat.xp.."/"..xp_lastpoint(stat.level).." experience points."
167             end
168             minetest.chat_send_player(name, "You are level "..stat.level.." in "..skill..appnd)
169         end
170     end
171 end
172
173 -- returns the probability of getting a drop, based on a player's level
174 -- if level_max is set to the default of 32, then:
175 -- at level 0, the prob. is about 1/100,000.
176 -- at level_max, it's about 1/1,000.
177 local function xp_gotdrop(level)
178     local prob, randn
179
180     if level > xp_max_level then
181         level = xp_max_level
182     end
183
184     prob = (level + 3)^2 / (xp_max_level + 3)^2 / 1000
185     randn = xp_prand:next(0, 32767) / 32767
186
187     if randn < prob then
188         return true
189     else
190         return false
191     end
192 end
193
194 -- gives a mese block to player named 'name'.
195 -- if thier inventory is full, then it falls on the ground.
196 local function xp_givemese(name)
197     local player, pos
198     player = minetest.env:get_player_by_name(name)
199
200     xp_initpxp(name, 'digging')
201     if xp_xplevels[name].digging.level >= 2 then
202         inv = player:get_inventory()
203
204         if inv:room_for_item('main', 'default:mese') then
205             inv:add_item('main', 'default:mese')
206         else
207             pos = player:getpos()
208             pos.y = pos.y + 1.5
209             minetest.spawn_item(pos, 'default:mese')
210         end
211     end
212 end
213
214
215 ----------------------------------
216
217 -- priviledge registration:
218 ----------------------------------
219
220 minetest.register_privilege("givexp", {"Can give xp to others with /givexp", give_to_singleplayer = false})
221
222 ----------------------------------
223
224 -- callback registration:
225 ----------------------------------
226
227 -- it raises the xp of a player by the specified number of points.
228 minetest.register_chatcommand("givexp", {
229     params = "<username> <skill> <num-xp>",
230     description = "Give a player experience points",
231     privs = {givexp=true},
232     func = function(caller, param)
233         local ign, name, skill, numxp, result
234
235         -- matches two words: the name and the skill
236         ign,ign,name,skill,numxp = string.find(param, "^([^%s]+)%s+([^%s]+)%s+(%d+)%s*$")
237
238         if numxp == nil then
239             minetest.chat_send_player(caller, "Incorrect usage. See: /help givexp")
240         else
241             result = xp_gainxp(name,skill,tonumber(numxp))
242             if result == "non-player" then
243                 minetest.chat_send_player(caller, name.." isn't a player.")
244             elseif result == "level-max" then
245                 minetest.chat_send_player(caller, name.."'s "..skill.." level is already maxed-out.")
246             elseif result == "gained-xp" or result == "gained-level" then
247                 minetest.chat_send_player(caller, name.." is now level "..xp_xplevels[name][skill].level.." plus "..xp_xplevels[name][skill].xp.." xp in "..skill..".")
248                 minetest.log("action", "xp mod: ("..caller.." gave "..name.." "..numxp.." xp in "..skill..".)")
249             end
250         end
251     end,
252 })
253
254 -- shows the xp for the calling player, for a given skill, or if none is listed, then all of them.
255 minetest.register_chatcommand("showxp", {
256     params = "<skill (opt)>",
257     description = "Show your experience points",
258     privs = {},
259     func = xp_printxp,
260 })
261
262 -- every time a player places an object, they get one extra xp for this skill
263 minetest.register_on_placenode(function(pos, newnode, placer, oldnode)
264     local name = placer:get_player_name()
265     xp_gainxp(name, "construction", 1)
266 end)
267
268 -- every time a player digs an object, they get one extra xp for this skill
269 -- also, for now, the player gets mese somewhat randomly, based on level.
270 minetest.register_on_dignode(function(pos, node, player)
271     local name = player:get_player_name()
272     xp_gainxp(name, "digging", 1)
273     if xp_gotdrop(xp_xplevels[name].digging.level) then
274         xp_givemese(name)
275     end
276 end)
277
278 -- every time a player joins the game, add their name to the list of existing players.
279 -- if their player file wasn't readable, this works around the issue. also, if this is a new player,
280 -- then they will be added to the table even if they were previously marked as non-existent.
281 minetest.register_on_joinplayer(function(player)
282     local name = player:get_player_name()
283     xp_playert[name] = true
284 end)
285
286 ----------------------------------
287
288 -- execute at the start of the game:
289 ----------------------------------
290
291 -- before this file is done being read, this function must be called
292 -- to load player xp and levels for each skill
293 xp_loadxp()
294