Combined Alchemy Pack [Assets]

Ask for help about creating mods and scripts for Grimrock 2 or share your tips, scripts, tools and assets with other modders here. Warning: forum contains spoilers!
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Alchemy questions + discussion

Post by AndakRainor »

potions.lua
SpoilerShow

Code: Select all

-- This file has been generated   by Dungeon Editor 2.1.17

-- TODO: place your custom item definitions here

defineObject{
  name = "flask",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Flask (empty)",
      gfxIndex = 143,
      weight = 0.1,
      stackable = true,
    },
    {
      class = "UsableItem",
      --emptyItem = "water_flask",
      sound = "consume_potion",
      onUseItem = function(self, champion)
        if party.elevation > 0 then return false end
        local dx,dy = getForward(party.facing)
        if party.map:getAutomapTile(party.x,party.y) == 2 or party.map:getAutomapTile(party.x+dx,party.y+dy) == 2 then
          for slot = 1,ItemSlot.MaxSlots do
            local item = champion:getItem(slot)
            if item then
              if item.go == self.go then
                local w = spawn("water_flask")
                w.item:setStackSize(self.go.item:getStackSize())
                --self.go:destroyDelayed()
                champion:removeItemFromSlot(slot)
                champion:insertItem(slot, w.item)
                playSound("party_move_wade")
                return false
              elseif item.go.containeritem then
                for s = 1,item.go.containeritem:getCapacity() do
                  local i = item.go.containeritem:getItem(s)
                  if i and i.go == self.go then
                    local w = spawn("water_flask")
                    w.item:setStackSize(self.go.item:getStackSize())
                    --self.go:destroyDelayed()
                    item.go.containeritem:removeItemFromSlot(s)
                    item.go.containeritem:insertItem(s, w.item)
                    playSound("party_move_wade")
                    return false
                  end
                end
              end
            end
          end
        end
        return false
      end,
    },
  },
}

defineObject{
  name = "potion_healing",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Healing Potion",
      gfxIndex = 146,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "consume_potion",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        local mult = iff(champion:hasTrait("fast_metabolism"), 2, 1)
        champion:regainHealth(75*mult)
        champion:removeCondition("head_wound")
        champion:removeCondition("chest_wound")
        champion:removeCondition("leg_wound")
        champion:removeCondition("feet_wound")
        champion:removeCondition("right_hand_wound")
        champion:removeCondition("left_hand_wound")
        champion:playHealingIndicator()
      end,
    },
  },
}

defineObject{
  name = "potion_greater_healing",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/big_potion.fbx",
    },
    {
      class = "Item",
      uiName = "Greater Healing Potion",
      gfxIndex = 409,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "consume_potion",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:regainHealth(150)
        champion:removeCondition("head_wound")
        champion:removeCondition("chest_wound")
        champion:removeCondition("leg_wound")
        champion:removeCondition("feet_wound")
        champion:removeCondition("right_hand_wound")
        champion:removeCondition("left_hand_wound")
        champion:playHealingIndicator()
      end,
    },
  },
}

defineObject{
  name = "potion_energy",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Energy Potion",
      gfxIndex = 145,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "consume_potion",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:regainEnergy(75)
        champion:playHealingIndicator()
      end,
    },
  },
}

defineObject{
  name = "potion_greater_energy",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/big_potion.fbx",
    },
    {
      class = "Item",
      uiName = "Greater Energy Potion",
      gfxIndex = 410,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "consume_potion",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:regainEnergy(150)
        champion:playHealingIndicator()
      end,
    },
  },
}

defineObject{
  name = "potion_poison",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Poignant Potion",
      gfxIndex = 147,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "consume_potion",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:setCondition("poison")
      end,
    },
  },
}

defineObject{
  name = "potion_shield",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Shield Potion",
      gfxIndex = 151,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "consume_potion",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:setCondition("protective_shield")
      end,
    },
  },
}

defineObject{
  name = "potion_cure_poison",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Antivenom",
      gfxIndex = 149,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "consume_potion",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:removeCondition("poison")
        champion:playHealingIndicator()
      end,
    },
  },
}

defineObject{
  name = "potion_cure_disease",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Antidote",
      gfxIndex = 148,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      canBeUsedByDeadChampion = true,
      sound = "consume_potion",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        -- can't be used by dead champion but can be used to cure petrification
        if not champion:isAlive() and not champion:hasCondition("petrified") then
          return false
        end
        champion:removeCondition("diseased")
        champion:removeCondition("paralyzed")
        champion:removeCondition("petrified")
        champion:playHealingIndicator()
      end,
    },
  },
}

defineObject{
  name = "potion_rage",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Rage Potion",
      gfxIndex = 152,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
      description = "This potion has a sulphurous stench.",
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "consume_potion",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:setConditionValue("rage", champion:getConditionValue("rage") + 20)
      end,
    },
  },
}

defineObject{
  name = "potion_speed",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Speed Potion",
      gfxIndex = 153,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "consume_potion",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:setCondition("haste")
      end,
    },
  },
}

defineObject{
  name = "potion_bear_form",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Potion of Bear Form",
      gfxIndex = 154,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "polymorph_bear",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:setCondition("bear_form", 1)
      end,
    },
  },
}

defineObject{
  name = "potion_oblivion",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Potion of Oblivion",
      gfxIndex = 155,
      weight = 0.3,
      stackable = true,
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "level_up",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        for _,skill in ipairs(skills.script.defOrdered) do
          local level = champion:getSkillLevel(skill.name) or 0
          if level>0 then
            champion:trainSkill(skill.name, -level)
            --champion:setSkillPoints(champion:getSkillPoints() + level)
          end
        end
        hudPrint(champion:getName().." forgot all learned skills.")
      end,
    },
  },
}

defineObject{
  name = "potion_resurrection",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask.fbx",
    },
    {
      class = "Item",
      uiName = "Potion of Resurrection",
      gfxIndex = 150,
      weight = 0.3,
      stackable = true,
      gameEffect = "Restores dead character to full health and energy.",
      description = "This potion has a foul stench.",
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "none",
      canBeUsedByDeadChampion = true,
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        -- can't be used on alive champion
        if champion:isAlive() then return false end

        playSound("heal_party")
        champion:setBaseStat("health", champion:getCurrentStat("max_health"))
        champion:setBaseStat("energy", champion:getCurrentStat("max_energy"))
        champion:playHealingIndicator()
      end,
    },
  },
}

defineObject{
  name = "potion_strength",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask_large.fbx",
    },
    {
      class = "Item",
      uiName = "Potion of Strength",
      gfxIndex = 414,
      weight = 0.5,
      stackable = true,
      gameEffect = "Permanently increases strength by 1.",
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "level_up",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:upgradeBaseStat("strength", 1)
      end,
    },
    {
      class = "Particle",
      particleSystem = "potion_of_strength",
    },
  },
}

defineObject{
  name = "potion_dexterity",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask_large.fbx",
    },
    {
      class = "Item",
      uiName = "Potion of Dexterity",
      gfxIndex = 413,
      weight = 0.5,
      stackable = true,
      gameEffect = "Permanently increases dexterity by 1.",
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "level_up",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:upgradeBaseStat("dexterity", 1)
      end,
    },
    {
      class = "Particle",
      particleSystem = "potion_of_dexterity",
    },
  },
}

defineObject{
  name = "potion_vitality",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask_large.fbx",
    },
    {
      class = "Item",
      uiName = "Potion of Vitality",
      gfxIndex = 411,
      weight = 0.5,
      stackable = true,
      gameEffect = "Permanently increases vitality by 1.",
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "level_up",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:upgradeBaseStat("vitality", 1)
      end,
    },
    {
      class = "Particle",
      particleSystem = "potion_of_vitality",
    },
  },
}

defineObject{
  name = "potion_willpower",
  baseObject = "base_item",
  components = {
    {
      class = "Model",
      model = "assets/models/items/flask_large.fbx",
    },
    {
      class = "Item",
      uiName = "Potion of Willpower",
      gfxIndex = 412,
      weight = 0.5,
      stackable = true,
      gameEffect = "Permanently increases willpower by 1.",
      traits = { "potion" },
    },
    {
      class = "UsableItem",
      --emptyItem = "flask",
      sound = "level_up",
      onUseItem = function(self, champion)
        champions.script.addFlask(self.go, champion)
        champion:upgradeBaseStat("willpower", 1)
      end,
    },
    {
      class = "Particle",
      particleSystem = "potion_of_willpower",
    },
  },
}
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Alchemy questions + discussion

Post by AndakRainor »

skills.lua
SpoilerShow

Code: Select all

