From f59ba90eea5ab923a284cd6ef09643b837540066 Mon Sep 17 00:00:00 2001 From: dg <dg@51575b47-30f0-44d4-a5cc-537603b46e54> Date: Mon, 7 Mar 2011 02:47:53 +0000 Subject: [PATCH] rework loot system Most things that happened on actor's turn now happen on game's turn. Due to this change Movement Infusion, Lightning Speed and Step Up now provide their buff for a veyr low number of turns (usualy one or two), but since they are game turns they give ample player turns git-svn-id: http://svn.net-core.org/repos/t-engine4@2953 51575b47-30f0-44d4-a5cc-537603b46e54 --- game/engines/default/engine/Zone.lua | 87 +++++++++---- .../default/modules/boot/class/Game.lua | 2 +- game/modules/tome/class/Actor.lua | 66 ++++++---- game/modules/tome/class/Game.lua | 1 + game/modules/tome/class/GameState.lua | 123 +++++++++++++++++- game/modules/tome/class/interface/Combat.lua | 2 +- .../tome/data/talents/gifts/storm-drake.lua | 7 +- .../tome/data/talents/misc/inscriptions.lua | 12 +- .../talents/techniques/battle-tactics.lua | 5 +- game/modules/tome/data/zones/daikara/zone.lua | 2 +- .../tome/data/zones/deep-bellow/zone.lua | 2 +- 11 files changed, 241 insertions(+), 68 deletions(-) diff --git a/game/engines/default/engine/Zone.lua b/game/engines/default/engine/Zone.lua index 10ae6e3f44..c917ff5b18 100644 --- a/game/engines/default/engine/Zone.lua +++ b/game/engines/default/engine/Zone.lua @@ -96,6 +96,10 @@ function _M:leave() game.level = nil end +function _M:level_adjust_level(level, type) + return self.base_level + (self.specific_base_level[type] or 0) + (level.level - 1) + (add_level or 0) +end + --- Parses the npc/objects list and compute rarities for random generation -- ONLY entities with a rarity properties will be considered.<br/> -- This means that to get a never-random entity you simply do not put a rarity property on it. @@ -104,12 +108,7 @@ function _M:computeRarities(type, list, level, filter, add_level, rarity_field) local r = { total=0 } print("******************", level.level, type) - local lev - if self.level_adjust_level then - lev = self:level_adjust_level(level, self, type) + (add_level or 0) - else - lev = self.base_level + (self.specific_base_level[type] or 0) + (level.level - 1) + (add_level or 0) - end + local lev = self:level_adjust_level(level, self, type) + (add_level or 0) for i, e in ipairs(list) do if e[rarity_field] and e.level_range and (not filter or filter(e)) then @@ -238,7 +237,8 @@ function _M:makeEntity(level, type, filter, force_level, prob_filter) if force_level then resolvers.current_level = force_level end if prob_filter == nil then prob_filter = util.getval(self.default_prob_filter, self, type) end - if filter == nil then filter = util.getval(self.default_filter, self, type) end + if filter == nil then filter = util.getval(self.default_filter, self, level, type) end + if filter and self.alter_filter then filter = util.getval(self.alter_filter, self, level, type, filter) end local e -- No probability list, use the default one and apply filter @@ -311,6 +311,22 @@ function _M:makeEntityByName(level, type, name, force_unique) return e, forced end +local pick_ego = function(self, level, e, egos_list, type, picked_etype, etype, ego_filter) + picked_etype[etype] = true + if _G.type(etype) == "number" then etype = "" end + local egos = level:getEntitiesList(type.."/"..e.egos..":"..etype) + + -- Filter the egos if needed + if ego_filter then + local list = {} + for z = 1, #egos do list[#list+1] = egos[z].e end + egos = self:computeRarities(type, list, level, function(e) return self:checkFilter(e, ego_filter) end, ego_filter.add_levels, ego_filter.special_rarity) + end + egos_list[#egos_list+1] = self:pickEntity(egos) + + if egos_list[#egos_list] then print("Picked ego", type.."/"..e.egos..":"..etype, ":=>", egos_list[#egos_list].name) else print("Picked ego", type.."/"..e.egos..":"..etype, ":=>", #egos_list) end +end + --- Finishes generating an entity function _M:finishEntity(level, type, e, ego_filter) e = e:clone() @@ -327,31 +343,48 @@ function _M:finishEntity(level, type, e, ego_filter) if not e.force_ego then if _G.type(e.egos_chance) == "number" then e.egos_chance = {e.egos_chance} end - -- Pick an ego, then an other and so until we get no more - local chance_decay = 1 - local picked_etype = {} - local etype = e.ego_first_type and e.ego_first_type or rng.tableIndex(e.egos_chance, picked_etype) - local echance = etype and e.egos_chance[etype] - while etype and rng.percent(util.bound(echance / chance_decay + (ego_chance or 0), 0, 100)) do - picked_etype[etype] = true - if _G.type(etype) == "number" then etype = "" end - local egos = level:getEntitiesList(type.."/"..e.egos..":"..etype) - - -- Filter the egos if needed - if ego_filter then - local list = {} - for z = 1, #egos do list[#list+1] = egos[z].e end - egos = self:computeRarities(type, list, level, function(e) return self:checkFilter(e, ego_filter) end, ego_filter.add_levels, ego_filter.special_rarity) + + if not ego_filter or not ego_filter.tries then + -------------------------------------- + -- Natural ego + -------------------------------------- + + -- Pick an ego, then an other and so until we get no more + local chance_decay = 1 + local picked_etype = {} + local etype = e.ego_first_type and e.ego_first_type or rng.tableIndex(e.egos_chance, picked_etype) + local echance = etype and e.egos_chance[etype] + while etype and rng.percent(util.bound(echance / chance_decay + (ego_chance or 0), 0, 100)) do + pick_ego(self, level, e, egos_list, type, picked_etype, etype, ego_filter) + + etype = rng.tableIndex(e.egos_chance, picked_etype) + echance = e.egos_chance[etype] + if e.egos_chance_decay then chance_decay = chance_decay * e.egos_chance_decay end end - egos_list[#egos_list+1] = self:pickEntity(egos) - if egos_list[#egos_list] then print("Picked ego", type.."/"..e.egos..":"..etype, ":=>", egos_list[#egos_list].name) else print("Picked ego", type.."/"..e.egos..":"..etype, ":=>", #egos_list) end + else + -------------------------------------- + -- Semi Natural ego + -------------------------------------- + + -- Pick an ego, then an other and so until we get no more + local picked_etype = {} + for i = 1, #ego_filter.tries do + local try = ego_filter.tries[i] - etype = rng.tableIndex(e.egos_chance, picked_etype) - echance = e.egos_chance[etype] - if e.egos_chance_decay then chance_decay = chance_decay * e.egos_chance_decay end + local etype = (i == 1 and e.ego_first_type and e.ego_first_type) or rng.tableIndex(e.egos_chance, picked_etype) + if not etype then break end + local echance = etype and try[etype] + forceprint("EGO TRY", i, ":", etype, echance, try) + + pick_ego(self, level, e, egos_list, type, picked_etype, etype, try) + end end else + -------------------------------------- + -- Forced ego + -------------------------------------- + local name = e.force_ego if _G.type(name) == "table" then name = rng.table(name) end print("Forcing ego", name) diff --git a/game/engines/default/modules/boot/class/Game.lua b/game/engines/default/modules/boot/class/Game.lua index 9c08653d61..bd9f415182 100644 --- a/game/engines/default/modules/boot/class/Game.lua +++ b/game/engines/default/modules/boot/class/Game.lua @@ -273,7 +273,7 @@ function _M:display(nb_keyframes) end -- Draw it here, inside the FBO - self.flyers:display(nb_keyframes) + if self.flyers then self.flyers:display(nb_keyframes) end -- Display using Framebuffer, so that we can use shaders and all if self.fbo then diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 45c2c12eab..70a9184ed8 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -206,23 +206,7 @@ function _M:useEnergy(val) end -function _M:act() - if not engine.Actor.act(self) then return end - - self.changed = true - - -- If resources are too low, disable sustains - if self.mana < 1 or self.stamina < 1 or self.psi < 1 then - for tid, _ in pairs(self.sustain_talents) do - local t = self:getTalentFromId(tid) - if (t.sustain_mana and self.mana < 1) or (t.sustain_stamina and self.stamina < 1) then - self:forceUseTalent(tid, {ignore_energy=true}) - elseif (t.sustain_psi and self.psi < 1) and t.remove_on_zero then - self:forceUseTalent(tid, {ignore_energy=true}) - end - end - end - +function _M:actTurn() if self:isTalentActive (self.T_DARKEST_LIGHT) and self.positive > self.negative then self:forceUseTalent(self.T_DARKEST_LIGHT, {ignore_energy=true}) game.logSeen(self, "%s's darkness can no longer hold back the light!", self.name:capitalize()) @@ -288,6 +272,40 @@ function _M:act() local t = self:getTalentFromId(self.T_UNSEEN_FORCE) t.do_unseenForce(self, t) end + + -- Suffocate ? + local air_level, air_condition = game.level.map:checkEntity(self.x, self.y, Map.TERRAIN, "air_level"), game.level.map:checkEntity(self.x, self.y, Map.TERRAIN, "air_condition") + if air_level then + if not air_condition or not self.can_breath[air_condition] or self.can_breath[air_condition] <= 0 then + self:suffocate(-air_level, self) + end + end +end + +function _M:act() + if not engine.Actor.act(self) then return end + + self.changed = true + + -- If resources are too low, disable sustains + if self.mana < 1 or self.stamina < 1 or self.psi < 1 then + for tid, _ in pairs(self.sustain_talents) do + local t = self:getTalentFromId(tid) + if (t.sustain_mana and self.mana < 1) or (t.sustain_stamina and self.stamina < 1) then + self:forceUseTalent(tid, {ignore_energy=true}) + elseif (t.sustain_psi and self.psi < 1) and t.remove_on_zero then + self:forceUseTalent(tid, {ignore_energy=true}) + end + end + end + + -- We compute turns at "default" speed, and only fire some actions when chaning turn + local actturn = math.floor(game.turn / 10) + if not self.last_act_turn or self.last_act_turn < actturn then + self:actTurn() + self.last_act_turn = actturn + end + -- Conduit talent prevents all auras from cooling down if self:isTalentActive(self.T_CONDUIT) then local auras = self:isTalentActive(self.T_CONDUIT) @@ -327,14 +345,6 @@ function _M:act() if self:attr("stoned") then self.energy.value = 0 end if self:attr("dazed") then self.energy.value = 0 end - -- Suffocate ? - local air_level, air_condition = game.level.map:checkEntity(self.x, self.y, Map.TERRAIN, "air_level"), game.level.map:checkEntity(self.x, self.y, Map.TERRAIN, "air_condition") - if air_level then - if not air_condition or not self.can_breath[air_condition] or self.can_breath[air_condition] <= 0 then - self:suffocate(-air_level, self) - end - end - -- Regain natural balance? local equilibrium_level = game.level.map:checkEntity(self.x, self.y, Map.TERRAIN, "equilibrium_level") if equilibrium_level then self:incEquilibrium(equilibrium_level) end @@ -382,7 +392,9 @@ function _M:move(x, y, force) else moved = engine.Actor.move(self, x, y, force) end - if not force and moved and (self.x ~= ox or self.y ~= oy) and not self.did_energy then self:useEnergy(game.energy_to_act * self:combatMovementSpeed()) end + if not force and moved and (self.x ~= ox or self.y ~= oy) and not self.did_energy then + self:useEnergy(game.energy_to_act * self:combatMovementSpeed()) + end end self.did_energy = nil @@ -1195,7 +1207,7 @@ function _M:die(src) end if src and src.knowTalent and src:knowTalent(src.T_STEP_UP) and rng.percent(src:getTalentLevelRaw(src.T_STEP_UP) * 20) then - src:setEffect(self.EFF_STEP_UP, 6, {}) + game:onTickEnd(function() src:setEffect(self.EFF_STEP_UP, 1, {}) end) end if self:hasEffect(self.EFF_CORROSIVE_WORM) then diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index f31c0f77d2..d56c700dab 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -333,6 +333,7 @@ function _M:loaded() Zone.check_filter = function(...) return self.state:entityFilter(...) end Zone.default_prob_filter = true Zone.default_filter = function(...) return self.state:defaultEntityFilter(...) end + Zone.alter_filter = function(...) return self.state:entityFilterAlter(...) end Map:setViewerActor(self.player) self:setupDisplayMode(false, "init") if self.player then self.player.changed = true end diff --git a/game/modules/tome/class/GameState.lua b/game/modules/tome/class/GameState.lua index 7123868b27..775b779633 100644 --- a/game/modules/tome/class/GameState.lua +++ b/game/modules/tome/class/GameState.lua @@ -595,9 +595,129 @@ end -- Loot filters -------------------------------------------------------------- +local default_drops = function(zone, level) + if zone.default_drops then return zone.default_drops end + local lev = util.bound(math.ceil(zone:level_adjust_level(level, "object") / 10), 1, 5) + return ({ + [1] = { + uniques = 0.5, + double_greater = 2, + greater_normal = 4, + greater = 12, + double_ego = 25, + ego = 45, + basic = 35, + }, + [2] = { + uniques = 0.7, + double_greater = 6, + greater_normal = 10, + greater = 20, + double_ego = 45, + ego = 45, + basic = 35, + }, + [3] = { + uniques = 1, + double_greater = 15, + greater_normal = 20, + greater = 30, + double_ego = 30, + ego = 20, + basic = 25, + }, + [4] = { + uniques = 1.1, + double_greater = 19, + greater_normal = 25, + greater = 30, + double_ego = 25, + ego = 15, + basic = 15, + }, + [5] = { + uniques = 1.2, + double_greater = 25, + greater_normal = 20, + greater = 24, + double_ego = 15, + ego = 10, + basic = 5, + }, + })[lev] +end + function _M:defaultEntityFilter(zone, level, type) + if type ~= "object" then return end + -- By default we dont apply special filters, but we always provide one so that entityFilter is called - return {} + return { + tome = default_drops(zone, level), + } +end + +function _M:entityFilterAlter(zone, level, type, filter) + if type ~= "object" then return end + + if not filter.tome then filter.tome = default_drops(zone, level) end + + if filter.tome then + local t = (filter.tome == true) and default_drops(zone, level) or filter.tome + filter.tome = nil + + local u, dg, ge, g, de, e, total = + t.uniques, + t.uniques + t.double_greater, + t.uniques + t.double_greater + t.greater_normal, + t.uniques + t.double_greater + t.greater_normal + t.greater, + t.uniques + t.double_greater + t.greater_normal + t.greater + t.double_ego, + t.uniques + t.double_greater + t.greater_normal + t.greater + t.double_ego + t.ego, + t.uniques + t.double_greater + t.greater_normal + t.greater + t.double_ego + t.ego + t.basic + + local r = rng.float(0, total) + if r < u then + print("[TOME ENTITY FILTER] selected Uniques", r, u) + filter.unique = true + + elseif r < dg then + print("[TOME ENTITY FILTER] selected Double Greater", r, dg) + filter.not_properties = filter.not_properties or {} + filter.not_properties[#filter.not_properties+1] = "unique" + filter.ego_chance={tries = { {ego_chance=100, properties={"greater_ego"}}, {ego_chance=100, properties={"greater_ego"}} } } + + elseif r < ge then + print("[TOME ENTITY FILTER] selected Greater + Ego", r, ge) + filter.not_properties = filter.not_properties or {} + filter.not_properties[#filter.not_properties+1] = "unique" + filter.ego_chance={tries = { {ego_chance=100, properties={"greater_ego"}}, {ego_chance=100, not_properties={"greater_ego"}} }} + + elseif r < g then + print("[TOME ENTITY FILTER] selected Greater", r, g) + filter.not_properties = filter.not_properties or {} + filter.not_properties[#filter.not_properties+1] = "unique" + filter.ego_chance={tries = { ego_chance=100, properties={"greater_ego"} } } + + elseif r < de then + print("[TOME ENTITY FILTER] selected Double Ego", r, de) + filter.not_properties = filter.not_properties or {} + filter.not_properties[#filter.not_properties+1] = "unique" + filter.ego_chance={tries = { {ego_chance=100, not_properties={"greater_ego"}}, {ego_chance=100, not_properties={"greater_ego"}} }} + + elseif r < e then + print("[TOME ENTITY FILTER] selected Ego", r, e) + filter.not_properties = filter.not_properties or {} + filter.not_properties[#filter.not_properties+1] = "unique" + filter.ego_chance={tries = { ego_chance=100, not_properties={"greater_ego"} } } + else + print("[TOME ENTITY FILTER] selected basic", r, total) + filter.not_properties = filter.not_properties or {} + filter.not_properties[#filter.not_properties+1] = "unique" + filter.ego_chance = -1000 + end + end + + -- By default we dont apply special filters, but we always provide one so that entityFilter is called + return filter end function _M:entityFilter(zone, e, filter, type) @@ -608,6 +728,7 @@ function _M:entityFilter(zone, e, filter, type) if not e.material_level then return true end if e.material_level < min_mlvl then return false end end + if max_mlvl then if not e.material_level then return true end if e.material_level > max_mlvl then return false end diff --git a/game/modules/tome/class/interface/Combat.lua b/game/modules/tome/class/interface/Combat.lua index b6b3efb9d9..b41bdcafb7 100644 --- a/game/modules/tome/class/interface/Combat.lua +++ b/game/modules/tome/class/interface/Combat.lua @@ -738,7 +738,7 @@ end --- Computes movement speed function _M:combatMovementSpeed() - return 1 + (self.movement_speed or 0) + return util.bound(1 + (self.movement_speed or 0), 0.2, 10) end --- Check if the actor has a gem bomb in quiver diff --git a/game/modules/tome/data/talents/gifts/storm-drake.lua b/game/modules/tome/data/talents/gifts/storm-drake.lua index c58a0d78fa..0775c974ef 100644 --- a/game/modules/tome/data/talents/gifts/storm-drake.lua +++ b/game/modules/tome/data/talents/gifts/storm-drake.lua @@ -30,13 +30,14 @@ newTalent{ tactical = { CLOSEIN = 2, ESCAPE = 2 }, requires_target = true, action = function(self, t) - self:setEffect(self.EFF_LIGHTNING_SPEED, math.ceil(6 + self:getTalentLevel(t) * 1.2), {power=400 + self:getTalentLevel(t) * 70}) + self:setEffect(self.EFF_LIGHTNING_SPEED, math.ceil(1 + self:getTalentLevel(t) * 0.3), {power=400 + self:getTalentLevel(t) * 70}) return true end, info = function(self, t) - return ([[You transform into pure lightning, moving %d%% faster for %d turns. + return ([[You transform into pure lightning, moving %d%% faster for %d game turns. Also provides 30%% physical damage resistance and 100%% lightning resistance. - Any actions other than moving will stop this effect.]]):format(400 + self:getTalentLevel(t) * 70, math.ceil(6 + self:getTalentLevel(t) * 1.2)) + Any actions other than moving will stop this effect. + Note: since you will be moving very fast, game turns will pass very slowly.]]):format(400 + self:getTalentLevel(t) * 70, math.ceil(1 + self:getTalentLevel(t) * 0.3)) end, } diff --git a/game/modules/tome/data/talents/misc/inscriptions.lua b/game/modules/tome/data/talents/misc/inscriptions.lua index f599ee7566..c3bb0bafb8 100644 --- a/game/modules/tome/data/talents/misc/inscriptions.lua +++ b/game/modules/tome/data/talents/misc/inscriptions.lua @@ -150,18 +150,19 @@ newInscription{ action = function(self, t) local data = self:getInscriptionData(t.short_name) self:setEffect(self.EFF_FREE_ACTION, data.dur, {power=1}) - game:onTickEnd(function() self:setEffect(self.EFF_WILD_SPEED, data.dur, {power=data.speed + data.inc_stat}) end) + game:onTickEnd(function() self:setEffect(self.EFF_WILD_SPEED, 1, {power=data.speed + data.inc_stat}) end) return true end, info = function(self, t) local data = self:getInscriptionData(t.short_name) - return ([[Activate the infusion to increase movement speed by %d%% for %d turns. + return ([[Activate the infusion to increase movement speed by %d%% for 1 game turn. Any actions other than movement will cancel the effect. - Also prevent stuns, dazes and pinning effects.]]):format(data.speed + data.inc_stat, data.dur) + Also prevent stuns, dazes and pinning effects for %d turns. + Note: since you will be moving very fast, game turns will pass very slowly.]]):format(data.speed + data.inc_stat, data.dur) end, short_info = function(self, t) local data = self:getInscriptionData(t.short_name) - return ([[%d%% speed; %d turns]]):format(data.speed, data.dur + data.inc_stat) + return ([[%d%% speed; %d turns]]):format(data.speed + data.inc_stat, data.dur) end, } @@ -594,6 +595,9 @@ newInscription{ points = 1, is_spell = true, tactical = { MANA = 1 }, + on_pre_use = function(self, t) + return self:knowTalent(self.T_MANA_POOL) + end, action = function(self, t) local data = self:getInscriptionData(t.short_name) self:incMana((data.mana + data.inc_stat) / 20) diff --git a/game/modules/tome/data/talents/techniques/battle-tactics.lua b/game/modules/tome/data/talents/techniques/battle-tactics.lua index 3e76719c3a..db3cd0f94a 100644 --- a/game/modules/tome/data/talents/techniques/battle-tactics.lua +++ b/game/modules/tome/data/talents/techniques/battle-tactics.lua @@ -45,8 +45,9 @@ newTalent{ mode = "passive", points = 5, info = function(self, t) - return ([[After killing a foe you have %d%% chances to gain a 1000%% movement speed bonus for 6 turns. - The bonus disappears as soon as any action other than moving is done.]]):format(self:getTalentLevelRaw(t) * 20) + return ([[After killing a foe you have %d%% chances to gain a 1000%% movement speed bonus for 1 game turns. + The bonus disappears as soon as any action other than moving is done. + Note: since you will be moving very fast, game turns will pass very slowly.]]):format(self:getTalentLevelRaw(t) * 20) end, } diff --git a/game/modules/tome/data/zones/daikara/zone.lua b/game/modules/tome/data/zones/daikara/zone.lua index 3831dce069..1401ef29a7 100644 --- a/game/modules/tome/data/zones/daikara/zone.lua +++ b/game/modules/tome/data/zones/daikara/zone.lua @@ -53,7 +53,7 @@ return { }, object = { class = "engine.generator.object.Random", - nb_object = {6, 9}, + nb_object = {60, 90}, filters = { {} } }, trap = { diff --git a/game/modules/tome/data/zones/deep-bellow/zone.lua b/game/modules/tome/data/zones/deep-bellow/zone.lua index ecc5be041d..71a2ec0c0f 100644 --- a/game/modules/tome/data/zones/deep-bellow/zone.lua +++ b/game/modules/tome/data/zones/deep-bellow/zone.lua @@ -29,7 +29,7 @@ return { all_lited = true, persistent = "zone", ambient_music = "Straight Into Ambush.ogg", - max_material_level = 24, + max_material_level = 2, generator = { map = { class = "engine.generator.map.Cavern", -- GitLab