text:
local rules = {}

-- Function for rolling dice following a formula.
-- Examples: 2d4+2, d6, 1d10, 2d10-1, d4+1
function rules.roll_dice(formula)
  -- find the 'd' symbol (necessary) and the operator '+' or '-' symbols (optional, default is 1)
  d = formula:find('d')
  operator = formula:find('[+-]')

  quantity = 1
  faces = 1
  sum = 0

  -- dice quantity (optional)
  quantity = tonumber(formula:sub(0, d-1))
  if quantity == nil or quantity <= 0 then
    quantity = 1
  end

  -- faces in dice (necessary)
  if operator ~= nil then
    faces = tonumber(formula:sub(d+1, operator-1))

    -- sum (optional, it is there only if there is a '+' or '-' operator)
    if formula:sub(operator, operator) == '+' then
      sum = tonumber(formula:sub(operator+1, formula:len()))
    elseif formula:sub(operator, operator) == '-' then
      sum = tonumber(formula:sub(operator, formula:len()))
    end

  else
    faces = tonumber(formula:sub(d+1, formula:len()))
  end

  local result = 0
  for i = 1, quantity do
    local die = math.random(1, faces)
    result = result + die
  end
  return result + sum
end

-- weapons & armor

rules.armor = {
  unarmored         = { name = "Unarmored",       ac = 10, type = "unarmored", icon = {x = 16*0, y = 16*4}, desc = "" },

  gambeson          = { name = "Gambeson",        ac = 14, type = "padded", icon = {x = 16*1, y = 16*4}, desc = "Textile armor made of many layers of linen." },
  leather_armor     = { name = "Leather Armor",   ac = 14, type = "padded", icon = {x = 16*2, y = 16*4}, desc = "Boiled hardened leather plates." },

  chain_mail        = { name = "Hauberk",         ac = 16, type = "chain", icon = {x = 16*3,  y = 16*4}, desc = "Riveted chain armor worn over padding." },
  scale_mail        = { name = "Scale Armor",     ac = 16, type = "chain", icon = {x = 16*4,  y = 16*4}, desc = "Armor made of bronze scales." },

  brigandine        = { name = "Brigandine",      ac = 18, type = "plate", icon = {x = 16*5,  y = 16*4}, desc = "Steel plates attached to a textile cover." },
  lamellar_armor    = { name = "Lamellar Armor",  ac = 18, type = "plate", icon = {x = 16*6,  y = 16*4}, desc = "Armor made of square steel parts sown together." },
  cuirass           = { name = "Cuirass",         ac = 18, type = "plate", icon = {x = 16*7,  y = 16*4}, desc = "Steel armor for the breast and the back." },
  full_plate_mail   = { name = "Full Plate Mail", ac = 20, type = "plate", icon = {x = 16*8,  y = 16*4}, desc = "Complete steel knightly armor." },

  dragon_scale_mail = { name = "Dragon Mail",     ac = 20, type = "chain", icon = {x = 16*10, y = 16*4}, desc = "Armor made from the scales of a dragon." },

  skeleton_bones    = { name = "Skeleton Bones",  ac = 12, type = "plate", icon = {x = 16*0,  y = 16*0}, desc = "Skeletons have a special protection due to the constitution of their fleshless bones." },
  dragon_scales     = { name = "Dragon Scales",   ac = 16, type = "chain", icon = {x = 16*0,  y = 16*0}, desc = "Dragon scales give it a formidable protection from weapons." },
  imp_scales        = { name = "Imp Scales",      ac = 12, type = "chain", icon = {x = 16*0,  y = 16*0}, desc = "Imp scales give it an improved protection from weapons." },
  crystal_shell     = { name = "Crystal Shell",   ac = 20, type = "plate", icon = {x = 16*0,  y = 16*0}, desc = "Crystal shell of the neather crystal." },
}

rules.shield = {
  no_shield = { name = "No Shield", ac_bonus = 0, icon = {x = 16*0, y = 16*5}, desc = "" },
  shield    = { name = "Shield",    ac_bonus = 3, icon = {x = 16*1, y = 16*5}, desc = "A wooden shield fortified with boiled leather and illustrated with heraldry." },
}

--
-- armor adjustments: is the bonus to hit with the weapon against an opponent wearing that type of armor.
-- size:
--    1 short
--    2 medium length
--    3 long
--    4 very long. Reach weapon.
-- weight:
--    1 light weight, easy to handle and has center of gravity closer to the hands.
--    2 medium weight, slower to handle, center of gravity closer to the tip.
--    3 heavy weight and slow to handle, center of gravity closer to the tip.
-- bulk:  -- not implemented
--    0 not bulky at all. Does not take inventory space.
--    1 small and very easy to carry. May be kept hidden.
--    2 may carry sheathed or hanging in the belt.
--    3 bulky, very large and difficult to put away on your person. Must carry only one.
-- ranged: ranged weapon.
-- gun: guns ignore some armor class when attacking.
-- cutthroat: may be used to inflict coup de grace.
rules.weapon = {
  unarmed           = { name = "Unarmed",             damage = "1d1",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 0, plate = 0 }, hands = 1, ac_bonus = 0, size = 1, weight = 1, bulk = 0, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*0, y = 16*0}, effect = "",              desc = "" },

  hammer            = { name = "Warhammer",           damage = "1d4",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 4, plate = 4 }, hands = 1, ac_bonus = 0, size = 2, weight = 3, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*1, y = 16*1}, effect = "",              desc = "Bludgeoning weapon with a sharp tip effective against armor." },
  dagger            = { name = "Dagger",              damage = "1d4",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 0, plate = 0 }, hands = 1, ac_bonus = 0, size = 1, weight = 1, bulk = 1, ranged = false, range = 1, gun = false, cutthroat = true , ammo_category = "",              icon = {x = 16*1, y = 16*0}, effect = "",              desc = "A small, handy blade." },

  mace              = { name = "Mace",                damage = "1d6",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 2, plate = 2 }, hands = 1, ac_bonus = 0, size = 2, weight = 2, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*4, y = 16*1}, effect = "",              desc = "A steel bludgeon with knobs to penetrate armor." },
  spear             = { name = "Spear",               damage = "1d6",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 0, plate = 0 }, hands = 1, ac_bonus = 0, size = 3, weight = 1, bulk = 3, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*1, y = 16*2}, effect = "",              desc = "Two handed thrusting weapon." },
  quarterstaff      = { name = "Quarterstaff",        damage = "1d6",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 0, plate = 0 }, hands = 2, ac_bonus = 1, size = 3, weight = 1, bulk = 3, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*5, y = 16*1}, effect = "",              desc = "Bludgeoning two handed weapon effective at parrying." },

  axe               = { name = "Axe",                 damage = "1d8",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 2, plate = 0 }, hands = 1, ac_bonus = 0, size = 2, weight = 3, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*3, y = 16*1}, effect = "",              desc = "A top-heavy bladed weapon." },
  arming_sword      = { name = "Arming Sword",        damage = "1d8",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 0, plate = 0 }, hands = 1, ac_bonus = 1, size = 2, weight = 1, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*2, y = 16*0}, effect = "",              desc = "Knightly one handed sword." },
  falcion           = { name = "Falcion",             damage = "1d8",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 4, chain = 0, plate = 0 }, hands = 1, ac_bonus = 1, size = 2, weight = 1, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*5, y = 16*0}, effect = "",              desc = "Broad, sharp, single-edged blade." },
  morning_star      = { name = "Morning Star",        damage = "1d8",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 2, plate = 0 }, hands = 2, ac_bonus = 0, size = 3, weight = 3, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*2, y = 16*1}, effect = "",              desc = "A mace with spikes." },
  maul              = { name = "Maul",                damage = "1d8",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 2, plate = 2 }, hands = 2, ac_bonus = 0, size = 3, weight = 3, bulk = 3, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*3, y = 16*2}, effect = "",              desc = "A two handed hammer." },
  estoc             = { name = "Estoc",               damage = "1d8",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 4, chain = 4, plate = 2 }, hands = 2, ac_bonus = 1, size = 3, weight = 1, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*6, y = 16*0}, effect = "",              desc = "A long two-handed sword with a sharp point used for thrusting" },
  pike              = { name = "Pike",                damage = "1d8",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 0, plate = 0 }, hands = 2, ac_bonus = 0, size = 4, weight = 2, bulk = 3, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*2, y = 16*2}, effect = "",              desc = "A long spear." },

  long_sword        = { name = "Long Sword",          damage = "1d10",   to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 2, plate = 2 }, hands = 2, ac_bonus = 1, size = 3, weight = 1, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*3, y = 16*0}, effect = "",              desc = "Long, two-handed knightly sword." },
  bill              = { name = "Bill",                damage = "1d10",   to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 0, plate = 0 }, hands = 2, ac_bonus = 0, size = 4, weight = 2, bulk = 3, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*4, y = 16*2}, effect = "",              desc = "Bladed pole arm with a hook." },
  halberd           = { name = "Halberd",             damage = "1d10",   to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 2, plate = 0 }, hands = 2, ac_bonus = 0, size = 4, weight = 2, bulk = 3, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*5, y = 16*2}, effect = "",              desc = "Bladed pole arm with a lance point." },
  pollaxe           = { name = "Pollaxe",             damage = "1d10",   to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 2, plate = 4 }, hands = 2, ac_bonus = 0, size = 4, weight = 3, bulk = 3, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*6, y = 16*2}, effect = "",              desc = "Knightly pole arm with a hammer head and an axe head." },
  bec_de_corbin     = { name = "Bec de Corbin",       damage = "1d10",   to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 2, plate = 4 }, hands = 2, ac_bonus = 0, size = 4, weight = 3, bulk = 3, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*7, y = 16*2}, effect = "",              desc = "Pole arm with a hammer head and a long sharp tip." },

  sling             = { name = "Sling",               damage = "1d4",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 0, plate = 0 }, hands = 2, ac_bonus = 0, size = 0, weight = 0, bulk = 1, ranged = true,  range = 6, gun = false, cutthroat = false, ammo_category = "sling_bullet",  icon = {x = 16*1, y = 16*3}, effect = "",              desc = "Ranged weapon that shoots sling bullets." },
  short_bow         = { name = "Short Bow",           damage = "1d6",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 0, plate = 0 }, hands = 2, ac_bonus = 0, size = 0, weight = 0, bulk = 3, ranged = true,  range = 6, gun = false, cutthroat = false, ammo_category = "arrow",         icon = {x = 16*2, y = 16*3}, effect = "",              desc = "Ranged weapon that shoots arrows." },
  long_bow          = { name = "Long Bow",            damage = "1d8",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 0, plate = 0 }, hands = 2, ac_bonus = 0, size = 0, weight = 0, bulk = 3, ranged = true,  range = 6, gun = false, cutthroat = false, ammo_category = "arrow",         icon = {x = 16*3, y = 16*3}, effect = "",              desc = "Powerful ranged weapon that shoots arrows." },
  crossbow          = { name = "Crossbow",            damage = "1d10",   to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 0, plate = 0 }, hands = 2, ac_bonus = 0, size = 0, weight = 0, bulk = 3, ranged = true,  range = 6, gun = false, cutthroat = false, ammo_category = "bolt",          icon = {x = 16*4, y = 16*3}, effect = "",              desc = "Ranged weapon that shoots crossbow bolts." },
  pistol            = { name = "Pistol",              damage = "1d12",   to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 0, plate = 0 }, hands = 1, ac_bonus = 0, size = 0, weight = 0, bulk = 2, ranged = true,  range = 6, gun = true,  cutthroat = false, ammo_category = "gun_bullet",    icon = {x = 16*5, y = 16*3}, effect = "",              desc = "One handed weapon that shoots gun bullets." },
  arquebus          = { name = "Arquebus",            damage = "1d12",   to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 0, plate = 0 }, hands = 2, ac_bonus = 0, size = 0, weight = 0, bulk = 3, ranged = true,  range = 6, gun = true,  cutthroat = false, ammo_category = "gun_bullet",    icon = {x = 16*6, y = 16*3}, effect = "",              desc = "Two handed weapon that shoots gun bullets." },

  muramasa          = { name = "Muramasa +1",         damage = "1d10+1", to_hit_bonus = 1, armor_adjustment = { unarmored = 0, padded = 4, chain = 0, plate = 0 }, hands = 2, ac_bonus = 1, size = 3, weight = 1, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*8, y = 16*0}, effect = "muramasa",      desc = "A magic two-handed sword that drinks the blood of its enemies. It thirsts." },
  muramasa_a        = { name = "Muramasa +2",         damage = "1d10+2", to_hit_bonus = 2, armor_adjustment = { unarmored = 0, padded = 4, chain = 0, plate = 0 }, hands = 2, ac_bonus = 1, size = 3, weight = 1, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*8, y = 16*0}, effect = "muramasa",      desc = "A magic two-handed sword that drinks the blood of its enemies. It regained some power." },
  muramasa_b        = { name = "Muramasa +3",         damage = "1d10+3", to_hit_bonus = 3, armor_adjustment = { unarmored = 0, padded = 4, chain = 0, plate = 0 }, hands = 2, ac_bonus = 1, size = 3, weight = 1, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*8, y = 16*0}, effect = "muramasa",      desc = "A magic two-handed sword that drinks the blood of its enemies. It yearns for more blood." },
  muramasa_c        = { name = "Muramasa +4",         damage = "1d10+4", to_hit_bonus = 4, armor_adjustment = { unarmored = 0, padded = 4, chain = 0, plate = 0 }, hands = 2, ac_bonus = 1, size = 3, weight = 1, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*8, y = 16*0}, effect = "muramasa",      desc = "A magic two-handed sword that drinks the blood of its enemies. It has become fat with blood." },
  muramasa_d        = { name = "Muramasa +5",         damage = "1d10+5", to_hit_bonus = 5, armor_adjustment = { unarmored = 0, padded = 4, chain = 0, plate = 0 }, hands = 2, ac_bonus = 1, size = 3, weight = 1, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*8, y = 16*0}, effect = "muramasa",      desc = "A magic two-handed sword that drinks the blood of its enemies. It has achieved its maximum power." },

  bastard_sword     = { name = "Bastard Sword",       damage = "2d4+2",  to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 2, plate = 2 }, hands = 1, ac_bonus = 1, size = 3, weight = 1, bulk = 2, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*10, y = 16*0},effect = "",              desc = "A long sword that can be used with one hand." },

  rat_tooth         = { name = "Rat Tooth",           damage = "1d1",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 0, chain = 0, plate = 0 }, hands = 1, ac_bonus = 0, size = 1, weight = 1, bulk = 0, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*0, y = 16*0}, effect = "rat_desease",   desc = "The tooth and claws of a plague rat has a chance to poison its victim." },
  steel_cutlery     = { name = "Steel Cutelry",       damage = "1d4",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 0, plate = 0 }, hands = 1, ac_bonus = 0, size = 1, weight = 1, bulk = 1, ranged = false, range = 1, gun = false, cutthroat = true , ammo_category = "",              icon = {x = 16*5, y = 16*10},effect = "",              desc = "Steel cutlery, fork and knife." },
  silver_cutlery    = { name = "Silver Cutelry",      damage = "1d4",    to_hit_bonus = 1, armor_adjustment = { unarmored = 0, padded = 2, chain = 0, plate = 0 }, hands = 1, ac_bonus = 0, size = 1, weight = 1, bulk = 1, ranged = false, range = 1, gun = false, cutthroat = true , ammo_category = "",              icon = {x = 16*4, y = 16*10},effect = "silver_attack", desc = "Silver cutlery, fork and knife. Silver weapons deal an additional 1d6 damage and may cause poiton to neather creatures." },
  strawberry_tooth  = { name = "Strawberry Tooth",    damage = "1d4",    to_hit_bonus = 0, armor_adjustment = { unarmored = 0, padded = 2, chain = 0, plate = 0 }, hands = 1, ac_bonus = 0, size = 1, weight = 1, bulk = 0, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*0, y = 16*0}, effect = "",              desc = "The teeth of a strawberry monster are as sharp as knives." },
  wail              = { name = "Wail",                damage = "1d4",    to_hit_bonus = 0, armor_adjustment = { unarmored = 2, padded = 6, chain = 8, plate = 10}, hands = 1, ac_bonus = 0, size = 1, weight = 1, bulk = 0, ranged = false, range = 1, gun = false, cutthroat = false, ammo_category = "",              icon = {x = 16*0, y = 16*0}, effect = "",              desc = "A ghost wail pierces though a living being's soul to torment it's heart." },
}