defOrdered = {
{
  name = "alchemy",
  uiName = "Alchemy",
  priority = 10,
  icon = 20,
  description = "A higher skill level in Alchemy allows you to brew a wider range of potions. To craft potions you also need herbs and a Mortar and Pestle. At 4th skill level you brew stronger healing and energy potions. At 5th skill level, when you craft bombs you get three bombs instead of one.",
  traits = { [4] = "improved_alchemy", [5] = "bomb_expert" },
},

{
  name = "athletics",
  uiName = "Athletics",
  priority = 20,
  icon = 12,
  description = "Increases your health by 20 for each skill point. At 3rd skill level your carrying capacity is increased by 15 kg. At 5th skill level your Health regenerates 25% faster.",
  traits = { [3] = "pack_mule" , [5] = "recover" },
  onRecomputeStats = function(champion, level)
    champion:addStatModifier("max_health", level*20)
  end,
},

{
  name = "concentration",
  uiName = "Concentration",
  priority = 30,
  icon = 26,
  description = "Increases your energy by 20 for each skill point. At 3rd skill level your Energy regeneration rate is increased by 25% while resting. At 5th skill level your Energy regenerates 25% faster.",
  onRecomputeStats = function(champion, level)
    champion:addStatModifier("max_energy", level*20)
  end,
  traits = { [3] = "meditation", [5] = "spirit" },
},

{
  name = "light_weapons",
  uiName = "Light Weapons",
  priority = 40,
  icon = 106,
  description = "Increases damage of Light Weapons by 20% for each skill point. At 3rd skill level you can dual wield Light Weapons as long one of them is a dagger. At 5th skill level you can dual wield any Light Weapons. When dual wielding you suffer a 40% penalty to weapon damage.",
  traits = { [3] = "dual_wield", [5] = "improved_dual_wield" },
},

{
  name = "heavy_weapons",
  uiName = "Heavy Weapons",
  priority = 50,
  icon = 105,
  description = "Increases damage of Heavy Weapons by 20% for each skill point. At 3rd skill level you gain Evasion +10 when equipped with heavy weapons. At 5th skill level you can wield two-handed weapons in one hand.",
  traits = { [3] = "parry", [5] = "two_handed_mastery" },
},

{
  name = "missile_weapons",
  uiName = "Missile Weapons",
  priority = 60,
  icon = 17,
  description = "Increases damage of Missile Weapons by 20% for each skill point. At 4th skill level your Missile Weapon attacks ignore 20 points of an enemy's armor. At 5th skill level cooldowns of your attacks with missile weapons are sped up by 20%.",
  traits = { [4] = "piercing_arrows", [5] = "missile_weapons_expert" },
},

{
  name = "throwing",
  uiName = "Throwing",
  priority = 70,
  icon = 16,
  description = "Increases damage of Throwing Weapons by 20% for each skill point. At 3rd skill level your throw attacks gain Accuracy +10. At 5th skill level you can throw weapons from both hands with one action.",
  traits = { [3] = "accurate_throw", [5] = "double_throw" },
},

{
  name = "firearms",
  uiName = "Firearms",
  priority = 80,
  icon = 90,
  description = "Increases range of firearm attacks by 1 square for each skill point. Also decreases the chance of a firearm malfunctioning. At 2nd skill level you do not suffer from firearm malfunctions anymore. At 5th skill level your firearms attacks gain Accuracy +20.",
  traits = { [2] = "firearm_mastery", [5] = "sniper" },
},

{
  name = "accuracy",
  uiName = "Accuracy",
  priority = 90,
  icon = 86,
  description = "Increases your Accuracy by 10 for each skill point. At 2nd skill level you can perform melee attacks from the back row. At 5th skill level cooldowns of all your actions are sped up by 10%.",
  onComputeAccuracy = function(champion, weapon, attack, attackType, level)
    return level * 10
  end,
  traits = { [2] = "reach", [5] = "haste" },
},

{
  name = "critical",
  uiName = "Critical",
  priority = 100,
  icon = 10,
  description = "Improves your chance of scoring a critical hit with melee, ranged, throwing or firearm attacks by 3% for each skill point. At 3rd level you can backstab an enemy with a dagger and deal triple damage. At 5th level you can backstab with any Light Weapon.",
  onComputeCritChance = function(champion, weapon, attack, attackType, level)
    return level * 3
  end,
  traits = { [3] = "backstab", [5] = "assassin" },
},

{
  name = "armors",
  uiName = "Armor",
  priority = 110,
  icon = 7,
  description = "Increases protection of armor pieces equipped by 5% for each skill point. At 2nd skill level you are proficient with Light Armor and can wear it without penalties. Similarly at 4th skill level you can wear Heavy Armor without penalties. At 5th skill level received damage of any type is reduced by 10%.",
  -- this skill is hardcoded
  traits = { [2] = "light_armor_proficiency", [4] = "heavy_armor_proficiency", [5] = "sacred_armor" },
},

{
  name = "dodge",
  uiName = "Dodge",
  priority = 120,
  icon = 9,
  description = "Increases evasion by 3 for each skill point. At 3rd skill level, the cooldown period for all of your actions is decreased by 10%. At 5th skill level cooldowns of all your actions are sped up by 20%.",
  onRecomputeStats = function(champion, level)
    champion:addStatModifier("evasion", level*3)
  end,
  traits = { [3] = "uncanny_speed", [5] = "light_speed" },
},

{
  name = "fire_magic",
  uiName = "Fire Magic",
  priority = 130,
  icon = 29,
  description = "Increases damage of fire spells by 20% for each skill point. At 3rd skill level your sword melee attacks have 10% chance to launch the fireburst spell or gain its energy cost. At 5th skill level you gain Resist Fire +50.",
  traits = { [3] = "fire_sword", [5] = "fire_mastery" },
},

{
  name = "air_magic",
  uiName = "Air Magic",
  priority = 140,
  icon = 30,
  description = "Increases damage of air spells by 20% for each skill point. At 3rd skill level your mace melee attacks have 10% chance to launch the shockburst spell or gain its energy cost. At 5th skill level you gain Resist Shock +50.",
  traits = { [3] = "thunder_hammer", [5] = "air_mastery" },
},

{
  name = "earth_magic",
  uiName = "Earth Magic",
  priority = 150,
  icon = 31,
  description = "Increases damage of earth spells by 20% for each skill point. At 3rd skill level your dagger melee attacks have 10% chance to launch the poison cloud spell or gain its energy cost. At 5th skill level you gain Resist Poison +50.",
  traits = { [3] = "poisoned_dagger", [5] = "earth_mastery" },
},

{
  name = "water_magic",
  uiName = "Water Magic",
  priority = 160,
  icon = 32,
  description = "Increases damage of water spells by 20% for each skill point. At 3rd skill level your axe melee attacks have 10% chance to launch the frostburst spell or gain its energy cost. At 5th skill level you gain Resist Cold +50.",
  traits = { [3] = "frost_axe", [5] = "water_mastery" },
},
}

defByName = {}
for _,v in ipairs(defOrdered) do defByName[v.name] = v end

function getSkillLevels(champ)
  local result = {}
  for _,skill in ipairs(defOrdered) do
    local value = champ:getSkillLevel(skill.name)
    if value > 0 then result[skill.name] = value end
  end
  return result
end
function setSkillLevels(champ,champSkills)
  for _,skill in ipairs(defOrdered) do
    local value = champSkills[skill.name] or 0
    local currentLevel = champ:getSkillLevel(skill.name)
    local trainToTarget = value - currentLevel
    champ:trainSkill(skill.name, trainToTarget)
  end
end

function updateChampion(champ)
  for _,skill in ipairs(defOrdered) do
    local value = champ:getSkillLevel(skill.name)
    if value > 0 then
      champ:trainSkill(skill.name, -value)
      champ:trainSkill(skill.name,  value)
    end
  end
end
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Alchemy questions + discussion

Post by AndakRainor »

init.lua
SpoilerShow

Code: Select all

-- import standard assets
import "mod_assets/scripts/standard_assets.lua"

-- import custom assets
import "mod_assets/scripts/conditions.lua"
import "mod_assets/scripts/objects.lua"
import "mod_assets/scripts/recipes.lua"
for _,v in ipairs(defOrdered) do defineRecipe(v) end
import "mod_assets/scripts/sounds.lua"
import "mod_assets/scripts/tiles.lua"

import "mod_assets/scripts/items/items.lua"
import "mod_assets/scripts/monsters/monsters.lua"

import "mod_assets/scripts/champions/races.lua"
import "mod_assets/scripts/champions/char_classes.lua"
import "mod_assets/scripts/champions/skills.lua"
for _,v in ipairs(defOrdered) do defineSkill(v) end
import "mod_assets/scripts/champions/traits.lua"
for _,v in ipairs(defOrdered) do defineTrait(v) end

import "mod_assets/scripts/spells/init_spells.lua"


-- other modders

import "mod_assets/sx_winter_tileset/scripts/sx_winter_tileset_init.lua"
import "mod_assets/sx_urban_town/scripts/sx_urban_town_init.lua"
import "mod_assets/sx_town/scripts/signs.lua"


-- generic objects

defineObject{
  name = "party",
  baseObject = "party",
  components = {
    { class = "Party",
      onGetPortrait = function(self, champion)
        if champions.script.get("overridePortraits") then
          local p = champions.script.get("portrait"..champion:getOrdinal())
          if type(p) == "string" then return p end
        end
      end,
      onAttack = function(self, champion, action, slot)
        champions.script.consumeWater(0.5, champion)
        if action and action:getClass() == "MeleeAttackComponent" then
          local item = champion:getItem(slot)
          if not item then return end
          if item:hasTrait("sword") then
            if champion:hasTrait("fire_sword") and math.random()<0.1 then
              champion:castSpell(1)
              champion:regainEnergy(spells_functions.script.defByName["fireburst"].manaCost)
            end
          elseif item:hasTrait("mace") then
            if champion:hasTrait("thunder_hammer") and math.random()<0.1 then
              champion:castSpell(3)
              champion:regainEnergy(spells_functions.script.defByName["shock"].manaCost)
            end
          elseif item:hasTrait("dagger") then
            if champion:hasTrait("poisoned_dagger") and math.random()<0.1 then
              champion:castSpell(7)
              champion:regainEnergy(spells_functions.script.defByName["poison_cloud"].manaCost)
            end
          elseif item:hasTrait("axe") then
            if champion:hasTrait("frost_axe") and math.random()<0.1 then
              champion:castSpell(9)
              champion:regainEnergy(spells_functions.script.defByName["frost_burst"].manaCost)
            end
          end
        end
      end,
      onCastSpell = function(self, champion, spellName)
        champions.script.consumeWater(1, champion)
        return spells_functions.script.onCastSpell(champion, spellName)
      end,
      onMove = function(self, direction)
        local dx, dy = getForward(direction)
        if not self.go.map:isBlocked(self.go.x + dx, self.go.y + dy, self.go.elevation) then
          for i = 1,4 do
            champions.script.consumeWater(0.28, party.party:getChampionByOrdinal(i))
          end
        end
      end,
      onRest = function(self)
        if dreams.script.sleep then dreams.script.loadDream(dreams.script.sleep) end
      end,
      onWakeUp = function(self)
        return not spells_functions.script.deepSleep and not dreams.script.sleep
      end,
      onDamage = function(self, champion, damage, damageType)
        if spells_functions.script.onDamage(champion, damage, damageType) then
          champions.script.addWeakness(champion, damageType)
          return true
        end
        return false
      end,
      onDrawGui = function(self, context)
        return not dreams.script.drawGUI(context) and
               not taverns.script.drawGUI(context) and
               not shops.script.drawGUI(context) and
               not boat.script.drawGUI(context)
      end,
      onDrawInventory = function(self, context, champion) champions.script.drawInventory(context, champion) end,	
      onDrawStats     = function(self, context, champion) champions.script.drawChampionGUI(context, champion) end,	
      onDrawSkills    = function(self, context, champion) champions.script.drawChampionGUI(context, champion) end,	
      onDrawTraits    = function(self, context, champion) champions.script.drawChampionGUI(context, champion) end,
    },
    { class = "Timer",
      name = "world",
      timerInterval = 1,
      triggerOnStart = false,
      onActivate = function(self)
        --champions.script.updateChampions(self:getTimerInterval())
        --champions.script.updateChampions(1)
      end,
    },
    { class = "Timer",
      timerInterval = 0.001,
      triggerOnStart = true,
      onActivate = function(self)
        spells_functions.script.moveLights()
        local dt = Time.deltaTime()
        if party.party:isResting() then
          for i = 1,4 do champions.script.consumeWater(dt, party.party:getChampionByOrdinal(i)) end
          GameMode.resetTimeOfDay()
        else
          for _,c in ipairs(champions.script.getEnabledChampions()) do
            c:setFood(math.max(0, c:getFood()-dt*0.001))
            --c:consumeFood(dt*0.001)
            champions.script.consumeWater(dt*0.001, c)
          end
          GameMode.advanceTime(dt/43200) -- 12 = 24S ; 720 = 24M ; 10800 = 6H; 43200 = 24H
        end
      end,
      onInit = function(self)
        local c = self.go.party:getChampionByOrdinal(1)
        local items = {}
        items[1] = spawn("crystal_shard_leveling").item items[1]:setStackSize(100)
        items[2] = spawn("crystal_shard_skills").item
        items[3] = spawn("crystal_shard_spells").item items[3]:setStackSize(4)
      end,
    },
    { class = "Light",
      name = "torch",
      range = 12,
    },
    { class = "Controller",
      onActivate = function(self)
        self.go.particle:fadeIn(3)
      end,
      onDeactivate = function(self)
        self.go.particle:fadeOut(1)
      end,
      onToggle = function(self)
        if self.go.particle:isEnabled() then
          self.go.particle:fadeOut(1)
        else
          self.go.particle:fadeIn(3)
        end
      end,
    },  
    { class = "Particle",
      name = "snow",
      ParticleSystem = "sx_snow_emitter_party",
    },
    { class = "Particle",
      name = "rain",
      particleSystem = "sx_rain_emitter_party",
    },
    { class = "Sound",
      name = "rainsound",
      sound = "sx_rain_loop"
    },
  },
  editorIcon = 32,
  placement = "floor",
}

function table.contains(table, element)
  for _,v in pairs(table) do if v == element then return true end end
  return false
end

--print("----------------------")
--print("------ new game ------")
--print("----------------------")
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Alchemy questions + discussion

Post by AndakRainor »

champions.lua (with some cuts!)
SpoilerShow

Code: Select all

data = {}
function get(name) return data[name] end
function set(name,value) data[name] = value end

