Advertisement
Xetrill

xs_transmutation.script

Aug 17th, 2013
234
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 24.52 KB | None | 0 0
  1. --[[-----------------------------------------------------------------------\\--
  2.     :: PRIVATE ::
  3. --\\-----------------------------------------------------------------------]]--
  4.  
  5. local tonumber, pairs, ipairs
  6.     = tonumber, pairs, ipairs
  7. local translate, get_nearest_anomaly
  8.     = game.translate_string, anomaly_holder.get_nearest_anomaly
  9. local insert, remove, rand
  10.     = table.insert, table.remove, math.random
  11. local map, min, find, check, vec2tbl, tbl2vec
  12.     = xs_utils.map, xs_utils.min, xs_utils.find, xs_utils.check, xs_utils.vec2tbl, xs_utils.tbl2vec
  13. local getScheduleManager, getSaveManager
  14.     = xs_timers.getScheduleManager, xs_save.getSaveManager
  15.  
  16. local TM_TIMER_PREFIX = "xs_at_" -- at for artifact transmutation
  17.  
  18. -- use "treasure" instead or add a level_map to "artifact_indicator".
  19. local TM_MAPSPOT_TYPE = xs_game.IS_SOC and "artefact_location" or "artifact_indicator"
  20.  
  21. -- TODO: make a custom PPE something like teleport but much faster/shorter.
  22. local TM_VFX_PPE = "teleport.ppe"
  23. local TM_VFX_UID = 2006
  24. -- TODO: add a sound to accompany the visual effect.
  25. local TM_SFX_SND = nil
  26.  
  27. local m_tm
  28.  
  29. -- TODO: Get rid of the next line, use something more general instead!
  30. local log = xs_game.IS_SOC and _G.log or _G.error_log
  31.  
  32.  
  33. --[[-----------------------------------------------------------------------\\--
  34.     :: PUBLIC ::
  35. --\\-----------------------------------------------------------------------]]--
  36.  
  37. __FILE__ = "xs_transmutation"
  38.  
  39. -- TODO: add feature to have multiple result sections?
  40. class "TransmutationManager" (XsObj)
  41.  
  42.     --[[-------------------------------------------------------------------\\--
  43.         :: Constructor/Destructor
  44.     --\\-------------------------------------------------------------------]]--
  45.  
  46.     function TransmutationManager:__init() super("TransmutationManager")
  47.         -- recipe infos
  48.         self.recipes = {
  49.             -- [1] = {
  50.             -- READ-FROM-LTX:
  51.             --  anomaly = string:  a sub-string identifying the required anomaly section,
  52.             --  source  = string:  the artifact source section,
  53.             --  recipe  = string:  the info section required to attempt a trans.,
  54.             --  result  = string:  the artifact to be spawned on a successful trans.,
  55.             --  chance  = table:   the success and failure rates,
  56.             --  delay   = number:  the time (in hours) until a trans. is finished,
  57.             -- OPTIONAL:
  58.             -- failure_artifact = string: if specified overturns the default failure-artifact,
  59.             -- experiment_malus = number: if specified overturns the default experimentation malus,
  60.             -- COMPUTED:
  61.             --  id    = number:  a unique-id one per transmutation-set; valid range 1-math.huge,
  62.             --  parent  = number:  the id of the parent of this transmutation,
  63.             --  complex = boolean: true if there are multiple sources,
  64.             -- }
  65.         }
  66.         -- active/pending transmutations (this table is persisted!)
  67.         self.pending = {
  68.             -- [1] = {
  69.             --  id   = number: timer or artifact id,
  70.             --  sid  = number: recipe source id (self.recipes[#].id),
  71.             --  gvid = number: spawn game vertex id,
  72.             --  lvid = number: spawn level vertex id,
  73.             --  pos  = table:  spawn position,
  74.             -- }
  75.         }
  76.         -- lookup table of source artifact sections; for faster execution
  77.         self.sources = {}
  78.         -- table to store temporary data when attempting a complex transmutation
  79.         self.complex = nil
  80.             -- {
  81.             --   id     = number:     recipe.id,
  82.             --   anomaly   = game_object: anomaly,
  83.             --   artifacts = table:    list of artifacts inside the anomaly,
  84.             --   numleft   = number:      how many more artifacts need to be thrown into the anomaly,
  85.             --   recipes   = table:    list of self.recipes entries
  86.             -- }
  87.  
  88.         local sprintf
  89.             = string.format
  90.         local trim, split, count, retain
  91.             = xs_utils.trim, xs_utils.split, xs_utils.count, xs_utils.retain
  92.         local max, clamp
  93.             = xs_utils.max, xs_utils.clamp
  94.         local trim2number = function (v, k)
  95.             return tonumber(trim(v))
  96.         end
  97.  
  98.         local INIS_TRANSMUTATIONS   = "transmutation"
  99.  
  100.         local INIP_SECTIONS         = "sections"
  101.         local INIP_SECTIONPREFIX    = "section_prefix"
  102.         local INIP_TIMESPAN         = "timespan"
  103.         local INIP_PREFIX           = "prefix"
  104.  
  105.         local INIP_FAILURE_ARTIFACT = "failure_artifact"
  106.         local INIP_EXPERIMENT_MALUS = "experiment_malus"
  107.  
  108.         local INIP_ANOMALY          = "anomaly"
  109.         local INIP_SOURCE           = "source"
  110.         local INIP_INFO             = "recipe"
  111.         local INIP_RESULT           = "result"
  112.         local INIP_CHANCE           = "chance"
  113.         local INIP_DELAY            = "delay"
  114.  
  115.         local path = [[scripts\xs_transmutation.ltx]]
  116.         local ini  = ini_file(path)
  117.  
  118.         check(ini:section_exist(INIS_TRANSMUTATIONS),
  119.               "[TransmutationManager]: Missing section '%s'.",
  120.               INIS_TRANSMUTATIONS)
  121.  
  122.         -- timespan
  123.         local timespan = map(split(',', ini:r_string(INIS_TRANSMUTATIONS, INIP_TIMESPAN)), trim2number, ipairs)
  124.  
  125.         check(#timespan == 2,
  126.               "[TransmutationManager]: Invalid property '%s' in section '%s'.",
  127.               INIP_TIMESPAN, INIS_TRANSMUTATIONS)
  128.  
  129.         self.timespan = {
  130.             min = min(timespan, nil, ipairs),
  131.             max = max(timespan, nil, ipairs)
  132.         }
  133.  
  134.         -- sections
  135.         local sections = {}
  136.         if xs_game.IS_SOC then
  137.             sections = map(split(',', ini:r_string(INIS_TRANSMUTATIONS, INIP_SECTIONS) or '', trim))
  138.         else
  139.             local prefix = ini:r_string(INIS_TRANSMUTATIONS, INIP_SECTIONPREFIX)
  140.  
  141.             check(prefix:len() > 0,
  142.                   "[TransmutationManager]: Invalid property '%s' in section '%s'.",
  143.                   INIP_PREFIX, INIS_TRANSMUTATIONS)
  144.  
  145.             sections = retain(
  146.                 xs_fs.listSections(xs_fs.gamePath('$game_config$', path)),
  147.                 function (v, k)
  148.                     return v:find(prefix, 1, true) == 1
  149.                 end,
  150.                 ipairs)
  151.         end
  152.  
  153.         -- Note: Log instead of crash here?
  154.         check(#sections > 0,
  155.               "[TransmutationManager]: No transmutation sections defined.")
  156.  
  157.         -- failure_artifact
  158.         -- The default artifact spawned in case of an failed transmutation attempt.
  159.         self.failure_artifact = ini:r_string(INIS_TRANSMUTATIONS, INIP_FAILURE_ARTIFACT)
  160.         check(self.failure_artifact:len() > 0,
  161.               "[TransmutationManager]: Invalid property '%s' in section '%s'.",
  162.               INIP_FAILURE_ARTIFACT, INIS_TRANSMUTATIONS)
  163.  
  164.         -- experiment_malus
  165.         -- The malus applied in case of performing an unknown transmutation.
  166.         -- To disable this feature set the value to 1.0.
  167.         self.experiment_malus = clamp(ini:r_float(INIS_TRANSMUTATIONS, INIP_EXPERIMENT_MALUS), 0.0, 1.0)
  168.  
  169.         -- now on to the real meat
  170.         local id = 0
  171.         for _, section in pairs(sections) do
  172.             repeat
  173.                 id = id + 1
  174.  
  175.                 if not ini:section_exist(section) then
  176.                     log(sprintf(
  177.                         "[TransmutationManager]: Section '%s' not found.",
  178.                         section))
  179.                     break
  180.                 end
  181.  
  182.                 -- required properties
  183.                 local anomaly = ini:r_string(section, INIP_ANOMALY)
  184.                 local source  = map(split(',', ini:r_string(section, INIP_SOURCE)), trim, ipairs)
  185.                 local recipe  = ini:r_string(section, INIP_INFO)
  186.                 local result  = ini:r_string(section, INIP_RESULT)
  187.                 local chance  = map(split(',', ini:r_string(section, INIP_CHANCE)), trim2number, ipairs)
  188.                 local delay   = ini:r_s32(section, INIP_DELAY)
  189.  
  190.                 -- TODO: give a better (read accurate) error.
  191.                 if not anomaly or
  192.                    not source or
  193.                    not recipe or
  194.                    not result or
  195.                    not chance or
  196.                    not delay then
  197.                     log(sprintf(
  198.                         "[TransmutationManager]: Invalid transmutation section '%s'.",
  199.                          section))
  200.                     break
  201.                 end
  202.  
  203.                 -- optional, can be nil
  204.                 local failure_artifact, experiment_malus
  205.                 if ini:line_exist(section, INIP_FAILURE_ARTIFACT) then
  206.                     failure_artifact = ini:r_string(section, INIP_FAILURE_ARTIFACT)
  207.  
  208.                     check(failure_artifact:len() > 0,
  209.                           "[TransmutationManager]: Invalid property '%s' in section '%s'.",
  210.                           INIP_EXPERIMENT_MALUS, section)
  211.                 end
  212.                 if ini:line_exist(section, INIP_EXPERIMENT_MALUS) then
  213.                     experiment_malus = clamp(ini:r_float(section, INIP_EXPERIMENT_MALUS), 0.0, 1.0)
  214.  
  215.                     -- no need if its the same as the base value; conserve resources man.
  216.                     if experiment_malus == self.experiment_malus then
  217.                         experiment_malus = nil
  218.                     end
  219.                 end
  220.  
  221.                 -- check for duplicate results
  222.                 local index = find(self.recipes, result, function (v, k) return v.result end, ipairs)
  223.                 if index then
  224.                     log(sprintf(
  225.                         "[TransmutationManager]: Duplicate transmutation %s '%s' in section '%s'.",
  226.                          INIP_RESULT, result, section))
  227.                     break
  228.                 end
  229.  
  230.                 -- check for duplicate sources
  231.                 index = find(self.recipes, source, function (v, k) return v.source end, ipairs)
  232.                 if index then
  233.                     log(sprintf(
  234.                         "[TransmutationManager]: Duplicate transmutation %s '%s' in section '%s'.",
  235.                          INIP_SOURCE, source, section))
  236.                     break
  237.                 end
  238.  
  239.                 if #source == 1 then
  240.                     source = source[1]
  241.                 end
  242.  
  243.                 if type(source) == "table" then
  244.                     for _, source in ipairs(source) do
  245.                         insert(self.recipes, {
  246.                             -- computed properties
  247.                             id    = id,
  248.                             parent  = 0,
  249.                             complex = true,
  250.                             -- parsed properties
  251.                             anomaly = anomaly,
  252.                             recipe  = recipe,
  253.                             result  = result,
  254.                             chance  = chance,
  255.                             delay   = delay,
  256.                             source  = source,
  257.                             -- optional properties (its a table, so nil's wont be stored)
  258.                             failure_artifact = failure_artifact,
  259.                             experiment_malus = experiment_malus,
  260.                         })
  261.                         self.sources[source] = true
  262.                     end
  263.                 else
  264.                     insert(self.recipes, {
  265.                         -- computed properties
  266.                         id    = id,
  267.                         parent  = 0,
  268.                         complex = false,
  269.                         -- parsed properties
  270.                         anomaly = anomaly,
  271.                         recipe  = recipe,
  272.                         result  = result,
  273.                         chance  = chance,
  274.                         delay   = delay,
  275.                         source  = source,
  276.                         -- optional properties
  277.                         failure_artifact = failure_artifact,
  278.                         experiment_malus = experiment_malus,
  279.                     })
  280.                     self.sources[source] = true
  281.                 end
  282.             until true
  283.         end
  284.  
  285.         -- assign parent id's -- Who's your daddy, hmm?
  286.         for _, child in ipairs(self.recipes) do
  287.             -- complex recipes don't have a single parent... this doesn't map well.
  288.             if child.complex then
  289.                 child.parent = 0
  290.             else
  291.                 local parent = self:getRecipeByResult(child.source)
  292.                 child.parent = parent and parent.id or 0
  293.             end
  294.         end
  295.         self.cc_getRecipeBySource = xs_cache.ComputeCache()
  296.  
  297.         getSaveManager():Attach(self)
  298.  
  299.         local hm, ev = getHookMechanics()
  300.         hm:Bind(ev.ItemTake, _G.db.actor, self, _G.isArtifact)
  301.         hm:Bind(ev.ItemDrop, _G.db.actor, self, _G.isArtifact)
  302.     end
  303.  
  304.     --[[-------------------------------------------------------------------\\--
  305.         :: Methods: Overloads
  306.     --\\-------------------------------------------------------------------]]--
  307.  
  308.     function TransmutationManager:__tostring()
  309.         if #self.pending == 0 then
  310.             string.format("%s [%08X] (#0)\n", self.__cls, self.__uid)
  311.         end
  312.  
  313.         local buffer = xs_text.StringBuilder()
  314.         buffer:AppendFormat("%s [%08X] (#%d)\n", self.__cls, self.__uid, #self.pending)
  315.         for i, t in ipairs(self.pending) do
  316.             buffer:AppendLine("[", i, "] = id: ", t.id, ", sid: ", t.sid, "")
  317.         end
  318.         return buffer:ToString()
  319.     end
  320.  
  321.     --[[-------------------------------------------------------------------\\--
  322.         :: Methods: Private
  323.     --\\-------------------------------------------------------------------]]--
  324.  
  325.     function TransmutationManager:getNearsetAnomaly(origin)
  326.         local status, id, pos, cls, dist, rad, sect = get_nearest_anomaly(db.actor)
  327.         if status then
  328.             return {
  329.                 id     = id,
  330.                 position = position,
  331.                 class   = cls,
  332.                 distance = dist,
  333.                 radius   = rad,
  334.                 section  = sect
  335.             }
  336.         else
  337.             return nil
  338.         end
  339.     end
  340.  
  341.     function TransmutationManager:createTimerName(id)
  342.         return TM_TIMER_PREFIX .. tostring(id)
  343.     end
  344.  
  345.     function TransmutationManager:getTransmutationIndexByID(id)
  346.         return find(self.pending, id, function (v, k) return v.id end, ipairs) or -1
  347.     end
  348.  
  349.     function TransmutationManager:getRecipeProperty(recipe, property, default)
  350.         return recipe[property] or self[property] or default
  351.     end
  352.  
  353.     function TransmutationManager:getRecipeByResult(section)
  354.         for _, recipe in ipairs(self.recipes) do
  355.             if recipe.result == section then
  356.                 return recipe
  357.             end
  358.         end
  359.         return nil
  360.     end
  361.  
  362.     function TransmutationManager:getRecipeBySource(section)
  363.         local cc = self.cc_getRecipeBySource
  364.         if cc:IsInvalid(section) then
  365.             cc:Update(section)
  366.             cc:SetResult(nil)
  367.             for _, recipe in ipairs(self.recipes) do
  368.                 if recipe.source == section then
  369.                     cc:SetResult(recipe)
  370.                     break
  371.                 end
  372.             end
  373.         end
  374.         return cc:GetResult()
  375.     end
  376.  
  377.     function TransmutationManager:getFinalArtifact(recipe)
  378.         local chance = recipe.chance
  379.         local result = rand(0, 100)
  380.  
  381.         if recipe.recipe and db.actor:dont_has_info(0, recipe.recipe) then
  382.             local malus = self:getRecipeProperty(recipe, INIP_EXPERIMENT_MALUS)
  383.             map(chance, function (v, k) return math.floor(v * malus) end, ipairs)
  384.         end
  385.  
  386.         -- 2012-07-02 17:39: ~Xetrill: Every time I look at this code, I think this needs refactoring,
  387.         -- too complicated for what it should (it's untested) accomplish.
  388.  
  389.         -- success or failure?
  390.         if result >= chance[0] then
  391.             -- success!
  392.             return recipe.result
  393.         else
  394.             -- failure?
  395.             if result < (chance[1] or 100 - chance[0]) then
  396.                 -- no-change or downgrade?
  397.                 if recipe.parent > 0 and result < (chance[2] or rand(5, 20)) then
  398.                     if recipe.complex then
  399.                         local artifacts = {}
  400.                         local recipes = self:getAllRecipesByID(recipe.id)
  401.                         local chance = math.max(5, math.ceil(chance[0] / #recipes) - math.ceil(#recipes * 1.5))
  402.                         for _, recipe in ipairs(recipes) do
  403.                             if rand(0, 100) > chance then
  404.                                 insert(recipe.source)
  405.                             end
  406.                         end
  407.                         return artifacts
  408.                     else
  409.                         -- *phew* no-change
  410.                         return recipe.source
  411.                     end
  412.                 else
  413.                     -- *mah* downgrade
  414.                     return self.recipes[recipe.parent].source
  415.                 end
  416.             else
  417.                 -- *aww shit* destruction!
  418.                 return self:getRecipeProperty(recipe, INIP_FAILURE_ARTIFACT)
  419.             end
  420.         end
  421.     end
  422.  
  423.     function TransmutationManager:getGameVertexID(artifact, anomaly)
  424.         local id
  425.  
  426.         id = level.object_by_id(anomaly:id()):game_vertex_id()
  427.         if id ~= 65535 then
  428.             return id
  429.         end
  430.  
  431.         id = artifact:game_vertex_id()
  432.         if id ~= 65535 then
  433.             return id
  434.         end
  435.  
  436.         id = db.actor:game_vertex_id()
  437.         if id ~= 65535 then
  438.             return id
  439.         end
  440.  
  441.         return -1
  442.     end
  443.  
  444.     function TransmutationManager:getLevelVertexID(artifact, anomaly)
  445.         return db.actor and db.actor:level_vertex_id() or -1
  446.     end
  447.  
  448.     -- 2012-07-02 17:37: ~Xetrill: I wrote that before I knew whether it was needed and also
  449.     -- I didn't know by how much 'scrambling' would be good.
  450.     -- (I still don't know.)
  451.     -- 2012-07-16 18:21: ~Xetrill: And now there aren't any vectors anymore, just good ol' tables
  452.     function TransmutationManager:scrambleVector(v)
  453.         -- local x, y, z = v.x, v.y, v.z
  454.         -- v:set(self:scrambleNumber(x, 2), self:scrambleNumber(y, 2), self:scrambleNumber(z, 1))
  455.         v.x = self:scrambleNumber(x, 2.0)
  456.         v.y = self:scrambleNumber(y, 2.0)
  457.         v.z = self:scrambleNumber(z, 1.0)
  458.         return v
  459.     end
  460.  
  461.     function TransmutationManager:scrambleNumber(n, range)
  462.         return n + rand(0, range)
  463.     end
  464.  
  465.     function TransmutationManager:getAllRecipesByID(id)
  466.         local recipes = {}
  467.         for _, recipe in ipairs(self.recipes) do
  468.             if recipe.id == id then
  469.                 insert(recipes, recipe)
  470.             end
  471.         end
  472.         return recipes
  473.     end
  474.  
  475.     function TransmutationManager:canBeginTransmutation(artifact, section, anomaly)
  476.         if anomaly.radius - anomaly.distance < 0 then
  477.             return false
  478.         end
  479.  
  480.         local h = level.get_time_hours()
  481.         if h > self.timespan.min or h < self.timespan.max then
  482.             return false
  483.         end
  484.  
  485.         local recipe = self:getRecipeBySource(section)
  486.         if anomaly:section():find(recipe.anomaly, 1, true) == nil then
  487.             return false
  488.         end
  489.  
  490.         if self.experiment_malus >= 1.0 and db.actor:dont_has_info(0, recipe.recipe) then
  491.             return false
  492.         end
  493.  
  494.         if not recipe.complex then
  495.             self.complex = nil
  496.             return true
  497.         end
  498.  
  499.         -- from here on its all about complex transmutations.
  500.         if self.complex ~= nil and self.complex.id == recipe.id then
  501.             -- CONTINUE a complex transmutation
  502.             local duplicate = false
  503.             for _, previousArtifact in ipairs(self.complex.artifacts) do
  504.                 if previousArtifact:section() == section then
  505.                     duplicate = true
  506.                     break
  507.                 end
  508.             end
  509.             if duplicate then
  510.                 return false
  511.             end
  512.             insert(self.complex.artifacts, artifact)
  513.  
  514.             self.complex.numleft = #self.complex.recipes - #self.complex.artifacts
  515.  
  516.             return self.complex.numleft == 0
  517.         else
  518.             self.complex = nil
  519.         end
  520.  
  521.         if self.complex == nil then
  522.             -- begin a NEW complex transmutation
  523.             local recipes = self:getAllRecipesByID(recipe.id)
  524.             self.complex = {
  525.                 id      = recipe.id,
  526.                 anomaly   = anomaly,
  527.                 artifacts = { artifact },
  528.                 numleft   = #recipes - 1,
  529.                 recipes   = recipes,
  530.             }
  531.         end
  532.  
  533.         return false
  534.     end
  535.  
  536.     function TransmutationManager:beginTransmutation(artifact, anomaly)
  537.         local section = artifact:section()
  538.         local recipe  = self:getRecipeBySource(section)
  539.         local id      = artifact:id()
  540.         local gvid  = self:getGameVertexID(artifact, anomaly)
  541.         local lvid  = self:getLevelVertexID(artifact, anomaly)
  542.         local timeout = time_global() + recipe.delay * 60
  543.  
  544.         if gvid < 0 or lvid < 0 then
  545.             log("[TransmutationManager:beginTransmutation]: Could not get a proper game-vertex-id or level-vertex-id.")
  546.             return
  547.         end
  548.  
  549.         insert(self.pending, {
  550.             id   = id,
  551.             sid  = recipe.id,
  552.             lvid = lvid,
  553.             gvid = gvid,
  554.             pos  = vec2tbl(artifact:position()),
  555.         })
  556.  
  557.         level.add_pp_effector(TM_VFX_PPE, TM_VFX_UID, false)
  558.         if TM_SFX_SND ~= nil then
  559.             local sfx
  560.             if xs_game.IS_SOC then
  561.                 sfx = xr_sound.get_safe_sound_object(TM_SFX_SND)
  562.             else
  563.                 sfx = sound_object(TM_SFX_SND)
  564.             end
  565.             sfx:play_no_feedback(db.actor, sound_object.s3d, 0, artifact:position(), 1.0)
  566.         end
  567.  
  568.         if self.complex ~= nil then
  569.             for _, artifact in ipairs(self.complex.artifacts) do
  570.                 alife():release(artifact, true)
  571.             end
  572.             self.complex = nil
  573.         else
  574.             alife():release(artifact, true)
  575.         end
  576.  
  577.         getScheduleManager():Add(
  578.             self:createTimerName(id),
  579.             timeout,
  580.             xs_utils.createCallback(__FILE__, "finishTransmutationCallback"))
  581.     end
  582.  
  583.     function TransmutationManager:finishTransmutation(id)
  584.         local index = self:getTransmutationIndexByID(id)
  585.         if index < 0 then
  586.             -- TODO: to crash or not to crash? IMO, in release no crash, until then ...
  587.             abort("[TransmutationManager:finishTransmutation]: Invalid transmutation id #%s.",
  588.                   tostring(id))
  589.         end
  590.  
  591.         local trans  = self.pending[index]
  592.         local result = self:getFinalArtifact(trans)
  593.  
  594.         local sim, aid  = alife(), db.actor:id()
  595.         local artifacts = {}
  596.  
  597.         if type(result) == "table" then
  598.             for i, result in ipairs(result) do
  599.                 insert(artifacts, sim:create(
  600.                     result,
  601.                     tlb2vec(trans.pos),
  602.                     trans.lvid,
  603.                     trans.gvid,
  604.                     aid))
  605.             end
  606.         else
  607.             insert(artifacts, sim:create(
  608.                 result,
  609.                 tlb2vec(trans.pos),
  610.                 trans.lvid,
  611.                 trans.gvid,
  612.                 aid))
  613.         end
  614.  
  615.         local name = translate(system_ini():r_string(result, "inv_name"))
  616.         local id = artifacts[1]:id()
  617.         if not level.map_has_object_spot(id, TM_MAPSPOT_TYPE) then
  618.             level.map_add_object_spot(
  619.                 id,
  620.                 TM_MAPSPOT_TYPE,
  621.                 translate("xs_tm_mapspot_caption_format"):format(name))
  622.         end
  623.  
  624.         xs_game.SendMessage(
  625.             not xs_game.IS_SOC and translate("xs_tm_notification_header") or nil,
  626.             translate("xs_tm_notification_format"):format(name),
  627.             "ui_iconsTotal_recover_item",
  628.             nil, nil, nil)
  629.     end
  630.  
  631.     --[[-------------------------------------------------------------------\\--
  632.         :: Methods: Public
  633.     --\\-------------------------------------------------------------------]]--
  634.  
  635.     function TransmutationManager:OnSave(sm)
  636.         getSaveManager():Update(self.__cls, self.pending)
  637.     end
  638.  
  639.     function TransmutationManager:OnLoad(sm)
  640.         self.pending = getSaveManager():Request(self.__cls) or {}
  641.     end
  642.  
  643.     function TransmutationManager:OnDropItem(artifact)
  644.         local section = artifact:section()
  645.         if not self.sources[section] then
  646.             return false
  647.         end
  648.  
  649.         local anomaly = self:getNearsetAnomaly(artifact)
  650.         if not anomaly then
  651.             return false
  652.         end
  653.  
  654.         if not self:canBeginTransmutation(artifact, section, anomaly) then
  655.             return false
  656.         end
  657.  
  658.         self:beginTransmutation(
  659.             artifact,
  660.             level.object_by_id(anomaly.id)
  661.             -- alife():object(anomaly.id)
  662.             -- db.storage[anomaly.id].object
  663.         )
  664.         return true
  665.     end
  666.  
  667.     function TransmutationManager:OnTakeItem(artifact)
  668.         local id    = artifact:id()
  669.         local index = self:getTransmutationIndexByID(id)
  670.  
  671.         if index < 0 then
  672.             log("[TransmutationManager:OnTakeItem]: Invalid transmutation id #%.0f.", id)
  673.             return false
  674.         end
  675.  
  676.         if self.experiment_malus ~= 1.0 then
  677.             local actor  = db.actor
  678.             local recipe = self.recipes[self.pending[index].sid]
  679.             if recipe and
  680.                recipe.recipe and
  681.                recipe.result == artifact:section() and
  682.                actor:dont_has_info(0, recipe.recipe)
  683.             then
  684.                 -- 2012-07-02 17:24: ~Xetrill: this could be improved by giving specialized infos, ones
  685.                 -- that show a message like "I discoved how to create artifact X by ...".
  686.                 actor:give_info_portion(recipe.recipe)
  687.  
  688.                 local name = translate(system_ini():r_string(artifact:section(), "inv_name"))
  689.                 xs_game.SendMessage(
  690.                     not xs_game.IS_SOC and translate("xs_tm_discovered_recipe_header") or nil,
  691.                     translate("xs_tm_discovered_recipe_format"):format(name),
  692.                     "ui_iconsTotal_found_thing",
  693.                     nil, nil, nil)
  694.             end
  695.         end
  696.         remove(self.pending, index)
  697.  
  698.         if level.map_has_object_spot(id, TM_MAPSPOT_TYPE) then
  699.             level.map_remove_object_spot(id, TM_MAPSPOT_TYPE)
  700.         end
  701.  
  702.         return true
  703.     end
  704.  
  705.     function TransmutationManager:Dump(print)
  706.         print = print or _G.print
  707.  
  708.         local l       = 0
  709.         local vec2str = function (v)
  710.             string.format("(x: %f, y: %f, z: %f)", v.x, v.y, v.z)
  711.         end
  712.  
  713.         local text = xs_text.StringBuilder()
  714.  
  715.         text:AppendRepeat("-", 80):AppendLine()
  716.             :AppendLine(self.__cls, " {"):IncreaseIndent()
  717.  
  718.         -- self recipes
  719.         l = #self.recipes
  720.         if l == 0 then
  721.             text:AppendLine("recipes {},")
  722.         else
  723.             text:AppendLine("recipes {"):IncreaseIndent()
  724.             for i, v in ipairs(self.recipes) do
  725.                 text:AppendLine("[", i, "] = {"):IncreaseIndent()
  726.                     :AppendLine("id*      = ",  v.id,       ",")
  727.                     :AppendLine("parent*  = ",  v.parent,   ",")
  728.                     :AppendLine("complex* = ",  v.complex,  ",")
  729.                     :AppendLine("anomaly  = ",  v.anomaly,  ",")
  730.                     :AppendLine("recipe   = ",  v.recipe,   ",")
  731.                     :AppendLine("result   = ",  v.result,   ",")
  732.                     :AppendLine("chance   = ",  table.concat(v.chance, ", "), ",")
  733.                     :AppendLine("delay  = ",    v.delay,    ",")
  734.                     :AppendIndent():Append("source   = ", v.source)
  735.                     :AppendIfOr(v.failure_artifact ~= nil or v.experiment_malus ~= nil, ",\n", "\n")
  736.  
  737.                 if v.failure_artifact ~= nil then
  738.                     text:AppendIndent():Append("*failure_artifact = ", v.failure_artifact)
  739.                         :AppendIfOr(v.experiment_malus ~= nil, ",\n", "\n")
  740.                 end
  741.                 if v.experiment_malus ~= nil then
  742.                     text:AppendLine("*experiment_malus = ", v.experiment_malus)
  743.                 end
  744.  
  745.                 text:DecreaseIndent():AppendIndent():Append("}"):AppendIfOr(i < l, ",\n", "\n")
  746.                 text:Apply(print):Clear()
  747.             end
  748.             text:DecreaseIndent():AppendLine("},")
  749.         end
  750.         text:Apply(print):Clear()
  751.  
  752.         -- self.pending
  753.         l = #self.pending
  754.         if l == 0 then
  755.             text:AppendLine("pending {},")
  756.         else
  757.             text:AppendLine("pending {"):IncreaseIndent()
  758.             for i, v in ipairs(self.pending) do
  759.                 text:AppendLine("[", i, "] = {"):IncreaseIndent()
  760.                  text:AppendLine("id   = ", v.id,   ",")
  761.                      :AppendLine("sid  = ", v.sid,  ",")
  762.                      :AppendLine("gvid = ", v.gvid, ",")
  763.                      :AppendLine("lvid = ", v.lvid, ",")
  764.                      :AppendLine("pos  = ", v.pos,  ",")
  765.                 text:DecreaseIndent():AppendIndent():Append("}"):AppendIf(i < l, ","):AppendLine()
  766.                 text:Apply(print):Clear()
  767.             end
  768.             text:DecreaseIndent():AppendLine("},")
  769.         end
  770.         text:Apply(print):Clear()
  771.  
  772.         -- self.timespan
  773.         text:AppendLine("timespan {"):IncreaseIndent()
  774.             :AppendLine("min = ", self.timespan.min, ",")
  775.             :AppendLine("max = ", self.timespan.max)
  776.         :DecreaseIndent():AppendIndent():AppendIfOr(self.complex ~= nil, "},\n", "}\n")
  777.  
  778.         -- self.complex
  779.         if self.complex ~= nil then
  780.             text:AppendLine("complex {"):IncreaseIndent()
  781.                 :AppendLine("id        = ", self.complex.id, ",")
  782.                 :AppendLine("anomaly   = (game_object, id: ", self.complex.anomaly:id(), "),")
  783.                 :AppendLine("artifacts = ", concat(self.complex.artifacts, ", "), ",")
  784.                 :AppendLine("numleft   = ", self.complex.numleft, ",")
  785.                 :AppendLine("recipes   = (table, #: ", #self.complex.recipes, ")")
  786.             :DecreaseIndent():AppendLine("}")
  787.         end
  788.  
  789.         text:DecreaseIndent():AppendLine("}")
  790.             :Apply(print):Clear()
  791.     end
  792.  
  793.  
  794. function getTransmutationManager()
  795.     if m_tm == nil then
  796.         m_tm = TransmutationManager()
  797.     end
  798.     return m_tm
  799. end
  800.  
  801. function finishTransmutationCallback(name)
  802.     local id = xs_utils.extractNo(name, TM_TIMER_PREFIX:len())
  803.     getTransmutationManager():finishTransmutation(id)
  804. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement