local S = minetest.get_translator("sf_upgrade") local NS = function(s) return s end local FS = function(...) return minetest.formspec_escape(S(...)) end local FALLBACK_UPGRADE_ICON = "sf_upgrade_noicon.png" sf_upgrade = {} local player_states = {} local registered_upgrades = {} local registered_categories = {} local upgrade_particles = function(player) minetest.add_particlespawner({ amount = 32, time = 0.05, exptime = { min = 3, max = 5, }, size = 1.5, pos = { min = vector.new(-0.8, 0, -0.8), max = vector.new(0.8, 1.5, 0.8), }, attached = player, vel = { min = vector.new(0, 1, 0), max = vector.new(0, 3, 0), }, drag = vector.new(5, 3, 5), texture = { name = "sf_upgrade_particle.png", alpha_tween = { 1, 0, start = 0.8 }, }, }) end sf_upgrade.register_upgrade = function(name, def) registered_upgrades[name] = def end sf_upgrade.register_category = function(name) table.insert(registered_categories, name) end sf_upgrade.register_category("speed") sf_upgrade.register_category("health") sf_upgrade.register_category("orb") sf_upgrade.register_category("lightstaff_damage") sf_upgrade.register_category("lightstaff_speed") sf_upgrade.register_upgrade("speed1", { name = NS("Fast Walker 1"), description = NS("Walking speed +5%"), icon = "sf_upgrade_speed1.png", category = "speed", price = 15, price_type = "sf_resources:shadow_fragment", on_apply = function(player) playerphysics.add_physics_factor(player, "speed", "sf_upgrade:speed1", 1.05) end, on_unapply = function(player) playerphysics.remove_physics_factor(player, "speed", "sf_upgrade:speed1") end, }) sf_upgrade.register_upgrade("speed2", { name = NS("Fast Walker 2"), description = NS("Walking speed +5%"), icon = "sf_upgrade_speed2.png", category = "speed", depends = "speed1", price = 30, price_type = "sf_resources:shadow_fragment", on_apply = function(player) playerphysics.add_physics_factor(player, "speed", "sf_upgrade:speed2", 1.05) end, on_unapply = function(player) playerphysics.remove_physics_factor(player, "speed", "sf_upgrade:speed2") end, }) sf_upgrade.register_upgrade("speed3", { name = NS("Fast Walker 3"), description = NS("Walking speed +5%"), icon = "sf_upgrade_speed3.png", category = "speed", depends = "speed2", price = 45, price_type = "sf_resources:shadow_fragment", on_apply = function(player) playerphysics.add_physics_factor(player, "speed", "sf_upgrade:speed3", 1.05) end, on_unapply = function(player) playerphysics.remove_physics_factor(player, "speed", "sf_upgrade:speed3") end, }) sf_upgrade.register_upgrade("bright_orb", { name = NS("Bright Orb"), description = NS("Your light orb shines brighter"), icon = "sf_upgrade_bright_orb.png", category = "orb", price = 10, price_type = "sf_resources:shadow_fragment", on_upgrade = function(player) minetest.after(3, function(player) if player and player:is_player() then sf_dialog.show_dialog(player, "brighter") end end, player) end, on_apply = function(player) local ppos = player:get_pos() local nodes = minetest.find_nodes_in_area(vector.offset(ppos, -5, -5, -5), vector.offset(ppos, 5, 5, 5), {"group:light_orb_light"}) for n=1, #nodes do minetest.set_node(nodes[n], {name="sf_mobs:light_orb_light_2"}) end end, on_unapply = function(player) local ppos = player:get_pos() local nodes = minetest.find_nodes_in_area(vector.offset(ppos, -5, -5, -5), vector.offset(ppos, 5, 5, 5), {"group:light_orb_light"}) for n=1, #nodes do minetest.set_node(nodes[n], {name="sf_mobs:light_orb_light"}) end end, }) sf_upgrade.register_upgrade("hard_light1", { name = NS("Hard Light 1"), description = NS("Staff of Light damage +1"), icon = "sf_upgrade_hard_light.png^sf_upgrade_level1.png", category = "lightstaff_damage", price = 20, price_type = "sf_resources:shadow_fragment", on_apply = function(player) -- Upgrade is handled by staff of light item end, on_unapply = function(player) -- Upgrade is handled by staff of light item end, }) sf_upgrade.register_upgrade("hard_light2", { name = NS("Hard Light 2"), description = NS("Staff of Light damage +2"), icon = "sf_upgrade_hard_light.png^sf_upgrade_level2.png", category = "lightstaff_damage", depends = "hard_light1", price = 30, price_type = "sf_resources:shadow_fragment", on_apply = function(player) -- Upgrade is handled by staff of light item end, on_unapply = function(player) -- Upgrade is handled by staff of light item end, }) sf_upgrade.register_upgrade("hard_light_3", { name = NS("Hard Light 3"), description = NS("Staff of Light damage +3"), icon = "sf_upgrade_hard_light.png^sf_upgrade_level3.png", depends = "hard_light2", category = "lightstaff_damage", price = 40, price_type = "sf_resources:shadow_fragment", on_apply = function(player) -- Upgrade is handled by staff of light item end, on_unapply = function(player) -- Upgrade is handled by staff of light item end, }) sf_upgrade.register_upgrade("swift_staff1", { name = NS("Swift Staff 1"), description = NS("Staff of Light fires faster"), icon = "sf_upgrade_swift_staff.png^sf_upgrade_level1.png", category = "lightstaff_speed", price = 40, price_type = "sf_resources:shadow_fragment", on_apply = function(player) -- Upgrade is handled by staff of light item end, on_unapply = function(player) -- Upgrade is handled by staff of light item end, }) sf_upgrade.register_upgrade("swift_staff2", { name = NS("Swift Staff 2"), description = NS("Staff of Light fires even faster"), icon = "sf_upgrade_swift_staff.png^sf_upgrade_level2.png", category = "lightstaff_speed", depends = "swift_staff1", price = 60, price_type = "sf_resources:shadow_fragment", on_apply = function(player) -- Upgrade is handled by staff of light item end, on_unapply = function(player) -- Upgrade is handled by staff of light item end, }) sf_upgrade.register_upgrade("swift_staff3", { name = NS("Swift Staff 3"), description = NS("Staff of Light fires super-fast!"), icon = "sf_upgrade_swift_staff.png^sf_upgrade_level3.png", category = "lightstaff_speed", price = 80, depends = "swift_staff2", price_type = "sf_resources:shadow_fragment", on_apply = function(player) -- Upgrade is handled by staff of light item end, on_unapply = function(player) -- Upgrade is handled by staff of light item end, }) local apply_health = function(player) sf_health.update_max_hp(player) end local unapply_health = function(player) sf_health.update_max_hp(player) end sf_upgrade.register_upgrade("health1", { name = NS("Life Force 1"), description = NS("Max. health +2"), icon = "sf_upgrade_health.png^sf_upgrade_level1.png", category = "health", price = 75, price_type = "sf_resources:healing_essence", on_apply = apply_health, on_unapply = unapply_health, }) sf_upgrade.register_upgrade("health2", { name = NS("Life Force 2"), description = NS("Max. health +2"), icon = "sf_upgrade_health.png^sf_upgrade_level2.png", category = "health", price = 75, price_type = "sf_resources:healing_essence", on_apply = apply_health, on_unapply = unapply_health, depends = "health1", }) sf_upgrade.register_upgrade("health3", { name = NS("Life Force 3"), description = NS("Max. health +2"), icon = "sf_upgrade_health.png^sf_upgrade_level3.png", category = "health", price = 75, price_type = "sf_resources:healing_essence", on_apply = apply_health, on_unapply = unapply_health, depends = "health2", }) sf_upgrade.register_upgrade("health4", { name = NS("Life Force 4"), description = NS("Max. health +2"), icon = "sf_upgrade_health.png^sf_upgrade_level4.png", category = "health", price = 75, price_type = "sf_resources:healing_essence", on_apply = apply_health, on_unapply = unapply_health, depends = "health3", }) local get_upgrade_tree = function() local categories = {} for k,v in pairs(registered_upgrades) do local col = v.category if not categories[col] then categories[col] = {} end table.insert(categories[col], k) end local sorted_categories = {} for categoryname, category in pairs(categories) do local trailing_upgrade local found local sorted_category = {} repeat found = false for cc=1, #category do local upgradeid = category[cc] local upgrade = registered_upgrades[upgradeid] if upgrade.depends == nil and #sorted_category == 0 then table.insert(sorted_category, upgradeid) trailing_upgrade = upgradeid found = true break end if upgrade.depends ~= nil and upgrade.depends == trailing_upgrade then table.insert(sorted_category, upgradeid) trailing_upgrade = upgradeid found = true break end end until (found == false) sorted_categories[categoryname] = sorted_category end return sorted_categories end local upgrade_tree = get_upgrade_tree() -- Buy upgrade, provided the player is ellegible for it. -- Returns , sf_upgrade.buy_upgrade = function(player, upgradeid) local price_type = registered_upgrades[upgradeid].price_type local pts = sf_resources.get_resource_count(player, price_type) local price = registered_upgrades[upgradeid].price if not sf_upgrade.upgrade_unlocked(player, upgradeid) then return false, "locked" end if pts < price then -- Buying fails if player doesn't have the upgrade points return false, "too_expensive" end if not sf_upgrade.has_upgrade(player, upgradeid) then -- Give upgrade and pay the price sf_upgrade.give_upgrade(player, upgradeid) local upgrade = registered_upgrades[upgradeid] if upgrade.on_upgrade then upgrade.on_upgrade(player) end pts = pts - price sf_resources.set_resource_count(player, price_type, pts) return true end -- Already has upgrade return false, "has_already" end -- Returns true if player has upgrade sf_upgrade.has_upgrade = function(player, upgradeid) local pmeta = player:get_meta() return pmeta:get_int("sf_upgrade:upgrade__"..upgradeid) == 1 end -- Returns true if upgrade is unlocked and can be bought sf_upgrade.upgrade_unlocked = function(player, upgradeid) local pmeta = player:get_meta() local upgrade = registered_upgrades[upgradeid] -- Check the upgrade's dependency if upgrade.depends == nil then -- No dependency return true end if sf_upgrade.has_upgrade(player, upgrade.depends) then -- The player has the dependency return true else -- The player does not have the dependency return false end end -- Give an upgrade to player sf_upgrade.give_upgrade = function(player, upgradeid) local pmeta = player:get_meta() pmeta:set_int("sf_upgrade:upgrade__"..upgradeid, 1) local upgrade = registered_upgrades[upgradeid] upgrade.on_apply(player) minetest.log("action", "[sf_upgrade] Upgrade '"..upgradeid.."' given to "..player:get_player_name()) end -- Remove an upgrade from player, if it active sf_upgrade.remove_upgrade = function(player, upgradeid) local pmeta = player:get_meta() if pmeta:get_int("sf_upgrade:upgrade__"..upgradeid) == 1 then pmeta:set_int("sf_upgrade:upgrade__"..upgradeid, 0) local upgrade = registered_upgrades[upgradeid] upgrade.on_unapply(player) minetest.log("action", "[sf_upgrade] Upgrade '"..upgradeid.."' removed from "..player:get_player_name()) end end -- Remove all active upgrades from player sf_upgrade.remove_all_upgrades = function(player) local pmeta = player:get_meta() for upgradeid, upgrade in pairs(registered_upgrades) do if pmeta:get_int("sf_upgrade:upgrade__"..upgradeid) == 1 then pmeta:set_int("sf_upgrade:upgrade__"..upgradeid, 0) upgrade.on_unapply(player) end end minetest.log("action", "[sf_upgrade] All upgrades removed from "..player:get_player_name()) end -- Give all possible upgrades to player sf_upgrade.give_all_upgrades = function(player) local pmeta = player:get_meta() for upgradeid, upgrade in pairs(registered_upgrades) do if pmeta:get_int("sf_upgrade:upgrade__"..upgradeid) == 0 then pmeta:set_int("sf_upgrade:upgrade__"..upgradeid, 1) upgrade.on_apply(player) end end minetest.log("action", "[sf_upgrade] All upgrades given to "..player:get_player_name()) end local upgrade_tree_to_formstring = function(tree, player) local buttons = "" -- formspec string fo buttons local connectors = "" -- lines that show the upgrade dependencies local x, y = 0, 0 local selection = player_states[player:get_player_name()].selected_upgrade for c=1, #registered_categories do local categoryname = registered_categories[c] local category = tree[categoryname] for u=1, #category do local upgradeid = category[u] local upgrade = registered_upgrades[upgradeid] local uicon = upgrade.icon or FALLBACK_UPGRADE_ICON local icon = uicon if not sf_upgrade.upgrade_unlocked(player, upgradeid) then icon = "("..icon.."^[hsl:0:-100:0)" end if sf_upgrade.has_upgrade(player, upgradeid) then icon = "sf_upgrade_has_upgrade.png^"..icon end if selection == upgradeid then icon = icon.."^sf_upgrade_selected_upgrade.png" end -- Add upgrade button (and tooltip) buttons = buttons .. "image_button["..x..","..y..";1,1;".. icon..";upgrade__"..upgradeid..";]" buttons = buttons .. "tooltip[upgrade__"..upgradeid..";"..FS(upgrade.name).."]" -- Draw dependency line if u < #category then connectors = connectors .. "box["..(x+0.45)..","..(y+0.9)..";0.1,0.45;#00000080]" end y = y + 1.25 end x = x + 1.25 y = 0 end local form = connectors .. buttons return form end sf_upgrade.show_upgrade_formspec = function(player) local pname = player:get_player_name() -- Get selected upgrade local sel = player_states[pname].selected_upgrade local sel_icon, sel_namedesc, sel_price, sel_buy = "", "", "", "" if sel then local upgrade = registered_upgrades[sel] local icon = upgrade.icon or FALLBACK_UPGRADE_ICON if not sf_upgrade.upgrade_unlocked(player, sel) then icon = icon.."^[hsl:0:-100:0" end sel_icon = "image[0.25,0.25;1.5,1.5;"..icon.."]" sel_namedesc = "textarea[2,0.25;2.75,1.5;;;"..FS(upgrade.name).."\n\n"..FS(upgrade.description).."]" if sf_upgrade.has_upgrade(player, sel) then sel_price = "label[0.25,0.4;"..minetest.colorize("#50FF50", FS("You have this upgrade.")).."]" elseif not sf_upgrade.upgrade_unlocked(player, sel) then sel_price = "label[0.25,0.4;"..FS("Upgrade locked.").."]" else local resource = sf_resources.registered_resources[upgrade.price_type] local player_has = sf_resources.get_resource_count(player, upgrade.price_type) sel_price = "label[0.25,0.40;"..FS("Cost:").."]" .. "image[0.25,0.75;0.5,0.5;"..resource.icon.."]" .. "tooltip[0.25,0.75;0.5,0.5;"..minetest.formspec_escape(resource.description).."]" .. "label[0.95,1;"..FS("×@1", upgrade.price).."]" .. "label[0.25,1.65;"..FS("In inventory: @1", player_has).."]" sel_buy = "button[3,1;1,0.75;buy;"..FS("Get").."]" end end local upgrade_tree_formstring = upgrade_tree_to_formstring(upgrade_tree, player) local form = [=[ formspec_version[6] size[10,9.3] box[0.25,0.25;9.5,0.7;#ff8000] label[0.5,0.6;]=]..FS("Upgrades")..[=[] container[0.25,0.95] box[0,0;9.5,5.25;#ff800020] container[0.25,0.25] ]=]..upgrade_tree_formstring..[=[ container_end[] container_end[] ]=] if sel then form = form .. [=[ container[0.25,6.7] box[0,-0.2;9.5,0.6;#ff8000] label[0.25,0.1;]=]..FS("Selected upgrade")..[=[] container[0,0.4] box[0,0;9.5,2;#ff800020] box[5,0;0.25,2;#ff800020] container[0,0] ]=]..sel_icon..sel_namedesc..[=[ container_end[] container[5.25,0] ]=]..sel_price..sel_buy..[=[ container_end[] container_end[] container_end[] ]=] end minetest.show_formspec(pname, "sf_upgrade:upgrade", form) end minetest.register_on_player_receive_fields(function(player, formname, fields) if formname ~= "sf_upgrade:upgrade" then return end local pname = player:get_player_name() -- Buy selected upgrade if fields.buy then local sel = player_states[pname].selected_upgrade if not sel then return end local buy_ok, buy_fail_reason = sf_upgrade.buy_upgrade(player, sel) if not buy_ok then minetest.sound_play({name="sf_upgrade_upgrade_fail", gain=0.8}, {to_player=pname}, true) else upgrade_particles(player) minetest.sound_play({name="sf_upgrade_upgrade", gain=0.5}, {to_player=pname}, true) sf_upgrade.show_upgrade_formspec(player) minetest.log("action", "[sf_upgrade] "..pname.." bought upgrade "..sel) end return end -- Select upgrade for upgradeid, upgrade in pairs(registered_upgrades) do if fields["upgrade__"..upgradeid] then player_states[pname].selected_upgrade = upgradeid sf_upgrade.show_upgrade_formspec(player) break end end end) minetest.register_chatcommand("remove_upgrades", { description = S("Remove all your upgrades"), privs = { server = true }, func = function(name) local player = minetest.get_player_by_name(name) if not player or not player:is_player() then return false, S("No player.") end sf_upgrade.remove_all_upgrades(player) return true, S("All upgrades removed.") end, }) minetest.register_chatcommand("get_upgrades", { description = S("Get all upgrades"), privs = { server = true }, func = function(name) local player = minetest.get_player_by_name(name) if not player or not player:is_player() then return false, S("No player.") end sf_upgrade.give_all_upgrades(player) return true, S("All upgrades received.") end, }) -- Apply the effects of all the player's upgrades initially local init_apply_upgrades = function(player) local pmeta = player:get_meta() for upgradeid, upgrade in pairs(registered_upgrades) do local has = pmeta:get_int("sf_upgrade:upgrade__"..upgradeid) == 1 if has then upgrade.on_apply(player) end end end minetest.register_on_joinplayer(function(player) player_states[player:get_player_name()] = {} -- Apply upgrade effects on join init_apply_upgrades(player) end) minetest.register_on_leaveplayer(function(player) player_states[player:get_player_name()] = nil end)