rules.ammo = {
  no_ammo       = { name = "No ammo",       category = "",              damage_bonus = 0, range_bonus = 0, stack_capacity = 60, icon = {x = 16*0, y = 16*1}, projectile_effect = "",       desc = "" },
  arrow         = { name = "Arrow",         category = "arrow",         damage_bonus = 0, range_bonus = 0, stack_capacity = 60, icon = {x = 16*2, y = 16*5}, projectile_effect = "arrow",  desc = "Arrows for bows." },
  bolt          = { name = "Bolt",          category = "bolt",          damage_bonus = 0, range_bonus = 0, stack_capacity = 60, icon = {x = 16*3, y = 16*0}, projectile_effect = "bolt",   desc = "Crossbow bolts." },


text:
data:extend(
{
  {
    type = "optimized-particle",
    name = "zircon-particle",
    flags = {"not-on-map"},
    life_time = 180,
    pictures =
    {
      {
        filename = "__bzzirconium__/graphics/entity/zircon-particle/zircon-particle-1.png",
        priority = "extra-high",
        width = 16,
        height = 16,
        frame_count = 1,
        hr_version =
        {
          filename = "__bzzirconium__/graphics/entity/zircon-particle/hr-zircon-particle-1.png",
          priority = "extra-high",
          width = 32,
          height = 32,
          frame_count = 1,
          scale = 0.5
        }
      },
      {
        filename = "__bzzirconium__/graphics/entity/zircon-particle/zircon-particle-2.png",
        priority = "extra-high",
        width = 16,
        height = 16,
        frame_count = 1,
        hr_version =
        {
          filename = "__bzzirconium__/graphics/entity/zircon-particle/hr-zircon-particle-2.png",
          priority = "extra-high",
          width = 32,
          height = 32,
          frame_count = 1,
          scale = 0.5
        }
      },
      {
        filename = "__bzzirconium__/graphics/entity/zircon-particle/zircon-particle-3.png",
        priority = "extra-high",
        width = 16,
        height = 16,
        frame_count = 1,
        hr_version =
        {
          filename = "__bzzirconium__/graphics/entity/zircon-particle/hr-zircon-particle-3.png",
          priority = "extra-high",
          width = 32,
          height = 32,
          frame_count = 1,
          scale = 0.5
        }
      },
      {
        filename = "__bzzirconium__/graphics/entity/zircon-particle/zircon-particle-4.png",
        priority = "extra-high",
        width = 16,
        height = 16,
        frame_count = 1,
        hr_version =
        {
          filename = "__bzzirconium__/graphics/entity/zircon-particle/hr-zircon-particle-4.png",
          priority = "extra-high",
          width = 32,
          height = 32,
          frame_count = 1,
          scale = 0.5
        }
      }
    },
    shadows =
    {
      {
        filename = "__bzzirconium__/graphics/entity/zircon-particle/zircon-particle-shadow-1.png",
        priority = "extra-high",
        width = 16,
        height = 16,
        frame_count = 1,
        hr_version =
        {
          filename = "__bzzirconium__/graphics/entity/zircon-particle/hr-zircon-particle-shadow-1.png",
          priority = "extra-high",
          width = 32,
          height = 32,
          frame_count = 1,
          scale = 0.5
        }
      },
      {
        filename = "__bzzirconium__/graphics/entity/zircon-particle/zircon-particle-shadow-2.png",
        priority = "extra-high",
        width = 16,
        height = 16,
        frame_count = 1,
        hr_version =
        {
          filename = "__bzzirconium__/graphics/entity/zircon-particle/hr-zircon-particle-shadow-2.png",
          priority = "extra-high",
          width = 32,
          height = 32,
          frame_count = 1,
          scale = 0.5
        }
      },
      {
        filename = "__bzzirconium__/graphics/entity/zircon-particle/zircon-particle-shadow-3.png",
        priority = "extra-high",
        width = 16,
        height = 16,
        frame_count = 1,
        hr_version =
        {
          filename = "__bzzirconium__/graphics/entity/zircon-particle/hr-zircon-particle-shadow-3.png",
          priority = "extra-high",
          width = 32,
          height = 32,
          frame_count = 1,
          scale = 0.5
        }
      },
      {
        filename = "__bzzirconium__/graphics/entity/zircon-particle/zircon-particle-shadow-4.png",
        priority = "extra-high",
        width = 16,
        height = 16,
        frame_count = 1,
        hr_version =
        {
          filename = "__bzzirconium__/graphics/entity/zircon-particle/hr-zircon-particle-shadow-4.png",
          priority = "extra-high",
          width = 32,
          height = 32,
          frame_count = 1,
          scale = 0.5
        }
      }
    }
  }
}
)

domain: lua


text:
-- Copyright (c) 2010-2011 by Robert G. Jakabosky <bobby@neoawareness.com>
--
-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE.

local _G = _G
local io = io
local fopen = io.open
local assert = assert
local sformat = string.format
local print = print
local ploaders = package.loaders
local m_require = require

local dir_sep = package.config:sub(1,1)
local path_sep = package.config:sub(3,3)
local path_mark = package.config:sub(5,5)
local path_match = "([^" .. path_sep .. "]*)" .. path_sep

local default_proto_path = ''
-- Use modified 'package.path' as search path for .poto files.
if package and package.path then
	-- convert '.lua' -> '.proto'
	for path in package.path:gmatch(path_match) do
		-- don't include "*/init.lua" paths
		if not path:match("init.lua$") then
			path = path:gsub('%.lua$','.proto')
			default_proto_path = default_proto_path .. path .. ';'
		end
	end
else
	default_proto_path = '.' .. dir_sep .. path_mark .. '.proto;'
end

local mod_name = ...

-- backend cache.
local backends = {}
local default_backend = 'standard'

local function new_backend(name, compile, encode, decode)
	local backend = {compile = compile, encode = encode, decode = decode, cache = {}}
	backends[name] = backend
	return backend
end

local function get_backend(name)
	name = name or default_backend
	local backend = backends[name]
	if not backend then
		-- load new backend
		local mod = require(mod_name .. '.' .. name)
		backend = new_backend(name, mod.compile, mod.encode, mod.decode)
	end
	return backend
end

-- pre-load default backend
get_backend(default_backend)

local function find_proto(name, search_path)
	local err_list = ''
	-- convert dotted name to directory path.
	name = name:gsub('%.', dir_sep)
	-- try each path in search path.
	for path in search_path:gmatch(path_match) do
		local fname = path:gsub(path_mark, name)
		local file, err = fopen(fname)
		-- return opened file
		if file then return file end
		-- append error and continue
		err_list = err_list .. sformat("\n\tno file %q", fname)
	end
	return nil, err_list
end

local function proto_file_to_name(file)
	local name = file:gsub("%.proto$", '')
	return name:gsub('/', '.')
end

module(...)

-- .proto search path.
_M.path = default_proto_path

_M.new_backend = new_backend

function set_default_backend(name)
	local old = default_backend
	-- test backend
	assert(get_backend(name) ~= nil)
	default_backend = name
	return old
end

local loading = "loading...."
function load_proto_ast(ast, name, backend, require)
	local b = get_backend(backend)

	-- Use sentinel mark in cache. (to detect import loops).
	if name then
		b.cache[name] = loading
	end

	-- process imports
	local imports = ast.imports
	if imports then
		require = require or _M.require
		for i=1,#imports do
			local import = imports[i]
			local name = proto_file_to_name(import.file)
			import.name = name
			-- recurively load imports.
			import.proto = require(name, backend)
		end
	end

	-- compile AST tree into Message definitions
	local proto = b.compile(ast)

	-- cache compiled .proto
	if name then
		b.cache[name] = proto
	end

	return proto
end

local proto_parser
function load_proto(text, name, backend, require)
	-- dynamically load proto parser.
	if not proto_parser then
		proto_parser = m_require(mod_name .. ".proto.parser")
	end
	-- parse .proto into AST tree
	local ast = proto_parser.parse(text)

	return load_proto_ast(ast, name, backend, require)
end

function require(name, backend)
	local b = get_backend(backend)
	-- check cache for compiled .proto
	local proto = b.cache[name]
	assert(proto ~= loading, "Import loop!")
	-- return compiled .proto, if cached
	if proto then return proto end

	-- load .proto file.
	local f=assert(find_proto(name, _M.path))
	local text = f:read("*a")
	f:close()

	-- compile AST tree into Message definitions
	return load_proto(text, name, backend, require)
end

local function install_proto(mod_name, proto)
	local parent = _G
	local mod_path = mod_name:match("^(.*)%.[^.]*$")
	if mod_path then
		-- build module path nodes
		for part in mod_path:gmatch("([^.]+)") do
			local node = parent[part]
			if not node then
				-- create missing node.
				node = {}
				parent[part] = node
			end
			parent = node
		end
		-- remove package prefix from module name
		mod_name = mod_name:sub(#mod_path+2)
	end
	parent[mod_name] = proto
	return proto
end

-- install pb.require as a package loader
local function pb_loader(mod_name, ...)
	local proto = require(mod_name, ...)
	-- simulate module loading.
	return function()
		return install_proto(proto['.package'] or mod_name, proto)
	end
end
ploaders[#ploaders + 1] = pb_loader

-- Raw Message for Raw decoding.
local raw

function decode_raw(...)
	if not raw then
		-- need to load Raw message definition.
		local proto = load_proto("message Raw {}")
		raw = proto.Raw
	end
	-- Raw message decoding
	local msg = raw()
	return msg:Parse(...)
end

function _M.print(msg)
	io.write(msg:SerializePartial('text'))
end


text:
dofile ("../../premake/amd_premake_util.lua")

workspace "AMD_SDK"
   configurations { "Debug", "Release" }
   platforms { "x64" }
   location "../build"
   filename ("AMD_SDK" .. _AMD_VS_SUFFIX)
   startproject "AMD_SDK"

   filter "platforms:x64"
      system "Windows"
      architecture "x64"

project "AMD_SDK"
   kind "StaticLib"
   language "C++"
   location "../build"
   filename ("AMD_SDK" .. _AMD_VS_SUFFIX)
   uuid "EBB939DC-98E4-49DF-B1F1-D2E80A11F60A"
   targetdir "../lib"
   objdir "../build/%{_AMD_SAMPLE_DIR_LAYOUT}"
   warnings "Extra"
   floatingpoint "Fast"

   -- Specify WindowsTargetPlatformVersion here for VS2015
   windowstarget (_AMD_WIN_SDK_VERSION)

   files { "../inc/**.h", "../src/**.h", "../src/**.cpp", "../src/**.hlsl" }

   filter "configurations:Debug"
      defines { "WIN32", "_DEBUG", "DEBUG", "PROFILE", "_WINDOWS", "_LIB", "_WIN32_WINNT=0x0601" }
      flags { "Symbols", "FatalWarnings", "Unicode" }
      targetsuffix ("_Debug" .. _AMD_VS_SUFFIX)

   filter "configurations:Release"
      defines { "WIN32", "NDEBUG", "_WINDOWS", "_LIB", "_WIN32_WINNT=0x0601" }
      flags { "LinkTimeOptimization", "Symbols", "FatalWarnings", "Unicode" }
      targetsuffix ("_Release" .. _AMD_VS_SUFFIX)
      optimize "On"


text:
local lzss = [===[--[[----------------------------------------------------------------------------
https://raw.githubusercontent.com/kieselsteini/lzss/master/lzss.lua
LZSS - encoder / decoder

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to <http://unlicense.org/>

--]]----------------------------------------------------------------------------
--------------------------------------------------------------------------------
local lzss = {}
local string, table = string, table

--------------------------------------------------------------------------------
local POS_BITS = 12
local LEN_BITS = 16 - POS_BITS
local POS_SIZE = 1 << POS_BITS
local LEN_SIZE = 1 << LEN_BITS
local LEN_MIN = 3

--------------------------------------------------------------------------------
function lzss.compress(input)
local offset, output = 1, {}
local window = ''

local function search()
    for i = LEN_SIZE + LEN_MIN - 1, LEN_MIN, -1 do
        local str = string.sub(input, offset, offset + i - 1)
        local pos = string.find(window, str, 1, true)
        if pos then
            return pos, str
        end
    end
end

while offset <= #input do
    local flags, buffer = 0, {}

    for i = 0, 7 do
        if offset <= #input then
            local pos, str = search()
            if pos and #str >= LEN_MIN then
                local tmp = ((pos - 1) << LEN_BITS) | (#str - LEN_MIN)
                buffer[#buffer + 1] = string.pack('>I2', tmp)
            else
                flags = flags | (1 << i)
                str = string.sub(input, offset, offset)
                buffer[#buffer + 1] = str
            end
            window = string.sub(window .. str, -POS_SIZE)
            offset = offset + #str
        else
            break
        end
    end

    if #buffer > 0 then
        output[#output + 1] = string.char(flags)
        output[#output + 1] = table.concat(buffer)
    end
end

return table.concat(output)
end

--------------------------------------------------------------------------------
function lzss.decompress(input)
local offset, output = 1, {}
local window = ''

while offset <= #input do
    local flags = string.byte(input, offset)
    offset = offset + 1

    for i = 1, 8 do
        local str = nil
        if (flags & 1) ~= 0 then
            if offset <= #input then
                str = string.sub(input, offset, offset)
                offset = offset + 1
            end
        else
            if offset + 1 <= #input then
                local tmp = string.unpack('>I2', input, offset)
                offset = offset + 2
                local pos = (tmp >> LEN_BITS) + 1
                local len = (tmp & (LEN_SIZE - 1)) + LEN_MIN
                str = string.sub(window, pos, pos + len - 1)
            end
        end
        flags = flags >> 1
        if str then
            output[#output + 1] = str
            window = string.sub(window .. str, -POS_SIZE)
        end
    end
end

return table.concat(output)
end

function lzss.getSXF(input, lua53) -- lua53 - force Lua 5.3 (OpenComputers ONLY)
input = input and input:gsub(".", {["\r"] = "\\r", ["\n"] = "\\n", ["\\"] = "\\\\", ["'"] = "\\'"})

local SXF = ([[local i,b,o,d,e,f,g,h,l='%s',1,'',''while b<=#i do e=o.byte(i,b)b=b+1;for j=0,7 do h=o.sub;l=h(i,b,b)if e>>j&1<1 and b<#i then g=o.unpack('>I2',i,b)f=1+(g>>4);l=h(d,f,f+(g&15)+2)b=b+1 end;b=b+1;o=o..l;d=h(d..l,-4^6)end end load(o,'=bios')()]])
    :format(
        input or ""
    )

if lua53 then
    SXF = ('computer.setArchitecture"Lua 5.3"load([=[%s]=],"=bios")()'):format(SXF)
end

return SXF
end

return lzss

]===]

local lzss = load(lzss, "=lzss.lua")
lzss = lzss and lzss() or error("This program requires Lua 5.3 or higher")

local file, data = io.open("minified.lua", "r")
data = file:read("*a")
file:close()

file = io.open("cyan.bin", "w")
file:write(lzss.getSXF(lzss.compress(data), true))
file:close()


text:
--
-- android/vsandroid_vcxproj.lua
-- vs-android integration for vstudio.
-- Copyright (c) 2012-2015 Manu Evans and the Premake project
--

	local p = premake

	p.modules.vsandroid = { }

	local android = p.modules.android
	local vsandroid = p.modules.vsandroid
	local vc2010 = p.vstudio.vc2010
	local vstudio = p.vstudio
	local project = p.project
	local config = p.config

--
-- Add android tools to vstudio actions.
--

	if vstudio.vs2010_architectures ~= nil then
		if _ACTION >= "vs2015" then
			vstudio.vs2010_architectures.arm = "ARM"
		else
			vstudio.vs2010_architectures.android = "Android"
		end
	end

--
-- Extend global properties
--
	premake.override(vc2010.elements, "globals", function (oldfn, prj)
		local elements = oldfn(prj)

		if prj.system == premake.ANDROID and prj.kind ~= premake.ANDROIDPROJ then
			-- Remove "IgnoreWarnCompileDuplicatedFilename".
			local pos = table.indexof(elements, vc2010.ignoreWarnDuplicateFilename)
			table.remove(elements, pos)
			elements = table.join(elements, {
				android.androidApplicationType
			})
		end

		return elements
	end)

	function android.androidApplicationType(cfg)
		_p(2, "<Keyword>Android</Keyword>")
		_p(2, "<RootNamespace>%s</RootNamespace>", cfg.project.name)
		_p(2, "<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>")
		_p(2, "<ApplicationType>Android</ApplicationType>")
		if _ACTION >= "vs2017" then
			_p(2, "<ApplicationTypeRevision>3.0</ApplicationTypeRevision>")
		elseif _ACTION >= "vs2015" then
			_p(2, "<ApplicationTypeRevision>2.0</ApplicationTypeRevision>")
		else
			_p(2, "<ApplicationTypeRevision>1.0</ApplicationTypeRevision>")
		end
	end

--
-- Extend configurationProperties.
--

	premake.override(vc2010.elements, "configurationProperties", function(oldfn, cfg)
		local elements = oldfn(cfg)
		if cfg.kind ~= p.UTILITY and cfg.kind ~= p.ANDROIDPROJ and cfg.system == premake.ANDROID then
			elements = table.join(elements, {
				android.androidAPILevel,
				android.androidStlType,
			})

			if _ACTION >= "vs2015" then
				elements = table.join(elements, {
					android.thumbMode,
				})
			end
		end
		return elements
	end)

	function android.androidAPILevel(cfg)
		if cfg.androidapilevel ~= nil then
			_p(2,'<AndroidAPILevel>android-%d</AndroidAPILevel>', cfg.androidapilevel)
		end
	end

	function android.androidStlType(cfg)
		if cfg.stl ~= nil then
			local stlType = {
				["none"] = "none",
				["minimal c++ (system)"] = "system",
				["c++ static"] = "gabi++_static",
				["c++ shared"] = "gabi++_shared",
				["stlport static"] = "stlport_static",
				["stlport shared"] = "stlport_shared",
				["gnu stl static"] = "gnustl_static",
				["gnu stl shared"] = "gnustl_shared",
				["llvm libc++ static"] = "c++_static",
				["llvm libc++ shared"] = "c++_shared",
			}

			if _ACTION >= "vs2015" then
				_p(2,'<UseOfStl>%s</UseOfStl>', stlType[cfg.stl])
			else
				_p(2,'<AndroidStlType>%s</AndroidStlType>', stlType[cfg.stl])
			end
		end
	end

	function android.thumbMode(cfg)
		if cfg.thumbmode then
			local thumbMode =
			{
				thumb = "Thumb",
				arm = "ARM",
				disabled = "Disabled",
			}
			_p(2,"<ThumbMode>%s</ThumbMode>", thumbMode[cfg.thumbmode])
		end
	end

	-- Note: this function is already patched in by vs2012...
	premake.override(vc2010, "platformToolset", function(oldfn, cfg)
		if cfg.system ~= premake.ANDROID then
			return oldfn(cfg)
		end

		if _ACTION >= "vs2015" then
			local gcc_map = {
				["_"]   = "GCC_4_9", -- default
				["4.6"] = "GCC_4_6",
				["4.8"] = "GCC_4_8",
				["4.9"] = "GCC_4_9",
			}
			local clang_map = {
				["_"]   = "Clang_3_8", -- default
				["3.4"] = "Clang_3_4",
				["3.5"] = "Clang_3_5",
				["3.6"] = "Clang_3_6",
				["3.8"] = "Clang_3_8",
			}

			local map = iif(cfg.toolset == "gcc", gcc_map, clang_map)
			local ts  = map[cfg.toolchainversion or "_"]
			if ts == nil then
				p.error('Invalid toolchainversion for the selected toolset (%s).', cfg.toolset or "clang")
			end

			_p(2, "<PlatformToolset>%s</PlatformToolset>", ts)
		else
			local archMap = {
				arm = "armv5te", -- should arm5 be default? vs-android thinks so...
				arm5 = "armv5te",
				arm7 = "armv7-a",
				mips = "mips",
				x86 = "x86",
			}
			local arch = cfg.architecture or "arm"

			if (cfg.architecture ~= nil or cfg.toolchainversion ~= nil) and archMap[arch] ~= nil then
				local defaultToolsetMap = {
					arm = "arm-linux-androideabi-",
					armv5 = "arm-linux-androideabi-",
					armv7 = "arm-linux-androideabi-",
					aarch64 = "aarch64-linux-android-",
					mips = "mipsel-linux-android-",
					mips64 = "mips64el-linux-android-",
					x86 = "x86-",
					x86_64 = "x86_64-",
				}
				local toolset = defaultToolsetMap[arch]

				if cfg.toolset == "clang" then
					error("The clang toolset is not yet supported by vs-android", 2)
					toolset = toolset .. "clang"
				elseif cfg.toolset and cfg.toolset ~= "gcc" then
					error("Toolset not supported by the android NDK: " .. cfg.toolset, 2)
				end

				local version = cfg.toolchainversion or iif(cfg.toolset == "clang", "3.5", "4.9")

				_p(2,'<PlatformToolset>%s</PlatformToolset>', toolset .. version)
				_p(2,'<AndroidArch>%s</AndroidArch>', archMap[arch])
			end
		end
	end)

--
-- Extend clCompile.
--

	premake.override(vc2010.elements, "clCompile", function(oldfn, cfg)
		local elements = oldfn(cfg)
		if cfg.system == premake.ANDROID then
			elements = table.join(elements, {
				android.debugInformation,
				android.strictAliasing,
				android.thumbMode,
				android.fpu,
				android.pic,
--				android.ShortEnums,
			})
			if _ACTION >= "vs2015" then
				table.remove(elements, table.indexof(elements, vc2010.debugInformationFormat))
				table.remove(elements, table.indexof(elements, android.thumbMode))
			end
		end
		return elements
	end)

	function android.debugInformation(cfg)
		if cfg.flags.Symbols then
			_p(3,'<GenerateDebugInformation>true</GenerateDebugInformation>')
		end
	end

	function android.strictAliasing(cfg)
		if cfg.strictaliasing ~= nil then
			_p(3,'<StrictAliasing>%s</StrictAliasing>', iif(cfg.strictaliasing == "Off", "false", "true"))
		end
	end

	function android.thumbMode(cfg)
		if cfg.flags.Thumb then
			_p(3,'<ThumbMode>true</ThumbMode>')
		end
	end

	function android.fpu(cfg)
		if cfg.fpu ~= nil then
			_p(3,'<SoftFloat>true</SoftFloat>', iif(cfg.fpu == "Software", "true", "false"))
		end
	end

	function android.pic(cfg)
		-- TODO: We only have a flag to turn it on, but android is on by default
		--       it seems we would rather have a flag to turn it off...
--		if cfg.pic ~= nil then
--			_p(3,'<PositionIndependentCode>%s</PositionIndependentCode>', iif(cfg.pic == "On", "true", "false"))
--		end
	end

	p.override(vc2010, "languageStandard", function(oldfn, cfg)
		if cfg.system == p.ANDROID then
			local cpp_langmap = {
				["C++98"]   = "c++98",
				["C++11"]   = "c++11",
				["C++14"]   = "c++1y",
				["gnu++98"] = "gnu++98",
				["gnu++11"] = "gnu++11",
				["gnu++14"] = "gnu++1y",
			}
			if cpp_langmap[cfg.cppdialect] ~= nil then
				vc2010.element("CppLanguageStandard", nil, cpp_langmap[cfg.cppdialect])
			end
		else
			oldfn(cfg)
		end
	end)

	p.override(vc2010, "additionalCompileOptions", function(oldfn, cfg, condition)
		if cfg.system == p.ANDROID then
			local opts = cfg.buildoptions

			local cpp_langmap = {
				["C++17"]   = "-std=c++1z",
				["gnu++17"] = "-std=gnu++1z",
			}
			if cpp_langmap[cfg.cppdialect] ~= nil then
				table.insert(opts, cpp_langmap[cfg.cppdialect])
			end

			if #opts > 0 then
				opts = table.concat(opts, " ")
				vc2010.element("AdditionalOptions", condition, '%s %%(AdditionalOptions)', opts)
			end
		else
			oldfn(cfg, condition)
		end
	end)

	p.override(p.vstudio.vc2010, "warningLevel", function(oldfn, cfg)
		if _ACTION >= "vs2015" and cfg.system == p.ANDROID and cfg.warnings and cfg.warnings ~= "Off" then
			p.vstudio.vc2010.element("WarningLevel", nil, "EnableAllWarnings")
		elseif (_ACTION >= "vs2015" and cfg.system == p.ANDROID and cfg.warnings) or not (_ACTION >= "vs2015" and cfg.system == p.ANDROID) then
			oldfn(cfg)
		end
	end)

	premake.override(vc2010, "clCompilePreprocessorDefinitions", function(oldfn, cfg, condition)
		if cfg.system == p.ANDROID then
			vc2010.preprocessorDefinitions(cfg, cfg.defines, false, condition)
		else
			oldfn(cfg, condition)
		end
	end)

	premake.override(vc2010, "exceptionHandling", function(oldfn, cfg, condition)
		if cfg.system == p.ANDROID then
			-- Note: Android defaults to 'off'
			if cfg.exceptionhandling then
				if _ACTION >= "vs2015" then
					vc2010.element("ExceptionHandling", condition, "Enabled")
				else
					vc2010.element("GccExceptionHandling", condition, "true")
				end
			end
		else
			oldfn(cfg, condition)
		end
	end)

	premake.override(vc2010, "runtimeTypeInfo", function(oldfn, cfg, condition)
		if cfg.system == premake.ANDROID then
			-- Note: Android defaults to 'off'
			if cfg.rtti then
				vc2010.element("RuntimeTypeInfo", condition, "true")
			end
		else
			oldfn(cfg, condition)
		end
	end)

--
-- Extend Link.
--

	premake.override(vc2010, "generateDebugInformation", function(oldfn, cfg)
		-- Note: Android specifies the debug info in the clCompile section
		if cfg.system ~= premake.ANDROID then
			oldfn(cfg)
		end
	end)

--
-- Add android tools to vstudio actions.
--

	premake.override(vc2010.elements, "itemDefinitionGroup", function(oldfn, cfg)
		local elements = oldfn(cfg)
		if cfg.system == premake.ANDROID and _ACTION < "vs2015" then
			elements = table.join(elements, {
				android.antBuild,
			})
		end
		return elements
	end)

	function android.antPackage(cfg)
		p.push('<AntPackage>')
		if cfg.androidapplibname ~= nil then
			vc2010.element("AndroidAppLibName", nil, cfg.androidapplibname)
		else
			vc2010.element("AndroidAppLibName", nil, "$(RootNamespace)")
		end
		p.pop('</AntPackage>')
	end

	function android.antBuild(cfg)
		if cfg.kind == premake.STATICLIB or cfg.kind == premake.SHAREDLIB then
			return
		end

		_p(2,'<AntBuild>')
		_p(3,'<AntBuildType>%s</AntBuildType>', iif(premake.config.isDebugBuild(cfg), "Debug", "Release"))
		_p(2,'</AntBuild>')
	end

	premake.override(vc2010, "additionalCompileOptions", function(oldfn, cfg, condition)
		if cfg.system == premake.ANDROID then
			vsandroid.additionalOptions(cfg, condition)
		end
		return oldfn(cfg, condition)
	end)

	premake.override(vc2010.elements, "user", function(oldfn, cfg)
		if cfg.system == p.ANDROID then
			return {}
		else
			return oldfn(cfg)
		end
	end)

--
-- Add options unsupported by vs-android UI to <AdvancedOptions>.
--
	function vsandroid.additionalOptions(cfg)
		if _ACTION >= "vs2015" then

		else
			local function alreadyHas(t, key)
				for _, k in ipairs(t) do
					if string.find(k, key) then
						return true
					end
				end
				return false
			end

			if not cfg.architecture or string.startswith(cfg.architecture, "arm") then
				-- we might want to define the arch to generate better code
--				if not alreadyHas(cfg.buildoptions, "-march=") then
--					if cfg.architecture == "armv6" then
--						table.insert(cfg.buildoptions, "-march=armv6")
--					elseif cfg.architecture == "armv7" then
--						table.insert(cfg.buildoptions, "-march=armv7")
--					end
--				end

				-- ARM has a comprehensive set of floating point options
				if cfg.fpu ~= "Software" and cfg.floatabi ~= "soft" then

					if cfg.architecture == "armv7" then

						-- armv7 always has VFP, may not have NEON

						if not alreadyHas(cfg.buildoptions, "-mfpu=") then
							if cfg.vectorextensions == "NEON" then
								table.insert(cfg.buildoptions, "-mfpu=neon")
							elseif cfg.fpu == "Hardware" or cfg.floatabi == "softfp" or cfg.floatabi == "hard" then
								table.insert(cfg.buildoptions, "-mfpu=vfpv3-d16") -- d16 is the lowest common denominator
							end
						end

						if not alreadyHas(cfg.buildoptions, "-mfloat-abi=") then
							if cfg.floatabi == "hard" then
								table.insert(cfg.buildoptions, "-mfloat-abi=hard")
							else
								-- Android should probably use softfp by default for compatibility
								table.insert(cfg.buildoptions, "-mfloat-abi=softfp")
							end
						end

					else

						-- armv5/6 may not have VFP

						if not alreadyHas(cfg.buildoptions, "-mfpu=") then
							if cfg.fpu == "Hardware" or cfg.floatabi == "softfp" or cfg.floatabi == "hard" then
								table.insert(cfg.buildoptions, "-mfpu=vfp")
							end
						end

						if not alreadyHas(cfg.buildoptions, "-mfloat-abi=") then
							if cfg.floatabi == "softfp" then
								table.insert(cfg.buildoptions, "-mfloat-abi=softfp")
							elseif cfg.floatabi == "hard" then
								table.insert(cfg.buildoptions, "-mfloat-abi=hard")
							end
						end

					end

				elseif cfg.floatabi == "soft" then

					table.insert(cfg.buildoptions, "-mfloat-abi=soft")

				end

				if cfg.endian == "Little" then
					table.insert(cfg.buildoptions, "-mlittle-endian")
				elseif cfg.endian == "Big" then
					table.insert(cfg.buildoptions, "-mbig-endian")
				end

			elseif cfg.architecture == "mips" then

				-- TODO...

				if cfg.vectorextensions == "MXU" then
					table.insert(cfg.buildoptions, "-mmxu")
				end

			elseif cfg.architecture == "x86" then

				-- TODO...

			end
		end
	end

--
-- Disable subsystem.
--

	p.override(vc2010, "subSystem", function(oldfn, cfg)
		if cfg.system ~= p.ANDROID then
			return oldfn(cfg)
		end
	end)

--
-- Remove .lib and list in LibraryDependencies instead of AdditionalDependencies.
--

	p.override(vc2010, "additionalDependencies", function(oldfn, cfg, explicit)
		if cfg.system == p.ANDROID then
			local links = {}

			-- If we need sibling projects to be listed explicitly, grab them first
			if explicit then
				links = config.getlinks(cfg, "siblings", "fullpath")
			end

			-- Then the system libraries, which come undecorated
			local system = config.getlinks(cfg, "system", "name")
			for i = 1, #system do
				local link = system[i]
				table.insert(links, link)
			end

			-- TODO: When to use LibraryDependencies vs AdditionalDependencies

			if #links > 0 then
				links = path.translate(table.concat(links, ";"))
				vc2010.element("LibraryDependencies", nil, "%%(LibraryDependencies);%s", links)
			end
		else
			return oldfn(cfg, explicit)
		end
	end)

--
-- Disable override of OutDir.  This is breaking deployment.
--
	p.override(vc2010, "outDir", function(oldfn, cfg)
		if cfg.system ~= p.ANDROID then
			return oldfn(cfg)
		end
	end)


text:
--[[ Copyright (c) 2009 Peter "Corsix" Cawley

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. --]]

local TH = require("TH")

--! An `Entity` which occupies at least a single map tile and does not move.
class "Object" (Entity)

---@type Object
local Object = _G["Object"]

local orient_mirror = {
  north = "west",
  west = "north",
  east = "south",
  south = "east",
}

function Object:getDrawingLayer()
  return 4
end

function Object:Object(world, object_type, x, y, direction, etc)
  local th = TH.animation()
  self:Entity(th)

  if etc == "map object" then
    if direction % 2 == 0 then
      direction = "north"
    else
      direction = "west"
    end
  end

  self.ticks = object_type.ticks
  self.object_type = object_type
  self.world = world
  self.hospital = world:getLocalPlayerHospital()
  self.user = false
  self.times_used = -1 -- Incremented in the call on the next line
  self:updateDynamicInfo()
  self:initOrientation(direction)
  self:setTile(x, y)
end

--! Initializes the footprint, finds out what to draw and checks for
--  split animations.
--!param direction The orientation in which the object is facing.
function Object:initOrientation(direction)
  self.direction = direction
  local object_type = self.object_type
  local flags = self.init_anim_flags or 0
  local anim = object_type.idle_animations[direction]
  if not anim then
    anim = object_type.idle_animations[orient_mirror[direction]]
    flags = 1
  end
  local footprint = object_type.orientations
  footprint = footprint and footprint[direction]
  if footprint and footprint.early_list then
    flags = flags + 1024
  end
  if footprint and footprint.list_bottom then
    flags = flags + 2048
  end
  local rap = footprint and footprint.render_attach_position
  if rap and rap[1] and type(rap[1]) == "table" then
    self.split_anims = {self.th}
    self.split_anim_positions = rap
    self.th:setCrop(rap[1].column)
    for i = 2, #rap do
      local point = rap[i]
      local th = TH.animation()
      th:setCrop(point.column)
      th:setHitTestResult(self)
      th:setPosition(Map:WorldToScreen(1-point[1], 1-point[2]))
      self.split_anims[i] = th
    end
  else
    -- Make sure these variables aren't left behind. The object
    -- might just have been moved and rotated.
    self.split_anims = nil
    self.split_anim_positions = nil
  end
  if footprint and footprint.animation_offset then
    self:setPosition(unpack(footprint.animation_offset))
  end
  footprint = footprint and footprint.footprint
  if footprint then
    self.footprint = footprint
  else
    self.footprint = nil
  end
  self:setAnimation(anim, flags)
end

--! Add methods to a class for creating and controlling a slave object
function Object.slaveMixinClass(class_method_table)
  local name = class.name(class_method_table)
  local super = class.superclass(class_method_table)
  local super_constructor = super[class.name(super)]

  -- Constructor
  class_method_table[name] = function(self, world, object_type, x, y, direction, ...)
    super_constructor(self, world, object_type, x, y, direction, ...)
    if object_type.slave_id then
      local orientation = object_type.orientations
      orientation = orientation and orientation[direction]
      if orientation.slave_position then
        x = x + orientation.slave_position[1]
        y = y + orientation.slave_position[2]
      end
      self.slave = world:newObject(object_type.slave_id, x, y, direction, ...)
      self.slave.master = self
    end
  end

  -- Slave -> Master redirects
  local function slave_to_master(method)
    local super_method = super[method]
    class_method_table[method] = function(self, ...)
      local master = self.master
      if master then
        return master[method](master, ...)
      else
        return super_method(self, ...)
      end
    end
  end
  slave_to_master("onClick")
  slave_to_master("updateDynamicInfo")
  slave_to_master("getDynamicInfo")

  -- Master -> Slave notifications
  local function master_to_slave(method)
    local super_method = super[method]
    class_method_table[method] = function(self, ...)
      local slave = self.slave
      if slave then
        slave[method](slave, ...)
      end
      return super_method(self, ...)
    end
  end
  master_to_slave("initOrientation")
  class_method_table.onDestroy = function(self, ...)
    local slave = self.slave
    if slave then
      self.world:destroyEntity(slave)
    end
    return super.onDestroy(self, ...)
  end
  class_method_table.setTile = function(self, x, y)
    if self.slave then
      local dx, dy = 0, 0
      local orientation = self.object_type.orientations
      orientation = orientation and orientation[self.direction]
      if orientation.slave_position then
        dx = orientation.slave_position[1]
        dy = orientation.slave_position[2]
      end
      if x then
        self.slave:setTile(x + dx, y + dy)
      else
        self.slave:setTile(x, y)
      end
    end
    return super.setTile(self, x, y)
  end

  return slave_to_master, master_to_slave
end

function Object:tick()
  if self.split_anims then
    if self.num_animation_ticks then
      for i = 2, #self.split_anims do
        local th = self.split_anims[i]
        for _ = 1, self.num_animation_ticks do
          th:tick()
        end
      end
    else
      for i = 2, #self.split_anims do
        self.split_anims[i]:tick()
      end
    end
  end
  return Entity.tick(self)
end

function Object:setPosition(x, y)
  if self.split_anims then
    -- The given position is for the primary tile, so position non-primary
    -- animations relative to the primary one.
    local bx, by = unpack(self.split_anim_positions[1])
    for i, th in ipairs(self.split_anims) do
      local point = self.split_anim_positions[i]
      local dx, dy = Map:WorldToScreen(1-point[1]+bx, 1-point[2]+by)
      th:setPosition(x + dx, y + dy)
    end
  else
    self.th:setPosition(x, y)
  end
  return self
end

function Object:setAnimation(animation, flags)
  if self.split_anims then
    flags = (flags or 0) + DrawFlags.Crop
    if self.permanent_flags then
      flags = flags + self.permanent_flags
    end
    if animation ~= self.animation_idx or flags ~= self.animation_flags then
      self.animation_idx = animation
      self.animation_flags = flags
      local anims = self.world.anims
      for _, th in ipairs(self.split_anims) do
        th:setAnimation(anims, animation, flags)
      end
    end
    return self
  else
    return Entity.setAnimation(self, animation, flags)
  end
end

--! Get the primary tile which the object is attached to for rendering
--[[! For objects which attach to a single tile for rendering, this method will
return the X and Y Lua world co-ordinates of that tile. For objects which split
their rendering over multiple tiles, one of them is arbitrarily designated as
the primary tile, and its co-ordinates are returned.
]]
function Object:getRenderAttachTile()
  local x, y = self.tile_x, self.tile_y
  local offset = self.object_type.orientations
  if x and offset then
    offset = offset[self.direction].render_attach_position
    if self.split_anims then
      offset = offset[1]
    end
    x = x + offset[1]
    y = y + offset[2]
  end
  return x, y
end

function Object:updateDynamicInfo()
  self.times_used = self.times_used + 1
  local object = self.object_type
  if object.dynamic_info then
    self:setDynamicInfo("text", {object.name, "", _S.dynamic_info.object.times_used:format(self.times_used)})
  end
end

function Object:getSecondaryUsageTile()
  local x, y = self.tile_x, self.tile_y
  local offset = self.object_type.orientations
  if offset then
    offset = offset[self.direction].use_position_secondary
    x = x + offset[1]
    y = y + offset[2]
  end
  return x, y
end

-- This function returns a list of all "only_passable" tiles belonging to an object.
-- It must be overridden by objects which do not have a footprint, but walkable tiles (e.g. doors of any kind)
function Object:getWalkableTiles()
  local tiles = {}
  for _, xy in ipairs(self.footprint) do
    if xy.only_passable then
      tiles[#tiles + 1] = { self.tile_x + xy[1], self.tile_y + xy[2] }
    end
  end
  return tiles
end

function Object:setTile(x, y)
  local function coordinatesAreInFootprint(object_footprint, xpos, ypos)
    for _, xy in ipairs(object_footprint) do
      if(xy[1] == xpos and xy[2] == ypos) then
        return true
      end
    end
    return false
  end

  local function isEmpty(table)
    for _, _ in pairs(table) do
      return false
    end
    return true
  end

  local function getComplementaryPassableFlag(passable_flag)
    if passable_flag == "travelNorth" or passable_flag == "travelSouth" then
      return passable_flag == "travelNorth" and "travelSouth" or "travelNorth"
    else
      return passable_flag == "travelEast" and "travelWest" or "travelEast"
    end
  end

  local function setPassableFlags(passable_flag, xpos, ypos, next_x, next_y, value)
    local flags1 = {}
    flags1[passable_flag] = value
    self.world.map.th:setCellFlags(xpos, ypos, flags1)
    local flags2 = {}
    flags2[getComplementaryPassableFlag(passable_flag)] = value
    self.world.map.th:setCellFlags(next_x, next_y, flags2)
  end

  local direction_parameters = {
            north = { x = 0, y = -1, buildable_flag = "buildableNorth", passable_flag = "travelNorth", needed_side = "need_north_side"},
            east = { x = 1, y = 0, buildable_flag =  "buildableEast", passable_flag = "travelEast", needed_side = "need_east_side"},
            south = { x = 0, y = 1, buildable_flag = "buildableSouth", passable_flag = "travelSouth", needed_side = "need_south_side"},
            west = { x = -1, y = 0, buildable_flag = "buildableWest", passable_flag = "travelWest", needed_side = "need_west_side"}
            }

  local direction = self.direction
  if self.object_type.thob == 50 and direction == "east" then
    direction = "west"
  end

  if self.tile_x ~= nil then
    self.world:removeObjectFromTile(self, self.tile_x, self.tile_y)
    if self.footprint then
      local map = self.world.map.th
      for _, xy in ipairs(self.footprint) do
        local xpos, ypos = self.tile_x + xy[1], self.tile_y + xy[2]

        if xy.only_side then
          if self.set_passable_flags then
            self.set_passable_flags = nil
            local par = direction_parameters[direction]
            local passableFlag, next_tile_x, next_tile_y = par.passable_flag, xpos + par.x, ypos + par.y
            setPassableFlags(passableFlag, xpos, ypos, next_tile_x, next_tile_y, true)
          end
          local flags_to_set= {}
          flags_to_set[direction_parameters[direction].buildable_flag] = true
          map:setCellFlags(xpos, ypos, flags_to_set)
        else
          local flags_to_set = {}
          for _, value in pairs(direction_parameters) do
            if coordinatesAreInFootprint(self.footprint, xy[1] + value.x, xy[2] + value.y) or
                xy.complete_cell or xy[value.needed_side] then
              flags_to_set[value.buildable_flag] = true
            end
          end

          if not isEmpty(flags_to_set) then
            map:setCellFlags(xpos, ypos, flags_to_set)
          end
          if not map:getCellFlags(xpos, ypos).passable then
            map:setCellFlags(xpos, ypos, {
              buildable = true,
              passable = true,
            })
          else
            -- passable tiles can "belong" to multiple objects, so we have to check that
            if not self.world:isTilePartOfNearbyObject(xpos, ypos, 10) then
              -- assumption: no object defines a passable tile further than 10 tiles away from its origin
              map:setCellFlags(xpos, ypos, {
                buildable = true,
              })
            end
          end
        end
      end
    end
  end

  local entity_map = self.world.entity_map
  -- Remove the reference to the object at it's previous coordinates
  if entity_map then
    entity_map:removeEntity(self.tile_x,self.tile_y, self)
  end

  -- Update the objects coordinates
  self.tile_x = x
  self.tile_y = y

  -- Update the entity map for the new position
  if entity_map then
    entity_map:addEntity(x,y,self)
  end

  if x then
    self.th:setDrawingLayer(self:getDrawingLayer())
    self.th:setTile(self.world.map.th, self:getRenderAttachTile())
    self.world:addObjectToTile(self, x, y)
    if self.footprint then
      local map = self.world.map.th
      local optional_found = false
      local room = self.world:getRoom(x, y)
      local roomId = room and room.id
      local next_tile_x, next_tile_y = x,y
      local passable_flag

      for _, xy in ipairs(self.footprint) do
        local change_flags = true
        local flags_to_set = {}
        local lx = x + xy[1]
        local ly = y + xy[2]
        local flags

        if xy.optional then
          if optional_found then
            -- An optional tile has been accepted, we don't need anymore such tiles.
            change_flags = false
          else
            flags = map:getCellFlags(lx, ly)
            local is_object_allowed = true
            if roomId and flags.roomId ~= roomId then
              is_object_allowed = false
            elseif xy.only_passable and not self.world.pathfinder:isReachableFromHospital(lx, ly) then
              is_object_allowed = false
            end

            if is_object_allowed then
              change_flags = true
              optional_found = true
            else
              change_flags = false
            end
          end
        end

        flags = map:getCellFlags(lx, ly)
        if xy.only_side then
          local par = direction_parameters[direction]
          flags_to_set[par["buildable_flag"]] = false
          passable_flag, next_tile_x, next_tile_y = par["passable_flag"], x + par["x"], y + par["y"]
        else
          for _, value in pairs(direction_parameters) do
            if coordinatesAreInFootprint(self.footprint, xy[1] + value["x"], xy[2] + value["y"]) or
            xy.complete_cell or xy[value["needed_side"]] then
              if map:getCellFlags(x, y, flags)[value["buildable_flag"]] == 0 then
                change_flags = false
              end
              flags_to_set[value["buildable_flag"]] = false
            end
          end
        end

        if change_flags then
          if not xy.only_side then
            map:setCellFlags(lx, ly, {
              buildable = false,
              passable = not not xy.only_passable,
            })
          end
          if not isEmpty(flags_to_set) then
            map:setCellFlags(lx, ly, flags_to_set)
          end
          if xy.only_side then
            if map:getCellFlags(lx, ly)[passable_flag] == true then
              self.set_passable_flags = true
              setPassableFlags(passable_flag, lx, ly, next_tile_x, next_tile_y, false)
            end
          end
        end
      end
    end
    if self.split_anims then
      local map = self.world.map.th
      local pos = self.split_anim_positions
      for i = 2, #self.split_anims do
        self.split_anims[i]:setTile(map, x + pos[i][1], y + pos[i][2])
      end
    end
  else
    self.th:setTile(nil)
    if self.split_anims then
      for i = 2, #self.split_anims do
        self.split_anims[i]:setTile(nil)
      end
    end
  end
  self.world:clearCaches()
  return self
end

-- Sets the user of this object to the given user. Note that if multiple_users_allowed
-- is set to true for this object "user" will always be the last added user to this object.
-- Please do not modify object.user directly.
--!param user The user that is about to use this object.
function Object:setUser(user)
  assert(user, "setUser: Expected a user, got nil") -- It makes no sense to add a nil value
  self.user = user
  if self.object_type.multiple_users_allowed then
    if not self.user_list then
      self.user_list = {}
    end
    table.insert(self.user_list, user)
  end
  self.th:makeInvisible()
  self:removeReservedUser(user)
end

-- Removes a user from the set of users if multiple_users_allowed is set to true for this
-- object. Otherwise it sets "user" to nil. Please don't directly change the variable "user".
function Object:removeUser(user)
  if self.object_type.multiple_users_allowed then
    if not user or not self.user_list then
      -- No user specified, empty the list; or the list didn't exist
      self.user_list = {}
    end
    local found = false
    for i, users in ipairs(self.user_list) do
      if users == user then
        table.remove(self.user_list, i)
        found = true
        break
      end
    end
    if not found then
      self.world:gameLog("Warning: Could not find a humanoid to remove from the user list")
    end
    if #self.user_list == 0 then
      -- No users left, make the object visible again.
      self.user = nil
      self.th:makeVisible()
    end
  else
    self.user = nil
    self.th:makeVisible()
  end
end

-- Adds a reserved user for this object. If multiple_users_allowed is set there can be
-- many users reserving the object. reserved_for will then be the user who most recently
-- reserved this object.
function Object:addReservedUser(user)
  assert(user, "Expected a user, got nil") -- It makes no sense to add a nil value
  if self.object_type.multiple_users_allowed then
    if not self.reserved_for_list then
      self.reserved_for_list = {}
    end
    table.insert(self.reserved_for_list, user)
  else
    assert(not self.reserved_for or user ~= self.reserved_for, "Object already reserved for another user")
  end
  self.reserved_for = user
end

-- If multiple_users_allowed is true: Removes the user specified from this object's list of reserved users.
-- If the argument is nil it is assumed that the list should be emptied.
-- Note that if there are many humanoids reserving this object reserved_for might still be set after a
-- call to this function.
-- Otherwise: sets reserved_for to nil.
function Object:removeReservedUser(user)
  if self.object_type.multiple_users_allowed then
    -- No user specified, delete the whole list; or no list found, make it.
    if not user or not self.reserved_for_list then
      self.reserved_for_list = {}
    end
    local found = false
    for i, users in ipairs(self.reserved_for_list) do
      if users == user then
        table.remove(self.reserved_for_list, i)
        found = true
        break
      end
    end
    if not found then
      self.world:gameLog("Warning: Could not find a humanoid to remove from the reserved user list")
    end
    if #self.reserved_for_list == 0 then
      -- No users left, set reserved_for to nil.
      self.reserved_for = nil
    end
  else
    self.reserved_for = nil
  end
end

-- Checks whether the object is reserved for the specified user.
-- If the argument is nil a check for any reserved user is done.
function Object:isReservedFor(user)
  if not user then
    if self.reserved_for_list then
      return #self.reserved_for_list > 0
    else


text:
-- Generated By protoc-gen-lua Do not Edit
local protobuf = require "protobuf/protobuf"
module('TaskHelpInfo_pb')

TASKHELPINFO = protobuf.Descriptor();
local TASKHELPINFO_ROLEID_FIELD = protobuf.FieldDescriptor();
local TASKHELPINFO_ROLENAME_FIELD = protobuf.FieldDescriptor();
local TASKHELPINFO_ITEMID_FIELD = protobuf.FieldDescriptor();
local TASKHELPINFO_ITEMCOUNT_FIELD = protobuf.FieldDescriptor();
local TASKHELPINFO_TIME_FIELD = protobuf.FieldDescriptor();

TASKHELPINFO_ROLEID_FIELD.name = "roleid"
TASKHELPINFO_ROLEID_FIELD.full_name = ".KKSG.TaskHelpInfo.roleid"
TASKHELPINFO_ROLEID_FIELD.number = 1
TASKHELPINFO_ROLEID_FIELD.index = 0
TASKHELPINFO_ROLEID_FIELD.label = 1
TASKHELPINFO_ROLEID_FIELD.has_default_value = false
TASKHELPINFO_ROLEID_FIELD.default_value = 0
TASKHELPINFO_ROLEID_FIELD.type = 4
TASKHELPINFO_ROLEID_FIELD.cpp_type = 4

TASKHELPINFO_ROLENAME_FIELD.name = "rolename"
TASKHELPINFO_ROLENAME_FIELD.full_name = ".KKSG.TaskHelpInfo.rolename"
TASKHELPINFO_ROLENAME_FIELD.number = 2
TASKHELPINFO_ROLENAME_FIELD.index = 1
TASKHELPINFO_ROLENAME_FIELD.label = 1
TASKHELPINFO_ROLENAME_FIELD.has_default_value = false
TASKHELPINFO_ROLENAME_FIELD.default_value = ""
TASKHELPINFO_ROLENAME_FIELD.type = 9
TASKHELPINFO_ROLENAME_FIELD.cpp_type = 9

TASKHELPINFO_ITEMID_FIELD.name = "itemid"
TASKHELPINFO_ITEMID_FIELD.full_name = ".KKSG.TaskHelpInfo.itemid"
TASKHELPINFO_ITEMID_FIELD.number = 3
TASKHELPINFO_ITEMID_FIELD.index = 2
TASKHELPINFO_ITEMID_FIELD.label = 1
TASKHELPINFO_ITEMID_FIELD.has_default_value = false
TASKHELPINFO_ITEMID_FIELD.default_value = 0
TASKHELPINFO_ITEMID_FIELD.type = 13
TASKHELPINFO_ITEMID_FIELD.cpp_type = 3

TASKHELPINFO_ITEMCOUNT_FIELD.name = "itemcount"
TASKHELPINFO_ITEMCOUNT_FIELD.full_name = ".KKSG.TaskHelpInfo.itemcount"
TASKHELPINFO_ITEMCOUNT_FIELD.number = 4
TASKHELPINFO_ITEMCOUNT_FIELD.index = 3
TASKHELPINFO_ITEMCOUNT_FIELD.label = 1
TASKHELPINFO_ITEMCOUNT_FIELD.has_default_value = false
TASKHELPINFO_ITEMCOUNT_FIELD.default_value = 0
TASKHELPINFO_ITEMCOUNT_FIELD.type = 13
TASKHELPINFO_ITEMCOUNT_FIELD.cpp_type = 3

TASKHELPINFO_TIME_FIELD.name = "time"
TASKHELPINFO_TIME_FIELD.full_name = ".KKSG.TaskHelpInfo.time"
TASKHELPINFO_TIME_FIELD.number = 5
TASKHELPINFO_TIME_FIELD.index = 4
TASKHELPINFO_TIME_FIELD.label = 1
TASKHELPINFO_TIME_FIELD.has_default_value = false
TASKHELPINFO_TIME_FIELD.default_value = 0
TASKHELPINFO_TIME_FIELD.type = 13
TASKHELPINFO_TIME_FIELD.cpp_type = 3

TASKHELPINFO.name = "TaskHelpInfo"
TASKHELPINFO.full_name = ".KKSG.TaskHelpInfo"
TASKHELPINFO.nested_types = {}
TASKHELPINFO.enum_types = {}
TASKHELPINFO.fields = {TASKHELPINFO_ROLEID_FIELD, TASKHELPINFO_ROLENAME_FIELD, TASKHELPINFO_ITEMID_FIELD, TASKHELPINFO_ITEMCOUNT_FIELD, TASKHELPINFO_TIME_FIELD}
TASKHELPINFO.is_extendable = false
TASKHELPINFO.extensions = {}

TaskHelpInfo = protobuf.Message(TASKHELPINFO)


text:
--[[
Copyright (c) 2012-2013 Matthias Richter

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

Except as contained in this notice, the name(s) of the above copyright holders
shall not be used in advertising or otherwise to promote the sale, use or
other dealings in this Software without prior written authorization.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
]] --
local Registry = {}
Registry.__index = function(self, key)
    return Registry[key] or (function()
        local t = {}
        rawset(self, key, t)
        return t
    end)()
end

function Registry:register(s, f)
    self[s][f] = f
    return f
end

function Registry:emit(s, ...) for f in pairs(self[s]) do f(...) end end

function Registry:remove(s, ...)
    local f = {...}
    for i = 1, select('#', ...) do self[s][f[i]] = nil end
end

function Registry:clear(...)
    local s = {...}
    for i = 1, select('#', ...) do self[s[i]] = {} end
end

function Registry:emitPattern(p, ...)
    for s in pairs(self) do if s:match(p) then self:emit(s, ...) end end
end

function Registry:removePattern(p, ...)
    for s in pairs(self) do if s:match(p) then self:remove(s, ...) end end
end

function Registry:clearPattern(p)
    for s in pairs(self) do if s:match(p) then self[s] = {} end end
end

-- the module
local function new()
    local registry = setmetatable({}, Registry)

    return setmetatable({
        new = new,
        register = function(...) return registry:register(...) end,
        emit = function(...) registry:emit(...) end,
        remove = function(...) registry:remove(...) end,
        clear = function(...) registry:clear(...) end,
        emitPattern = function(...) registry:emitPattern(...) end,
        removePattern = function(...) registry:removePattern(...) end,
        clearPattern = function(...) registry:clearPattern(...) end
    }, {__call = new})
end

return new()


text:
local ipairs = ipairs
local type = type
local tostring = tostring

local utils = require("orange.utils.utils")
local orange_db = require("orange.store.orange_db")
local BasePlugin = require("orange.plugins.base_handler")
local counter = require("orange.plugins.rate_limiting.counter")

local function get_current_stat(limit_key)
    return counter.get(limit_key)
end

local function incr_stat(limit_key, limit_type)
    counter.incr(limit_key, 1, limit_type)
end

local RateLimitingHandler = BasePlugin:extend()
RateLimitingHandler.PRIORITY = 1000

function RateLimitingHandler:new(store, redis, config)
    RateLimitingHandler.super.new(self, "rate-limiting-plugin")
    self.store = store
    self.redis = redis
    self.config = config
end

function RateLimitingHandler:access(conf)
    RateLimitingHandler.super.access(self)

    local ngx_var = ngx.var
    local ngx_var_uri = ngx_var.uri
    local ngx_var_host = ngx_var.http_host
    local ngx_var_scheme = ngx_var.scheme
    local ngx_var_args = ngx_var.args

    local host, port, uriId, newUrl = self.redis:queryHostByUri(ngx_var_uri)
    ngx.log(ngx.DEBUG, "self.redis:queryHostByUri.result:".." host : "..host.." port : "..port.."uriId : "..uriId)
    if not host then
        ngx.log(ngx.ERR, "[Proxy][ngx_var_uri:", ngx_var_uri, "]Error!")
        return ngx.exit(ngx.HTTP_NOT_FOUND)
    end

    local current_timetable = utils.current_timetable()
    local time_key = current_timetable["Second"]
    local limit_key = uriId .. "#" .. time_key
    local current_stat = get_current_stat(limit_key) or 0
    local limit_count = 10
    local limit_type = "Second"
    -- 暂时写死 后续从redis读取
    ngx.header["X-RateLimit-Limit" .. "-" .. limit_type] = limit_count
    ngx.log(ngx.ERR,"============================================I'm here============================================")
     ngx.log(ngx.INFO, " uri:", ngx_var_uri, " limit:", limit_count, " reached:", current_stat, " remaining:", 0)

     if current_stat >= limit_count then
          -- ngx.log(ngx.INFO, " uri:", ngx_var_uri, " limit:", limit_count, " reached:", current_stat, " remaining:", 0)

          ngx.header["X-RateLimit-Remaining" .. "-" .. limit_type] = 0
          return ngx.exit(429)
      else
          ngx.header["X-RateLimit-Remaining" .. "-" .. limit_type] = limit_count - current_stat - 1
          incr_stat(limit_key, limit_type)

          -- only for test, comment it in production
          -- if handle.log == true then
          --     ngx.log(ngx.INFO, "[RateLimiting-Rule] ", rule.name, " uri:", ngx_var_uri, " limit:", handle.count, " reached:", current_stat + 1)
          -- end
      end

      return false
end

return RateLimitingHandler


text:
-- Alias System [Settings File] (v1.0)
-- Copyright (c) 2022, Jericho Crosby <jericho.crosby227@gmail.com>

return {

    -- File that contains the json database of aliases:
    -- This file will be created in 'cg/sapp/'
    --
    file = 'aliases.json',

    -- Base command:
    --
    command = 'alias',

    -- Minimum permission required to execute /command:
    --
    permission = 1,

    -- If true, the .json database will be updated
    -- every time a player joins.
    --
    -- If false, the .json database will only be updated
    -- when the game ends (recommended).
    --
    update_file_on_join = false,

    -- If a player hasn't connected with a specific hash or ip
    -- after this many days, it's considered stale and will be deleted:
    --
    delete_stale_records = true,
    stale_period = 30,

    -- When executing /alias <pid>, what should the default look up table be?
    -- Valid Tables: 'ip_addresses' or 'hashes'
    --
    default_table = 'ip_addresses',

    -- IP-alias lookup flag:
    ip_lookup_flag = '-ip',

    -- Hash-alias lookup flag:
    hash_lookup_flag = '-hash',

    --------------------------------------------------------------------------
    -- Advanced users only:
    --
    --
    -- Max results to load per page:
    max_results = 50,

    -- Aliases are printed in columns of 5:
    max_columns = 5,

    -- Spacing between names per column:
    spaces = 2,

    -- List of all known pirated hashes:
    -- If someone has a pirated hash, the script will tell you upon querying the hash:
    --
    known_pirated_hashes = {
        ['388e89e69b4cc08b3441f25959f74103'] = true,
        ['81f9c914b3402c2702a12dc1405247ee'] = true,
        ['c939c09426f69c4843ff75ae704bf426'] = true,
        ['13dbf72b3c21c5235c47e405dd6e092d'] = true,
        ['29a29f3659a221351ed3d6f8355b2200'] = true,
        ['d72b3f33bfb7266a8d0f13b37c62fddb'] = true,
        ['76b9b8db9ae6b6cacdd59770a18fc1d5'] = true,
        ['55d368354b5021e7dd5d3d1525a4ab82'] = true,
        ['d41d8cd98f00b204e9800998ecf8427e'] = true,
        ['c702226e783ea7e091c0bb44c2d0ec64'] = true,
        ['f443106bd82fd6f3c22ba2df7c5e4094'] = true,
        ['10440b462f6cbc3160c6280c2734f184'] = true,
        ['3d5cd27b3fa487b040043273fa00f51b'] = true,
        ['b661a51d4ccf44f5da2869b0055563cb'] = true,
        ['740da6bafb23c2fbdc5140b5d320edb1'] = true,
        ['7503dad2a08026fc4b6cfb32a940cfe0'] = true,
        ['4486253cba68da6786359e7ff2c7b467'] = true,
        ['f1d7c0018e1648d7d48f257dc35e9660'] = true,
        ['40da66d41e9c79172a84eef745739521'] = true,
        ['2863ab7e0e7371f9a6b3f0440c06c560'] = true,
        ['34146dc35d583f2b34693a83469fac2a'] = true,
        ['b315d022891afedf2e6bc7e5aaf2d357'] = true,
        ['63bf3d5a51b292cd0702135f6f566bd1'] = true,
        ['6891d0a75336a75f9d03bb5e51a53095'] = true,
        ['325a53c37324e4adb484d7a9c6741314'] = true,
        ['0e3c41078d06f7f502e4bb5bd886772a'] = true,
        ['fc65cda372eeb75fc1a2e7d19e91a86f'] = true,
        ['f35309a653ae6243dab90c203fa50000'] = true,
        ['50bbef5ebf4e0393016d129a545bd09d'] = true,
        ['a77ee0be91bd38a0635b65991bc4b686'] = true,
        ['3126fab3615a94119d5fe9eead1e88c1'] = true,
    }
}


text:
ITEM.name = "Ruger MKIII"
ITEM.description= "An internally silenced target practice pistol chambered for .22LR."
ITEM.longdesc = "The Ruger Mark III is a .22 Long Rifle semi-automatic pistol manufactured by Sturm, Ruger & Company.\nIt is the successor to the Ruger MK II, and includes several new features.\n\nAmmo: .22LR\nMagazine Capacity: 10"
ITEM.model = ("models/weapons/w_pist_ruger23a1.mdl")
ITEM.class = "cw_rugermk3"
ITEM.weaponCategory = "secondary"
ITEM.width = 2
ITEM.price = 3000
ITEM.height = 1
ITEM.canAttach = false

ITEM.bulletweight = 0.0025
ITEM.unloadedweight = 0.900

ITEM.repair_PartsComplexity = 1
ITEM.repair_PartsRarity = 2

function ITEM:GetWeight()
  return self.unloadedweight + (self.bulletweight * self:GetData("ammo", 0))
end

ITEM.iconCam = {
	pos = Vector(-2, 200, -1.6000000238419),
	ang = Angle(0, 270, -1.2000000476837),
	fov = 4,
}

ITEM.pacData = {
[1] = {
	["children"] = {
		[1] = {
			["children"] = {
				[1] = {
					["children"] = {
					},
					["self"] = {
						["Model"] = "models/weapons/w_pist_ruger23a1.mdl",
						["ClassName"] = "model",
						["Position"] = Vector(-6.862, 3.164, 3.017),
						["EditorExpand"] = true,
						["UniqueID"] = "3543276351",
						["Bone"] = "pelvis",
						["Name"] = "ruger",
						["Angles"] = Angle(0, 90, 30),
					},
				},
			},
			["self"] = {
				["AffectChildrenOnly"] = true,
				["ClassName"] = "event",
				["UniqueID"] = "1532812402",
				["Event"] = "weapon_class",
				["EditorExpand"] = true,
				["Name"] = "weapon class find simple\"@@1\"",
				["Arguments"] = "cw_rugermk3@@0",
			},
		},
	},
	["self"] = {
		["ClassName"] = "group",
		["UniqueID"] = "8358321183",
		["EditorExpand"] = true,
	},
},
}


text:
--[[
  OEML - REST API

  This section will provide necessary information about the `CoinAPI OEML REST API` protocol.
 This API is also available in the Postman application: https://postman.coinapi.io/

 Implemented Standards:   * [HTTP1.0](https://datatracker.ietf.org/doc/html/rfc1945)  * [HTTP1.1](https://datatracker.ietf.org/doc/html/rfc2616)  * [HTTP2.0](https://datatracker.ietf.org/doc/html/rfc7540)

  The version of the OpenAPI document: v1
  Contact: support@coinapi.io
  Generated by: https://openapi-generator.tech
]]

--[[
Unit tests for openapiclient.api.orders_api
Automatically generated by openapi-generator (https://openapi-generator.tech)
Please update as you see appropriate
]]
describe("orders_api", function()
  local openapiclient_orders_api = require "openapiclient.api.orders_api"
  -- unit tests for v1_orders_cancel_all_post
  describe("v1_orders_cancel_all_post test", function()
    it("should work", function()
      -- TODO assertion here: http://olivinelabs.com/busted/#asserts
    end)
  end)

  -- unit tests for v1_orders_cancel_post
  describe("v1_orders_cancel_post test", function()
    it("should work", function()
      -- TODO assertion here: http://olivinelabs.com/busted/#asserts
    end)
  end)

  -- unit tests for v1_orders_get
  describe("v1_orders_get test", function()
    it("should work", function()
      -- TODO assertion here: http://olivinelabs.com/busted/#asserts
    end)
  end)

  -- unit tests for v1_orders_post
  describe("v1_orders_post test", function()
    it("should work", function()
      -- TODO assertion here: http://olivinelabs.com/busted/#asserts
    end)
  end)

  -- unit tests for v1_orders_status_client_order_id_get
  describe("v1_orders_status_client_order_id_get test", function()
    it("should work", function()
      -- TODO assertion here: http://olivinelabs.com/busted/#asserts
    end)
  end)

end)


text:
--[[-----------------------------------------------------------------------------------
 Original Drathals HUD (c) 2006 by Markus Inger / Drathal / Silberklinge / Silbersegen
 DHUD for WotLK and later expansions (c) 2013 by MADCAT (EU-Гордунни, Мадкат)
 (http://eu.battle.net/wow/en/character/гордунни/Мадкат/advanced)
---------------------------------------------------------------------------------------
 This file contains text formatting tools
 @author: MADCAT
-----------------------------------------------------------------------------------]]--

----------------
-- Text tools --
----------------

--- Class to make text changes
DHUDTextTools = {
	-- number to use as boundary to showing milliSeconds
	milliSecondsBound = 10,
	-- number of maximum allowed digits
	numberDigitsLimit = 5,
	-- list of metric prefixes from wikipedia (http://en.wikipedia.org/wiki/Metric_prefix)
	METRIC_PREFIXES = { "k", "M", "G", "T", "P", "E", "Z", "Y" },
	-- table with cached format functions
	cacheFormatToFunction = { },
}

--- setting with short numbers has changed
function DHUDTextTools:onTextShortNumbersSetting(e)
	local value = DHUDSettings:getValue("misc_textShortNumbers");
	self.numberDigitsLimit = value and 5 or 1000;
end

--- setting with milliseconds numbers has changed
function DHUDTextTools:onTextTimerMillisecondsSetting(e)
	local value = DHUDSettings:getValue("misc_textTimerShowMilliSeconds");
	self.milliSecondsBound = value and 10 or 0;
end

--- Initialize DHUDGuiSlotManager static values
function DHUDTextTools:init()
	-- listen to settings change events
	DHUDSettings:addEventListener(DHUDSettingsEvent.EVENT_SPECIFIC_SETTING_CHANGED_PREFIX .. "misc_textShortNumbers", self, self.onTextShortNumbersSetting);
	DHUDSettings:addEventListener(DHUDSettingsEvent.EVENT_SPECIFIC_SETTING_CHANGED_PREFIX .. "misc_textTimerShowMilliSeconds", self, self.onTextTimerMillisecondsSetting);
	self:onTextShortNumbersSetting(nil);
	self:onTextTimerMillisecondsSetting(nil);
end

--- Format seconds to short and readable text (in most cases this function tries to use less than 3 symbols)
-- @param secs number of seconds
-- @return short and readable text
function DHUDTextTools:formatTime(secs, millisecondsBorder)
	-- days
	if secs >= 86400 then
		return format('%dd', floor(secs / 86400 + 0.5));
	-- hours
	elseif secs >= 3600 then
		return format('%dh', floor(secs/3600 + 0.5));
	-- minutes
	elseif secs >= 99 then
		return format('%dm', floor(secs/60 + 0.5));
	-- seconds
	elseif secs >= self.milliSecondsBound then
		return format('%d', floor(secs + 0.5));
	-- milliseconds
	else
		return format('%.1f', secs);
	end
end

--- Format number to short and readable text (in most cases this function tries to use less than <limit> symbols)
-- @param number number to be shortened
-- @param limit limit of chars (maximum number of digit symbols in the result, e.g. 3, 6)
-- @param numberRef number to be used when counting number of digits or nil
-- @return short and readable text
function DHUDTextTools:formatNumber(number, limit, numberRef)
	limit = limit or self.numberDigitsLimit;
	numberRef = numberRef or number;
	local numChars = floor(log10(abs(numberRef)) + 1); -- +1 for digit below
	if (numChars <= limit) then
		return format('%d', number);
	end
	-- 0 should be present as 0
	if (number == 0) then
		return "0";
	end
	local overLimit = numChars - limit;
	local prefixToUse = floor((overLimit + 2) / 3);
	local divideBy = 10 ^ (prefixToUse * 3);
	local prefix = self.METRIC_PREFIXES[prefixToUse] or "";
	-- return formatted number
	return format('%d%s', floor(number / divideBy + 0.5), prefix);
end

--- Format number to short and readable text using number of digits after comma
-- @param number number to be shortened
-- @param precision number of digits after comma
-- @return short and readable text
function DHUDTextTools:formatNumberWithPrecision(number, precision)
	-- return formatted number
	return format('%.' .. precision .. 'f', number);
end

--- Remove server name from full player name
-- @param name full name with server name
-- @return short name without server name
function DHUDTextTools:getShortPlayerName(name)
	local indexS, indexE = strfind(name, "%-", 1); -- search for "-"
	-- special word found?
	if (indexS ~= nil) then
		name = strsub(name, 1, indexS - 1);
	end
	return name;
end

--- Create text that will colorize the text after it
-- @param arg any value, not readed
-- @param colorHex number corresponding to color (user can pass any value here, even table, so be sure to check it's type)
-- @return text string with color
function DHUDTextTools:createTextColorizeStart(arg, colorHex)
	colorHex = colorHex or "ffffff";
	if ("string" == type(colorHex)) then
		return "|cff" .. colorHex;
	end
	return "|cffffffff";
end

--- Create text that will colorize the text after it
-- @param arg any value, not readed
-- @param colorHex number corresponding to color
-- @return text string with color
function DHUDTextTools:createTextColorizeStop(arg)
	return "|r";
end

--- Create function for user specified number format (e.g. only mana, mana with percents, etc), better for performance then using gsub on every possible combination, all created functions are stored in cache
-- @param textFormat user defined text format, all custom values should be in braces (e.g. "<value> (<percent>)" )
-- @param mapFunctions map with functions to be executed (e.g. { "value": function(self, ref) return ref.amount; end } ), this functions will be invoked with nil self arg!
-- @param argToFunctions argument that will be passed to functions from map (e.g. reference to data tracker)
-- @return function to be executed to generate text in format specified
function DHUDTextTools:parseDataFormatToFunction(textFormat, mapFunctions, argToFunctions)
	-- lookup in cache
	local cacheForTextFormat = self.cacheFormatToFunction[textFormat];
	if (cacheForTextFormat == nil) then
		cacheForTextFormat = { };
		self.cacheFormatToFunction[textFormat] = cacheForTextFormat;
	end
	for ci, cv in ipairs(cacheForTextFormat) do
		if (cv[1] == mapFunctions and cv[2] == argToFunctions) then
			return cv[3];
		end
	end
	local cacheValue = { };
	table.insert(cacheForTextFormat, cacheValue);
	-- process
	local indexStrBegin = 1; -- index of text to substring from before "<word>"
	local indexS = 0; -- index at which "<word>" found
	local indexE = 0; -- index at which "<word>" found
	local indexLocalArgsS = 0; -- index at which "(123, 22)" found in "<word>"
	local indexLocalArgsE = 0; -- index at which "(123, 22)" found in "<word>"
	local arrayFunctions = { }; -- array for functions to execute
	local arrayResults = { }; -- array for functions results
	local subString = "";
	local specialWord = "";
	local specialArgs = nil;
	local specialFunc = nil;
	while (true) do
		indexS, indexE = strfind(textFormat, "<.->", indexE + 1); -- search for "<word>"
		-- no more special words found, end search
		if (indexS == nil) then
			subString = strsub(textFormat, indexStrBegin);
			if (#subString ~= 0) then
				table.insert(arrayFunctions, false); -- can't push nil to array
				table.insert(arrayResults, subString);
			end
			break;
		end
		-- special word found, process
		specialWord = strsub(textFormat, indexS + 1, indexE - 1);
		-- search for local function arguments
		specialArgs = nil;
		indexLocalArgsS, indexLocalArgsE = strfind(specialWord, "%(.-%)"); -- search for "(123, 22)" in "<word>"
		-- function local arguments found, process
		if (indexLocalArgsS ~= nil) then
			specialArgs = { strsplit(",", strsub(specialWord, indexLocalArgsS + 1, indexLocalArgsE - 1)) };
			specialWord = strsub(specialWord, 1, indexLocalArgsS - 1);
			--print("specialWord " .. specialWord);
			-- convert to values
			for i, v in ipairs(specialArgs) do
				local evalFunc = loadstring("return " .. v, "DHUD textField arguments");
				if (evalFunc ~= nil) then
					specialArgs[i] = evalFunc();
				end
			end
		end
		-- search function in map
		specialFunc = mapFunctions[specialWord];
		--print("specialWord: " .. specialWord .. ", func exists: " .. (specialFunc and "yes" or "no"));
		-- function found?
		if (specialFunc ~= nil) then
			-- update function if it has local arguments
			if (specialArgs ~= nil) then
				local specialFuncLocal = specialFunc;
				local specialArgsLocal = specialArgs;
				specialFunc = function(self, arg) return specialFuncLocal(self, arg, unpack(specialArgsLocal)); end
			end
			-- insert substring
			subString = strsub(textFormat, indexStrBegin, indexS - 1);
			if (#subString ~= 0) then
				table.insert(arrayFunctions, false); -- can't push nil to array
				table.insert(arrayResults, subString);
			end
			-- insert function
			table.insert(arrayFunctions, specialFunc);
			table.insert(arrayResults, "");
			-- update index of static text
			indexStrBegin = indexE + 1;
		end
	end
	-- create function, it references local vars (arrayFunctions, arrayResults, argToFunctions)
	local func = function()
		--print("arrayResults: " .. table.concat(arrayResults, ", "));
		-- iterate over functions
		for i, v in ipairs(arrayFunctions) do
			if (v) then
				arrayResults[i] = v(nil, argToFunctions);
			end
			--print("arrayResults " .. i .. " is " .. MCTableToString(arrayResults[i]));
		end
		return table.concat(arrayResults);
	end
	-- save in cache
	cacheValue[1] = mapFunctions;
	cacheValue[2] = argToFunctions;
	cacheValue[3] = func;
	return func;
end