levels = {1,2,3,4,5,6,7,8,9,10,20,25,50,50,50,50,100,100,500,1000}
function xpUp(level) return level>0 and levels[math.min(#levels,level)]*1000 or 0 end
function xp(level)
  if level <= 1 then return 0 end
  local l1 = math.floor(level)
  local l2 = level-l1
  local x = 0
  for l = 1,l1-1 do x = x + xpUp(l) end
  return x + l2*xpUp(l1)
end
function level(xp)
  local level = 1
  while true do
    local up = xpUp(level)
    if xp < up then return level,xp/up else xp = xp-up level = level+1 end
  end
end
function floatLevel(xp)
  local l1,l2 = level(xp)
  return l1+l2
end

function addStat(tab, name, value) tab[name] = (tab[name] or 0)+value end
function levelUp(c)
  local class = traits.script.defByName[c.class]
  if class then
    addStat(c, "maxHealth", class.hu)
    addStat(c, "maxEnergy", class.eu)
  end
  if c.class == "barbarian" then addStat(c.currentStats, "strength", 1)
  elseif c.class == "knight" then addStat(c, "protection", 1)
  elseif c.class == "wizard" then addStat(c.currentStats, "willpower", 1)
  end
end

stats = {'strength','dexterity','vitality','willpower','exp_rate'}

conditions = {}

champions = {}
function defineChampion(def)
  local index = #champions+1
  def.index = index
  champions[index] = def
end
function storeChampion(champ, tavern)
  defineChampion {
    baseStats = getBaseStats(champ),
    currentStats = getCurrentStats(champ),
    class = champ:getClass(),
    dualClass = champ:getDualClass(),
    energy = champ:getEnergy(),
    evasion = champ:getEvasion(),
    experience = champ:getExp(),
    food = champ:getFood(),
    water = getWater(champ),
    health = champ:getHealth(),
    level = champ:getLevel(),
    load = champ:getLoad(),
    maxEnergy = champ:getMaxEnergy(),
    maxHealth = champ:getMaxHealth(),
    maxLoad = champ:getMaxLoad(),
    name = champ:getName(),
    portrait = getPortrait(champ),
    protection = champ:getProtection(),
    race = champ:getRace(),
    sex = champ:getSex(),
    skillLevels = skills.script.getSkillLevels(champ),
    traits = traits.script.getTraits(champ),
    spells = traits.script.getSpells(champ),
    skillPoints = champ:getSkillPoints(),
    description = getDescription(champ),
    deltaLevel = getDeltaLevel(champ),
    plan = getPlan(champ),
    magic = getMagic(champ),
    items = getItems(champ),
    money = getMoney(champ),
    tavern = tavern,
  }
  champ:setEnabled(false)
  set("mortar"..champ:getOrdinal(), nil)
end
function loadChampion(index, ordinal)
  local replaceChamp = nil
  if not ordinal then for i = 1,4 do if not party.party:getChampionByOrdinal(i):getEnabled() then ordinal = i break end end end
  if not ordinal then return end
  replaceChamp = party.party:getChampionByOrdinal(ordinal)
  if not replaceChamp or replaceChamp:getEnabled() then return end
  local data = table.remove(champions, index)
  if index <= championUpdateIndex then championUpdateIndex = championUpdateIndex-1 end
  for i = 1,#champions do champions[i].index = i end
  if not data then return end
  if type(data.portrait) == "number" and ordinal ~= data.portrait then
    local loadChamp = party.party:getChampionByOrdinal(data.portrait)
    if loadChamp:getEnabled() then
      storeChampion(loadChamp)
      loadChampion(#champions, ordinal)
    end
    replaceChamp = loadChamp
    ordinal = data.portrait
  end
  replaceChamp:setEnabled(true)

  replaceChamp:resetExp()
  traits.script.resetTraitsAndSpells(replaceChamp) 
  resetItems(replaceChamp)  

  local expRate = replaceChamp:getCurrentStat("exp_rate")
  if expRate ~= 100 then replaceChamp:addStatModifier("exp_rate", 100-expRate) end

  replaceChamp:gainExp(data.experience)
  replaceChamp:setName(data.name) 
  replaceChamp:setRace(data.race)   
  replaceChamp:setSex(data.sex)   
  replaceChamp:setClass(data.class)   
  setBaseStats(replaceChamp,data.baseStats)   
  replaceChamp:setEnergy(data.energy)   
  replaceChamp:setFood(data.food)
  setWater(data.water, replaceChamp)   
  replaceChamp:setHealth(data.health)   
  
  set("portrait"..ordinal, data.portrait)
  set("description"..ordinal, data.description)
  set("deltaLevel"..ordinal, data.deltaLevel)
  set("plan"..ordinal, data.plan)
  set("magic"..ordinal, data.magic)

  traits.script.addTraits(replaceChamp, data.traits)  
  traits.script.addSpells(replaceChamp, data.spells)  
  skills.script.setSkillLevels(replaceChamp, data.skillLevels)
  replaceChamp:setSkillPoints(data.skillPoints)
  addItems(replaceChamp, data.items)
  money[replaceChamp:getOrdinal()] = data.money<0 and 0 or data.money  
  
  hudPrint('') hudPrint('') hudPrint('') hudPrint('') 
  hudPrint(data.name..' joined to your party.')
  if data.onRecruit then
    data.onRecruit(data)
  end
end

function getPortrait(from)
  if type(from) == "string" then return from end
  if type(from) == "number" then for j = 1,4 do if party.party:getChampion(j):getOrdinal() == from then return "$portrait"..j end end return from end
  local i = from:getOrdinal()
  local p = get("portrait"..i)
  return iff(type(p) == "string", p, i)
end
function getCurrentPortrait(champ)
  local i = champ:getOrdinal()
  local p = get("portrait"..i)
  return iff(type(p) == "string", p, getPortrait(i))
end
function getDescription(champ)
  return get("description"..champ:getOrdinal()) or "Your old friend. Always there since the beginning!"
end
function getDeltaLevel(champ)
  return get("deltaLevel"..champ:getOrdinal()) or 0
end
function getPlan(champ)
  return get("plan"..champ:getOrdinal())
end
function getMagic(champ)
  return get("magic"..champ:getOrdinal())
end


function getItems(champ)
  local c = findEntity("champions")
  local items = spawn("inventory", c.level, c.x, c.y, c.facing, c.elevation)
  for i = 1, ItemSlot.MaxSlots do
    local item = champ:getItem(i)
    if item then
      items.store:insertItem(i, item)
      champ:removeItemFromSlot(i)
    end
  end
  return items.id
end
function resetItems(champ)
  local c = findEntity("champions")
  for i = 1, ItemSlot.MaxSlots do if champ:getItem(i) then champ:removeItemFromSlot(i) end end
end
function addItems(champ, items)
  if not items then return end
  if type(items) == "string" then
    items = findEntity(items)
    if not items then return end
    for i = 1, ItemSlot.MaxSlots do
      local item = items.store:getItem(i)
      if item then champ:insertItem(i, item) end
    end
    items:destroy()
  else
    for i = 1, ItemSlot.MaxSlots do
      if items[i] then
        local item = createItem(items[i])
        if item then champ:insertItem(i, item) end
      end
    end
  end
end
function createItem(def)
  local item = nil
  if type(def) == "string" then item = self.go:spawn(def).item
  else
    item = self.go:spawn(def[1]).item
    if item:getStackable() then item:setStackSize(def[2])
    elseif item.go.containerItem then for i = 2,#def do
      local it = createItem(def[i])
      if it then item.go.containerItem:addItem(it) end
    end
    else item:setCharges(def[2]) end
  end
  return item 
end

function getBaseStats(champ)
  local result = {}
  for _,stat in ipairs(stats) do
    result[stat] = champ:getBaseStat(stat)
  end
  return result
end
function setBaseStats(champ,stats)
  for stat,value in pairs(stats) do
    champ:setBaseStat(stat,value)
  end
end
function getCurrentStats(champ)
  local result = {}
  for _,stat in ipairs(stats) do
    result[stat] = champ:getCurrentStat(stat)
  end
  return result
end

-- GUI functions

mouseX,mouseY = 0,0
craftX,craftY = 0,0
mouseL,mouseR = false,false
tooltip = {"Water","All actions you do consume water. The bar turns", "yellow when you're Thirsty and you start losing", "Energy. If you don't have Energy you lose", "Health instead."}
function drawChampionGUI(context, champion)	
  local w, h, f = context.width, context.height, context.height/1080
  --context.drawImage2(fileName, x, y, srcX, srcY, srcWidth, srcHeight, destWidth, destHeight)
  context.drawImage2("mod_assets/textures/gui/food-water.tga", w-f*421, f*144, 0, 0, 373, 23, f*373, f*23)
  local food = champion:getFood()
  local foodbar = food<250 and "mod_assets/textures/gui/bar-red.tga" or food<350 and "mod_assets/textures/gui/bar-yellow.tga" or "mod_assets/textures/gui/bar-green.tga"
  context.drawImage2(foodbar, w-f*298, f*147, 0, 0, 246, 8, f*246*food/1000, f*8)
  local water = getWater(champion)
  local waterbar = water<250 and "mod_assets/textures/gui/bar-red.tga" or water<350 and "mod_assets/textures/gui/bar-yellow.tga" or "mod_assets/textures/gui/bar-blue.tga"
  local xw, ww, yw, hw = w-f*298, f*246, f*156, f*8
  context.drawImage2(waterbar, xw, yw, 0, 0, 246, 8, ww*water/1000, hw)

  local mX,mY = context.inverseTransformPoint(context.mouseX, context.mouseY)

  if xw < mX and mX < xw+ww and yw+2 < mY and mY < yw+2+hw then
    context.font(fontSize[1+math.floor(#fontSize*f*0.5)] or fontSize[#fontSize])
    local th = context.getLineHeight()
    local wt,ht,b = 0,#tooltip*th,8
    for _,l in ipairs(tooltip) do
      local lw = context.getTextWidth(l)
      if lw>wt then wt=lw end
    end
    wt,ht = wt+2*b,ht+2*b
    local x, y = math.min(w-wt-2*b, mX-wt/2), mY<h/2 and math.min(mY+37, 1080-ht) or math.max(mY-37-ht, 0)
    context.color(0, 0, 0, 192)
    context.drawRect(x-1, y-1, wt+2, ht+2)
    context.color(255, 255, 255, 255)
  
    --context.drawGuiItem2(item, x, y, srcX, srcY, srcWidth, srcHeight, width, height)
    context.drawGuiItem2("DialogFrameSideBottom", x, y+ht-2, 0, 0, 90, 5, wt, 5)
    context.drawGuiItem2("DialogFrameSideLeft", x-3, y, 0, 0, 5, 90, 5, ht)
    context.drawGuiItem2("DialogFrameSideRight", x+wt-2, y, 0, 0, 5, 90, 5, ht)
    context.drawGuiItem2("DialogFrameSideTop", x, y-3, 0, 0, 90, 5, wt, 5)
  
    --context.drawGuiItem(item, x, y)
    context.drawGuiItem("DialogFrameCornerBottomLeft", x-3, y+ht-2)
    context.drawGuiItem("DialogFrameCornerBottomRight", x+wt-2, y+ht-2)
    context.drawGuiItem("DialogFrameCornerTopLeft", x-3, y-3)
    context.drawGuiItem("DialogFrameCornerTopRight", x+wt-2, y-3)
    context.color(255, 255, 255, 255)
    x,y = x+b,y+b
    context.font(fontSize[1+math.floor(#fontSize*f)] or fontSize[#fontSize])
    context.drawText(tooltip[1], x, y+th)
    context.font(fontSize[1+math.floor(#fontSize*f*0.5)] or fontSize[#fontSize])
    for i = 2,#tooltip do context.drawText(tooltip[i], x, y+i*th) end
  end

  local mortar = get("mortar"..champion:getOrdinal())
  if not mortar then return end
  mortar = champion:getItem(mortar)
  if not mortar or not mortar.go.craftpotioncustom then set("mortar"..champion:getOrdinal(), nil) return end

  --context.scale(f, f)
  local x0, y0 ,w0 ,h0 = w-f*544+craftX, h-f*352+craftY, f*64, f*320
  context.button("craftpotionbutton", x0, y0, w0, h0)  

  --context.drawGuiItem2(item, x, y, srcX, srcY, srcWidth, srcHeight, width, height)
  context.drawGuiItem2("InventoryTab", x0, y0, 237, 64, 64, 320, w0, h0)

  --context.drawGuiItem2(item, x, y, srcX, srcY, srcWidth, srcHeight, width, height)
  context.drawGuiItem2("DialogFrameSideBottom", x0, y0+h0-2*f, 0, 0, 90, 5, w0, 5*f)
  context.drawGuiItem2("DialogFrameSideLeft", x0-3*f, y0, 0, 0, 5, 90, 5*f, h0)
  context.drawGuiItem2("DialogFrameSideRight", x0+w0-2*f, y0, 0, 0, 5, 90, 5*f, h0)
  context.drawGuiItem2("DialogFrameSideTop", x0, y0-3*f, 0, 0, 90, 5, w0, 5*f)

  --context.drawGuiItem(item, x, y)
  context.drawGuiItem("DialogFrameCornerBottomLeft", x0-3*f, y0+h0-2*f)
  context.drawGuiItem("DialogFrameCornerBottomRight", x0+w0-2*f, y0+h0-2*f)
  context.drawGuiItem("DialogFrameCornerTopLeft", x0-3*f, y0-3*f)
  context.drawGuiItem("DialogFrameCornerTopRight", x0+w0-2*f, y0-3*f)

  context.drawImage2("mod_assets/textures/gui/up.tga", x0+w0*0.25, y0+h0*0.2-w0*0.125, 0, 0, 64, 32, w0*0.5, w0*0.25)

  local potion = mortar.go.craftpotioncustom:get(0)  
  local atlas,x,y
  if potion then atlas,x,y = shops.script.getItemIconByName(potion[1]) else atlas,x,y = shops.script.getItemIcon(mortar) end
  context.drawImage2(atlas, x0, y0, x, y, 75, 75, w0, w0)

  local mL = context.mouseDown(0)
  if x0 < mX and mX < x0+w0 and y0 < mY and mY < y0+h0 then
    if mL then
      if mouseL then
        craftX,craftY = craftX+mX-mouseX,craftY+mY-mouseY
      else
        local mouseItem = getMouseItem()
        local index = math.floor(5*(mY-y0)/h0)
        if index>0 then
          local craftItem = mortar.go.craftpotioncustom:get(index)
          if mouseItem then
            if mouseItem:hasTrait("herb") or mouseItem.go.name == "water_flask" then
              if craftItem and craftItem[2]>0 and craftItem[1] == mouseItem.go.name then
                if context.keyDown("shift") then
                  mortar.go.craftpotioncustom:set(index, {craftItem[1], craftItem[2]-1})
                  mouseItem:setStackSize(mouseItem:getStackSize()+1)
                else
                  mortar.go.craftpotioncustom:set(index, {craftItem[1], craftItem[2]+mouseItem:getStackSize()})
                  setMouseItem(nil)
                end
              else
                mortar.go.craftpotioncustom:set(index, {mouseItem.go.name, mouseItem:getStackSize()})
                mouseItem = nil
                setMouseItem(nil)
              end
            end
          else
            if craftItem and craftItem[2]>0 and context.keyDown("shift") then
              mortar.go.craftpotioncustom:set(index, {craftItem[1], craftItem[2]-1})
              mouseItem = spawn(craftItem[1]).item
              setMouseItem(mouseItem)
            else
              mortar.go.craftpotioncustom:set(index, nil)
            end
          end
          if not mouseItem and craftItem and craftItem[2]>0 then
            mouseItem = spawn(craftItem[1]).item
            mouseItem:setStackSize(craftItem[2])
            setMouseItem(mouseItem)
          end
          local tmp = {}
          for i = 1,4 do
            craftItem = mortar.go.craftpotioncustom:get(i)
            if craftItem and craftItem[2]>0 then
              tmp[ingredientID[craftItem[1]]] = (tmp[ingredientID[craftItem[1]]] or 0) + craftItem[2]
            end
          end
          local ingredients = ""
          for i = 1,6 do if tmp[i] then for j = 1,tmp[i] do ingredients = ingredients..tostring(i) end end end
          if tmp[0] then for j = 1,tmp[0] do ingredients = ingredients.."0" end end
          local level = champion:getSkillLevel("alchemy")
          mortar.go.craftpotioncustom:set(0, nil)
          for _,r in ipairs(recipes.script.defOrdered) do
            if level < r.level then break end
            if ingredients == tostring(r.ingredients) then
              local name,num = r.potion,1
              if champion:hasTrait("improved_alchemy") then
                name = name == "potion_healing" and "potion_greater_healing" or name == "potion_energy" and "potion_greater_energy" or name
              end
              if champion:hasTrait("bomb_expert") and (name == "fire_bomb" or name == "frost_bomb" or name == "poison_bomb" or name == "shock_bomb") then num = 3 end
              mortar.go.craftpotioncustom:set(0, {name, num})
              break
            end
          end
        elseif potion and not mouseItem then
          mouseItem = spawn(potion[1]).item
          mouseItem:setStackSize(potion[2])
          setMouseItem(mouseItem)
          for i = 0,4 do mortar.go.craftpotioncustom:set(i, nil) end
        end
      end
    end
  end
  context.font(fontSize[1+math.floor(#fontSize*f*0.5)] or fontSize[#fontSize])
  for l = 1,4 do
    local craftItem = mortar.go.craftpotioncustom:get(l)
    if craftItem and craftItem[2]>0 then
      atlas,x,y = shops.script.getItemIconByName(craftItem[1])
      context.drawImage2(atlas, x0, y0+l*w0, x, y, 75, 75, w0, w0)
      local n = tostring(craftItem[2])
      local nw,nh = context.getTextWidth(n),context.getLineHeight()
      context.drawText(n, x0+0.9*w0-nw, y0+l*w0+nh)
    end
  end
  mouseX,mouseY = mX,mY
  mouseL = mL
end
ingredientID = {
  water_flask    = 0,
  blooddrop_cap  = 1,
  etherweed      = 2,
  mudwort        = 3,
  falconskyre    = 4,
  blackmoss      = 5,
  crystal_flower = 6,
}

fontSize = {"tiny", "small", "medium", "large"}
function drawInventory(context, champion)	
  drawChampionGUI(context, champion)
  local f = context.height/1080
  drawMoney(context, f, getMoney(champion), context.width-f*549, f*609, 1, champion)
end

function getMoneyWidth(context, f, money, tf)
  tf = tf or f
  context.font(fontSize[1+math.floor(#fontSize*f*0.5)] or fontSize[#fontSize])
  local result = 0
  local ms = f*24
  local showSilver = false 
  local gold = math.floor(money/10000)
  if gold>0 then
    gold = tostring(gold)
    result = result + context.getTextWidth(gold)/tf + 1.5*ms
    showSilver = true
  end
  local silv = math.floor(money/100) % 100
  if silv>0 or showSilver then
    silv = tostring(silv)
    result = result + context.getTextWidth(silv)/tf + 1.5*ms
  end
  local copp = math.floor(money) % 100
  copp = tostring(copp)
  result = result + context.getTextWidth(copp)/tf + ms
  return result
end

function drawMoney(context, f, money, x, y, tf, championShare)
  tf = tf or f
  context.font(fontSize[1+math.floor(#fontSize*f*0.5)] or fontSize[#fontSize])
  local x0 = x
  local th = context.getLineHeight()
  local ms = f*24
  local showSilver = false 
  local gold = math.floor(money/10000)
  if gold>0 then
    gold = tostring(gold)
    local gw = context.getTextWidth(gold)/tf
    context.color(214, 204, 170, 255)
    context.drawText(gold, x, y+0.75*ms)
    x = x+gw
    context.color(255, 255, 255, 255)
    --context.drawImage2(fileName, x, y, srcX, srcY, srcWidth, srcHeight, destWidth, destHeight)
    context.drawImage2("mod_assets/textures/gui/gold.tga", x, y, 0, 0, 24, 24, ms, ms)
    x = x+1.5*ms
    showSilver = true
  end
  local silv = math.floor(money/100) % 100
  if silv>0 or showSilver then
    silv = tostring(silv)
    local sw = context.getTextWidth(silv)/tf
    context.color(192, 192, 192, 255)
    context.drawText(silv, x, y+0.75*ms)
    x = x+sw
    context.color(255, 255, 255, 255)
    context.drawImage2("mod_assets/textures/gui/silver.tga", x, y, 0, 0, 24, 24, ms, ms)
    x = x+1.5*ms
  end
  local copp = math.floor(money) % 100
  copp = tostring(copp)
  local cw = context.getTextWidth(copp)/tf
  context.color(188, 160, 139, 255)
  context.drawText(copp, x, y+0.75*ms)
  x = x+cw
  context.color(255, 255, 255, 255)
  context.drawImage2("mod_assets/textures/gui/copper.tga", x, y, 0, 0, 24, 24, ms, ms)
  if championShare then
    x = x+1.5*ms
    local mouseX,mouseY = context.inverseTransformPoint(context.mouseX, context.mouseY)
    if x0<mouseX and mouseX<x and y<mouseY and mouseY<y+ms then
      local mL, mR = context.mouseDown(0), context.mouseDown(2)
      if mouseL and not mL then
        if context.keyDown("shift") then shareAllMoney() else getAllMoney(championShare) end
      elseif mouseR and not mR then
        shareAllMoney()
      end
      mouseL, mouseR = mL, mR
    end
  end
end

function getBody(champ)
  local result = "default"
  local race = champ:getRace()
  if race == "insectoid" or race == "lizardman" or  race == "ratling" then
    result = race
  else
    result = race.."_"..champ:getSex()
  end
  return "mod_assets/textures/gui/inventory_backgrounds/alpha/"..result..".tga"
end

-- elemental resistance functions

lastHurt = {fire={0,0,0,0},shock={0,0,0,0},cold={0,0,0,0},poison={0,0,0,0}}
weakness = {fire={0,0,0,0},shock={0,0,0,0},cold={0,0,0,0},poison={0,0,0,0}}
function getWeakness(champion, element)
  local e = weakness[element]
  if e and champion:getEnabled() then
    return math.floor(e[champion:getOrdinal()])
  end
  return 0
end
function addWeakness(champion, element)
  local e = weakness[element]
  if e and champion:getEnabled() then
    local i = champion:getOrdinal()
    local t = Time.currentTime()
    local r = champion:getResistance(element)
    local w = champion:hasCondition(element.."_weakness")
    if not w then e[i] = 0 lastHurt[element][i] = 0 end
    local malus = math.min(0.5, 0.02*(t-lastHurt[element][i]))*r/100*(r+e[i])
    lastHurt[element][i] = t
    e[i] = e[i]+malus
    if e[i]>=1 and not w then champion:setCondition(element.."_weakness") end
  end
end
function weaknessTick(champion, element)
  local e = weakness[element]
  if e and champion:getEnabled() then
    local i = champion:getOrdinal()
    e[i] = e[i]*0.98
    return e[i]>=1
  end
end

-- money functions

money = {0,0,0,0}
function getMoney(champion) return money[champion:getOrdinal()] end 
function modifyMoney(amount, champion) setMoney(getMoney(champion)+amount, champion) end
function setMoney(amount, champion) money[champion:getOrdinal()] = amount<0 and 0 or amount playSound("money") end

function partyPay(cost)
  local each, total = {}, 0
  for i = 1,4 do
    local c = party.party:getChampionByOrdinal(i)
    if c and c:getEnabled() and c:isAlive() then
      local m = getMoney(c)
      each[#each+1] = {c, m}
      total = total+m
    end 
  end
  if total<cost then return false end
  repeat
    for i = #each,1,-1 do if each[i][2] == 0 then table.remove(each, i) end end
    local share = #each
    for _,c in ipairs(each) do
      local amount = spells_functions.script.quantum(cost/share)
      if c[2]<amount then amount = c[2] end
      c[2] = c[2]-amount
      money[c[1]:getOrdinal()] = c[2]
      cost = cost-amount
      share = share-1
    end
  until cost < 1
  playSound("money")
  return true
end

function shareAllMoney()
  local traders, total = {}, 0
  for i = 1,4 do
    local c = party.party:getChampionByOrdinal(i)
    if c and c:getEnabled() then
      total = total + money[i]
      money[i] = 0
      traders[#traders+1] = c
    end 
  end
  if total>0 then playSound("money") end
  while #traders>1 do
    local share = math.floor(total/#traders)
    money[traders[#traders]:getOrdinal()] = share
    traders[#traders] = nil
    total = total-share
  end
  money[traders[1]:getOrdinal()] = total
end

function getAllMoney(champion)
  local total = 0
  for i = 1,4 do
    local c = party.party:getChampionByOrdinal(i)
    if c and c:getEnabled() then
      total = total + money[i]
      money[i] = 0
    end 
  end
  money[champion:getOrdinal()] = total
  if total>0 then playSound("money") end
end

-- water bar functions

waterLevels = {700, 700, 700, 700}
function getWater(champion) return waterLevels[champion:getOrdinal()] end 
function consumeWater(amount, champion) modifyWater(-amount, champion) end
function modifyWater(amount, champion) setWater(getWater(champion)+amount, champion) end
function setWater(water, champion)
  water = water<0 and 0 or water>1000 and 1000 or water
  waterLevels[champion:getOrdinal()] = water
  if water<250 then
    if not champion:hasCondition("dehydration") then champion:setCondition("dehydration") end
  else
    if champion:hasCondition("dehydration") then champion:removeCondition("dehydration") end
  end
  --hudPrint(champion:getName().." food="..champion:getFood().." water="..water)
end

-- empty flask functions

function addFlask(potion, champion)
  local empty, pos, containers = 0, 0, {}
  for slot = ItemSlot.BackpackFirst,ItemSlot.BackpackLast do
    local item = champion:getItem(slot)
    if item then
      if item.go.name == "flask" then item:setStackSize(1+item:getStackSize()) return end
      if item.go.containeritem then containers[#containers+1] = {item.go.containeritem, slot, 0, 0} end
      if pos<1 and item.go.id == potion.id then pos = slot end
    else
      if empty<1 then empty = slot end
    end
  end
  for i = 1,#containers do
    for slot = 1,containers[i][1]:getCapacity() do
      local item = containers[i][1]:getItem(slot)
      if item then
        if item.go.name == "flask" then item:setStackSize(1+item:getStackSize()) return end
        if containers[i][3]<1 and item.go.id == potion.id then containers[i][3] = slot end
      else
        if containers[i][4]<1 then containers[i][4] = slot end
      end
    end
  end
  if potion.item:getStackSize()<2 then
    if pos>0 then delayedCall("champions", 0, "delayedFlask", champion:getOrdinal(), pos) return end
    for i = 1,#containers do
      if containers[i][3]>0 then
        delayedCall("champions", 0, "delayedFlask", champion:getOrdinal(), containers[i][2], containers[i][3]) return
      end
    end
  end
  if empty>0 then champion:insertItem(empty, spawn("flask").item) return end
  for i = 1,#containers do
    if containers[i][3]>0 then containers[i][1]:insertItem(containers[i][3], spawn("flask").item) return end
  end
  if getMouseItem() then
    if potion.item:getStackSize()<2 then
      delayedCall("champions", 0, "delayedFlask")
    else
      spawn("flask")
    end
  else
    setMouseItem(spawn("flask").item)
  end
end
function delayedFlask(ordinal, slot, slotInSlot)
  if slot then
    if slotInSlot then
      party.party:getChampionByOrdinal(ordinal):getItem(slot).go.containeritem:insertItem(slotInSlot, spawn("flask").item)
    else
      party.party:getChampionByOrdinal(ordinal):insertItem(slot, spawn("flask").item)
    end
  else
    setMouseItem(spawn("flask").item)
  end
end

-- enabled champions

function getEnabledChampions()
  local tab = {}
  if taverns.script.current then
    for i = 1,4 do
      if taverns.script.enabled[i] then tab[#tab+1] = party.party:getChampionByOrdinal(i) end
    end
  elseif shops.script.current then
    for i = 1,4 do
      if shops.script.enabled[i] then tab[#tab+1] = party.party:getChampionByOrdinal(i) end
    end
  else
    for i = 1,4 do
      local champ = party.party:getChampionByOrdinal(i)
      if champ:getEnabled() then tab[#tab+1] = champ end
    end
  end
  return tab
end

-- update

championUpdateIndex = 0
timeMoney = 50
timeXP = 50
timeShop = 50
timeLearn = 5
function updateChampions(dt)
  --print("------- update -------")
  shops.script.setLastSell()
  shops.script.setLastBuy()
  if championUpdateIndex >= #champions then
    shops.script.updateShops()
    championUpdateIndex = 0
    return
  else
    championUpdateIndex = championUpdateIndex < 0 and 1 or championUpdateIndex + 1
  end
  local c = champions[championUpdateIndex]
  if not c then return end
  if not c.plan then c.plan = plans[c.class] end
  if type(c.items) == "table" then
    local items = self.go:spawn("inventory")
    for i = 1,ItemSlot.MaxSlots do
      if c.items[i] then
        local item = createItem(c.items[i])
        if item then items.store:insertItem(i, item) end
      end
    end
    c.items = items.id
  end
  --print(c.name.." is champion "..c.index.."/"..#champions)
  if c.busy then
    local t = Time.currentTime()
    if c.busy > t then
      --print(c.name.." is busy")
      return
    end
    --print(c.name.." comes back from "..((c.moneyGoal or c.xpGoal) and "a quest!" or c.shopping and "shopping" or "learning skills"))
    if c.moneyGoal then
      c.money = c.money + (timeMoney + t-c.busy)/timeMoney*(0.5+math.random())*c.level*1000
      for i = 1,spells_functions.script.quantum(7*math.random()*c.level/(20+c.level)) do market.script.loot(c) end
    end
    if c.xpGoal then
      c.experience = c.xpGoal
      local lvl = level(c.experience)
      local up = lvl-c.level
      if up > 0 then
        c.skillPoints = c.skillPoints + up
        c.level = lvl
        for _,v in ipairs(c.traits) do
          if v == "mutation" then
            for i = 1,up do
              local stat = stats[math.random(#stats)]
              c.baseStats[stat] = c.baseStats[stat]+1
              c.currentStats[stat] = c.currentStats[stat]+1
              if stat == "strength" then c.maxLoad = c.maxLoad and c.maxLoad + 3 or c.currentStats[stat] * 3
              elseif stat == "dexterity" then c.evasion = (c.evasion or 0) + 5
              elseif stat == "vitality" then c.maxHealth = c.maxHealth + 5
              elseif stat == "willpower" then c.maxEnergy = c.maxEnergy + 5
              end
            end
            break
          end
        end
        for i = 1,up do levelUp(c) end
        learn(c)
      end
      market.script.loot(c)
    end
    if math.random() < (c.wandering or 0) then c.tavern = taverns.script.taverns[math.random(#taverns.script.taverns)] end
    c.busy = nil
    --print(c.name.." is back in "..c.tavern)
  else
    --print(c.name.." is available")
    c.moneyGoal = nil
    c.xpGoal = nil
    c.shopping = nil
    c.learning = nil
    dt = dt * (#champions+1)
    local resting = party.party:isResting()
    local champs = getEnabledChampions()
    if not c.tavern then c.tavern = taverns.script.taverns[math.random(#taverns.script.taverns)] end
    local tavern = findEntity(c.tavern).script.tavern
    --print(c.name.." is in tavern "..tavern.name)

    -- eat/drink
    local consume = dt * (resting and 1 or 0.001)
    c.food = c.food - consume
    c.water = c.water - consume
    if c.water < 250 or c.food < 0 then
      if c.money < tavern.menuCost then
        c.moneyGoal = true
      else
        c.money = c.money - tavern.menuCost
        c.food  = math.min(1000, c.food  + 250)
        c.water = math.min(1000, c.water + 250)
      end
    end

    -- heal/sleep
    local gain = dt/8/3600 * (resting and 1000 or 1)
    c.health = math.min(c.maxHealth, c.health + c.maxHealth*gain)
    c.energy = math.min(c.maxEnergy, c.energy + c.maxEnergy*gain)

    -- money/trade
    local shop = findEntity(shops.script.shops[math.random(#shops.script.shops)])
    shop = shop and shop.script.shop
    if shop and GameMode.getTimeOfDay()<1 then
      --print(c.name.." looks for goods in shop "..shop.name)
      local inv = findEntity(c.items)
      local rope,craftpotion = cleanInventory(c, inv, shop)
      local list = {}
      for _,item in ipairs(shop.items) do if item[2] > 0 then list[#list+1] = item end end
      local item = list[math.random(#list)]
      if item then
        local o = shops.script.getObject(item)
        if o.spellscrollitem then
          local spell = spells_functions.script.defByName[o.spellscrollitem:getSpell()]
          if spell and canUse(c, spell.requirements) and not hasSpell(c, spell.name) then
            if buy(c, inv, nil, nil, o, item, shop) then c.spells[#c.spells+1] = spell.name end
          end
        elseif o.craftpotioncustom then
          if not craftpotion and ((c.skillLevels["alchemy"] or 0) > 0 or hasTrait(c, "alchemist")) then
            buy(c, inv, nil, nil, o, item, shop)
          end
        elseif o.name == "flask" then
          if (c.skillLevels["alchemy"] or 0) > 0 or hasTrait(c, "alchemist") then buy(c, inv, nil, nil, o, item, shop, 7) end
        elseif o.item:hasTrait("herb") then
          if (c.skillLevels["alchemy"] or 0) > 0 or hasTrait(c, "alchemist") then buy(c, inv, nil, nil, o, item, shop, 30) end
        elseif o.item:hasTrait("potion") then
          if     o.name == "potion_strength"  then statShopping(c, "strength" , o, item, inv, shop)
          elseif o.name == "potion_dexterity" then statShopping(c, "dexterity", o, item, inv, shop)
          elseif o.name == "potion_vitality"  then statShopping(c, "vitality" , o, item, inv, shop)
          elseif o.name == "potion_willpower" then statShopping(c, "willpower", o, item, inv, shop)
          else buy(c, inv, nil, nil, o, item, shop, 3) end
        elseif o.ropetool then
          if not rope then buy(c, inv, nil, nil, o, item, shop) end
        elseif o.name == "lock_pick" then
          buy(c, inv, nil, nil, o, item, shop, 7)
        elseif o.usableitem then
          local f,w = food_functions.script.getNutritionValue(o.name),food_functions.script.getHydrationValue(o.name)
          if (f>0 or w>0) and (c.food+f<=1000 and c.water+w<=1000) and buy(c, inv, 0, nil, o, item, shop) then
            c.food = c.food+f
            c.water = c.water+w
            if o.name == "water_flask" then sell(c, spawn("flask"), shop) end
            if o.map then o:destroy() end
          end
        elseif o.containeritem then
          local empty,slot,current,wslot,worst = getInventoryEmptySpace(c, inv)
          if empty < 9 and slot then
            if current then inv.store:removeItemFromSlot(slot) end
            if buy(c, inv, slot, nil, o, item, shop) then if current then o.containeritem:addItem(current.item) end
            elseif current then inv.store:insertItem(slot, current.item) end
          elseif worst and isBetterContainer(o, worst) then
            inv.store:removeItemFromSlot(wslot)
            if buy(c, inv, wslot, nil, o, item, shop) then
              for s = 1,worst.containeritem:getCapacity() do
                local item = worst.containeritem:getItem(s)
                if item then worst.containeritem:removeItemFromSlot(s) o.containeritem:insertItem(s, item) end
              end
              sell(c, worst, shop)
            else inv.store:insertItem(wslot, worst.item) end            
          end
        else
          local slot = market.script.getSlot(o)
          if slot then
            --print(item[1].." goes in slot "..(slot or "nil"))
            local current = inv.store:getItem(slot)
            current = current and current.go
            if isUpgradeTo(c, o, current) then buy(c, inv, slot, current, o, item, shop) end
          elseif canUseItem(c, o) then
            local h1,h2 = inv.store:getItem(ItemSlot.Weapon),inv.store:getItem(ItemSlot.OffHand)
            h1,h2 = h1 and h1.go,h2 and h2.go
            local l1,l2 = legalHands(c, o, h2),legalHands(c, h1, o)
            if h1 and h2 then
              if l1 and l2 then
                local v1,v2 = market.script.getUseValue(c, h1),market.script.getUseValue(c, h2)
                local current,s
                if v1<v2 then current,s = h1,ItemSlot.Weapon else current,s = h2,ItemSlot.OffHand end
                if isUpgradeTo(c, o, current) then buy(c, inv, s, current, o, item, shop, 40) end
              elseif l1 then
                if isUpgradeTo(c, o, h1) then buy(c, inv, ItemSlot.Weapon, h1, o, item, shop, 40) end
              elseif l2 then
                if isUpgradeTo(c, o, h2) then buy(c, inv, ItemSlot.OffHand, h2, o, item, shop, 40) end
              elseif legalHands(c, o) and isUpgradeTo(c, o, h1) and isUpgradeTo(c, o, h2) then
                local o1,o2,s1,s2
                if     h1.name == o.name   then o1,o2,s1,s2 = h1,h2,ItemSlot.Weapon,ItemSlot.OffHand
                elseif h2.name == o.name   then o1,o2,s1,s2 = h2,h1,ItemSlot.OffHand,ItemSlot.Weapon
                elseif math.random() < 0.5 then o1,o2,s1,s2 = h1,h2,ItemSlot.Weapon,ItemSlot.OffHand
                else                            o1,o2,s1,s2 = h2,h1,ItemSlot.OffHand,ItemSlot.Weapon
                end
                if buy(c, inv, s1, o1, o, item, shop, 40) then sell(c, o2, shop) inv.store:removeItemFromSlot(s2) end
              end
            elseif h1 then
              if l2 then
                if isUpgradeTo(c, o, h2) then buy(c, inv, ItemSlot.OffHand, h2, o, item, shop, 40) end
              elseif l1 then
                if isUpgradeTo(c, o, h1) then buy(c, inv, ItemSlot.Weapon, h1, o, item, shop, 40) end
              end
            elseif h2 then
              if l1 then
                if isUpgradeTo(c, o, h1) then buy(c, inv, ItemSlot.Weapon, h1, o, item, shop, 40) end
              elseif l2 then
                if isUpgradeTo(c, o, h2) then buy(c, inv, ItemSlot.OffHand, h2, o, item, shop, 40) end
              end
            elseif l1 then
              buy(c, inv, math.random() < 0.5 and ItemSlot.Weapon or ItemSlot.OffHand, nil, o, item, shop, 40)
            end
          end
        end
      end
    end
    if not c.moneyGoal then
      if c.money < c.level/2 then
        c.moneyGoal = true
      else
        local moneyGoal = -c.money
        for _,champ in ipairs(champs) do moneyGoal = moneyGoal + getMoney(champ)/#champs end
        if moneyGoal > 0 then c.moneyGoal = true end
      end
    end

    -- xp
    local flvl = floatLevel(c.experience)
    local lvlGoal = (c.deltaLevel or 0)-flvl
    for _,champ in ipairs(champs) do lvlGoal = lvlGoal + (floatLevel(champ:getExp())-getDeltaLevel(champ))/#champs end
    if lvlGoal > math.random() then c.xpGoal = xp(flvl+lvlGoal) end
    learn(c)

    local duration = (c.moneyGoal and timeMoney or 0) + (c.xpGoal and timeXP or 0) + (c.shopping and timeShop or 0) + timeLearn*(c.learning or 0)
    if duration > 0 then c.busy = Time.currentTime() + duration end
    --print(c.name.." end of turn")
  end
end

-- shopping parts cut...

-- potion craft ui

function craftPotion(mortar, champion)
  local mord = "mortar"..champion:getOrdinal()
  local slot
  for s = 1,ItemSlot.MaxSlots do if champion:getItem(s) == mortar.item then slot = s break end end
  if not slot then hudPrint("Cannot be used inside a container.") return end
  if get(mord) == slot then set(mord, nil) else set(mord, slot) end
  --print("craft potion ok for slot "..slot)
end

-- champions definitions

skillPlan = {
  alchemy = {
    {"alchemy",5},
  },
  critical = {
    {"critical",5},
  },
  defense = {
    {"athletics",5},
    {"armors",2},
    {"dodge",5},
    {"armors",5},
  },
  dualWield = {
    {"light_weapons",5},
    {"critical",5},
    {"accuracy",5},
    {"dodge",5},
  },
  firearm = {
    {"firearms",5},
    {"critical",5},
    {"accuracy",5},
    {"dodge",5},
  },
  frontHeavy = {
    {"heavy_weapons",5},
    {"armors",5},
    {"accuracy",5},
  },
  frontMage = {
    {"concentration",5},
    {"armors",5},
  },
  heal = {
    {"concentration",1},
    {"water_magic",1},
    {"earth_magic",1},
    {"water_magic",2},
    {"earth_magic",2},
    {"water_magic",3},
    {"earth_magic",3},
    {"concentration",2},
    {"water_magic",4},
    {"earth_magic",4},
    {"water_magic",5},
    {"earth_magic",5},
    {"concentration",3},
    {"air_magic",1},
    {"concentration",4},
    {"air_magic",2},
    {"concentration",5},
    {"air_magic",5},
    {"fire_magic",5},
  },
  magic = {
    {"concentration",1},
    {"fire_magic",1},
    {"air_magic",1},
    {"water_magic",1},
    {"earth_magic",1},
    {"concentration",2},
    {"air_magic",2},
    {"water_magic",2},
    {"earth_magic",2},
    {"fire_magic",2},
    {"concentration",3},
    {"water_magic",3},
    {"earth_magic",3},
    {"fire_magic",3},
    {"air_magic",3},
    {"concentration",4},
    {"earth_magic",4},
    {"fire_magic",4},
    {"air_magic",4},
    {"water_magic",4},
    {"concentration",5},
    {"fire_magic",5},
    {"air_magic",5},
    {"water_magic",5},
    {"earth_magic",5},
  },
  magicFAC = {
    {"fire_magic",1},
    {"air_magic",1},
    {"concentration",1},
    {"air_magic",2},
    {"fire_magic",2},
    {"concentration",2},
    {"fire_magic",3},
    {"air_magic",3},
    {"concentration",3},
    {"air_magic",4},
    {"fire_magic",4},
    {"concentration",4},
    {"fire_magic",5},
    {"air_magic",5},
    {"concentration",5},
  },
  magicFWC = {
    {"fire_magic",1},
    {"water_magic",1},
    {"concentration",1},
    {"water_magic",2},
    {"fire_magic",2},
    {"concentration",2},
    {"fire_magic",3},
    {"water_magic",3},
    {"concentration",3},
    {"water_magic",4},
    {"fire_magic",4},
    {"concentration",4},
    {"fire_magic",5},
    {"water_magic",5},
    {"concentration",5},
  },
  missile = {
    {"missile_weapons",5},
    {"critical",5},
    {"accuracy",5},
    {"dodge",5},
  },
  reach = {
    {"accuracy",2},
  },
  throw = {
    {"throwing",5},
    {"accuracy",5},
    {"critical",5},
    {"dodge",5},
  },
}

plans = {
  -- class standard
  fighter = {
    stats = {strength = 1, dexterity = 0.75, vitality = 0.75, willpower = 0.5},
    skills = {skillPlan.frontHeavy, skillPlan.critical, skillPlan.defense, skillPlan.throw},
  },
  barbarian = {
    stats = {strength = 1, dexterity = 0.75, vitality = 0.5, willpower = 0.25},
    skills = {skillPlan.frontHeavy, skillPlan.throw, skillPlan.defense},
  },
  knight = {
    stats = {strength = 1, dexterity = 0.75, vitality = 0.75, willpower = 0.5},
    skills = {skillPlan.frontHeavy, skillPlan.defense, skillPlan.heal},
  },
  rogue = {
    stats = {strength = 0.75, dexterity = 1, vitality = 0.75, willpower = 0.5},
    skills = {skillPlan.reach, skillPlan.dualWield, skillPlan.missile, skillPlan.defense},
  },
  battle_mage = {
    stats = {strength = 0.5, dexterity = 0.5, vitality = 0.75, willpower = 1},
    skills = {skillPlan.frontMage, skillPlan.magic, skillPlan.defense, skillPlan.reach},
  },
  wizard = {
    stats = {strength = 0.25, dexterity = 0.25, vitality = 0.75, willpower = 1},
    skills = {skillPlan.magic, skillPlan.defense, skillPlan.reach},
  },
  alchemist = {
    stats = {strength = 0.75, dexterity = 1, vitality = 0.75, willpower = 0.75},
    skills = {skillPlan.alchemy, skillPlan.firearm, skillPlan.defense},
  },
  farmer = {
    stats = {strength = 0.25, dexterity = 0.25, vitality = 0.75, willpower = 1},
    skills = {skillPlan.magic, skillPlan.defense, skillPlan.reach},
  },
  
  -- other templates
  archer = {
    stats = {strength = 0.25, dexterity = 1, vitality = 0.75, willpower = 0.5},
    skills = {skillPlan.missile, skillPlan.defense},
  },
  healer = {
    stats = {strength = 0.25, dexterity = 0.25, vitality = 0.75, willpower = 1},
    skills = {skillPlan.heal, skillPlan.defense, skillPlan.reach},
  },
  ninja = {
    stats = {strength = 1, dexterity = 0.75, vitality = 0.75, willpower = 0.5},
    skills = {skillPlan.throw, skillPlan.defense},
  },
  shooter = {
    stats = {strength = 1, dexterity = 0.75, vitality = 0.75, willpower = 0.5},
    skills = {skillPlan.firearm, skillPlan.defense},
  },
}

function randomOrder(tab)
  local result = {}
  for i = 1,#tab do table.insert(result, math.random(i), tab[i]) end
  return result
end

champions = randomOrder(champions)
for i = 1,#champions do
  champions[i].index = i
  champions[i].food = math.random(1000)
  champions[i].water = math.random(1000)
end
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Alchemy questions + discussion

Post by AndakRainor »

If you look at all this code, you will see I added ingredient 0 to the recipes, that is the water flask ! But I did not really work on enhancing alchemy game play so far, only the system and the water flask aspect, so if any one is kind enough to share some beautiful new herbs models or textures or whatever, that would be great :)
User avatar
akroma222
Posts: 1029
Joined: Thu Oct 04, 2012 10:08 am

Re: Alchemy questions + discussion

Post by akroma222 »

AndakRainor wrote:If you look at all this code, you will see I added ingredient 0 to the recipes, that is the water flask ! But I did not really work on enhancing alchemy game play so far, only the system and the water flask aspect, so if any one is kind enough to share some beautiful new herbs models or textures or whatever, that would be great :)
Hey AndakRainor!
Sorry for the late reply, Ive been away house sitting for a few days
I will read over your posts today (hopefully) or tomorrow and get a handle on whats happening

Also, I have a bunch of retextured herbs from LoG1 (a recoloured duplicate of each of the base herbs - no new models unfortunately) that ill post here too
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Alchemy questions + discussion

Post by AndakRainor »

Here you go ! Usual link: http://ge.tt/9HPTdWE2

I extracted the useful code and made a map to test things. I think nothing is missing... but some function here and there may be not used. I had to keep some related systems I changed that were closely linked to alchemy; the water bar for champions and also the real time day/night cycle, because it deals with hunger and thirst.

I believe the code is now small enough so you should be able to take or remove what you want... If you find it hard, take it as an opportunity to learn coding :lol:

(or ask me... and pretend that was a joke :twisted: )

edit: Also, keep in mind this is a work in progress... Some aspects of the gui may not be ideal, but we have this thread to discuss any potential improvements!
User avatar
Eleven Warrior
Posts: 752
Joined: Thu Apr 18, 2013 2:32 pm
Location: Australia

Re: Alchemy questions + discussion

Post by Eleven Warrior »

Here is one for ya guys.

Thank you to ArdTru he has made new portable crafting system. It uses the Dm Toolbox as the crafting tool. What this means is now when your on the run and need to craft a item like a (torch) you can. Simular to the Mortar thing. You will of course need items to make the Torch eg:

Code: Select all

{
	name = "torch",
	UIname = "Torch",
	source = { branch_torch = 1,cloth_rag_torch = 1,oil_flask_torch =1},
	target = { torch = {1,100}},
	effect   = {timer = 1, eff = "Normal"},
	--need = {"rock","rope"},
	exp = 5,
	kind = "Common"
},
I have asked a few people here and AndakRainor gave us a good potion crafting system Thxs man :) I glad ArdTru made this system after asking a few people.

He will in time release this for all to use.

This toolbox system will allow the player to make anything possible on the run. 10/10 for this system. Opens up new things for everything.

I am now going to use those 3d models of flowers berry bushes etc... that I got for free and create a new potion system, simular to The Quest game.

My god this is what I was so desperate for so thxs again ArdTur awesome work as usual :)
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Alchemy questions + discussion

Post by AndakRainor »

Very interesting! I will propose an update for the alchemy system with similar code for the ingredients, as Akroma pointed a problem with the recipes definitions by numbers: it does not work for more than 10 different ingredients with 1 digit.
User avatar
AndakRainor
Posts: 674
Joined: Thu Nov 20, 2014 5:18 pm

Re: Alchemy questions + discussion

Post by AndakRainor »

I have a new version to test on Nexus here : http://www.nexusmods.com/legendofgrimrock2/mods/60?

The mortar now has 10 slots, its gui can not be dragged anymore, it accepts any item with the traits "herb" or "ingredient", and the recipes are defined with more useful data (and don't need to be used with the default defineRecipe() function of the game)

some examples of what you can write easily for your recipes:

Code: Select all

{
  potion = "potion_energy",
  level = 1,
  ingredients = {etherweed = 1, water_flask = 1},
  upgrade = {trait = "improved_alchemy", potion = "potion_greater_energy", quantity = 1},
},

{
  potion = "potion_oblivion",
  level = 1,
  ingredients = {mudwort = 3, falconskyre = 3, blackmoss = 4, water_flask = 1},
},

{
  potion = "shock_bomb",
  level = 4,
  ingredients = {falconskyre = 1, blackmoss = 1},
  upgrade = {trait = "bomb_expert", potion = "shock_bomb", quantity = 3},
},
just respect one thing for your recipes list; group them by level with ascending order, it is the order used by the mortar script to find a matching recipe (it stops on a recipe of higher level than the champion).
Post Reply