• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Python Script to make all vocation related movements into a LUA list (+ semi-automated daily reward system)

krafttomten

Well-Known Member
Joined
Jul 23, 2017
Messages
103
Solutions
1
Reaction score
75
Location
Sweden
First of all, this is a python code. I couldn't find a good place to post this, maybe there is no such place for a reason. But this code has been very useful to me.

Second of all, this is made by a noob, so it is really wonky. But it works. I am hoping that it at least can serve as inspiration to someone here to make something but better. Or that someone can use the code like I do. To avoid having to manually change quest rewards each time I add new items to the game.

The code compares the movements.xml with the items.xml and creates matching pairs which is then formatted into a LUA list.

Here's the code.
Small note, since the potions are not movements, I added those manually through a string and they are only part of the python code because laziness / convenience.
You can add any items manually through this string, and the stuff will be inserted to the corresponding vocation.


Python:
pots = ["""-- Sorcerer Potions
{itemid = 7440, count = {5, 15}, level = 0, slot = "feet", name = "mastermind potion"},
{itemid = 7620, count = {40, 100}, level = 8, slot = "feet", name = "mana potion"},
{itemid = 7589, count = {40, 100}, level = 50, slot = "feet", name = "strong mana potion"},
{itemid = 7590, count = {40, 100}, level = 80, slot = "feet", name = "great mana potion"},
{itemid = 26029, count = {40, 100}, level = 130, slot = "feet", name = "ultimate mana potion"},

-- Sorcerer Gear
""", """-- Druid Potions
{itemid = 7440, count = {40, 100}, level = 0, slot = "feet", name = "mastermind potion"},
{itemid = 7620, count = {40, 100}, level = 8, slot = "feet", name = "mana potion"},
{itemid = 7589, count = {40, 100}, level = 50, slot = "feet", name = "strong mana potion"},
{itemid = 7590, count = {40, 100}, level = 80, slot = "feet", name = "great mana potion"},
{itemid = 26029, count = {40, 100}, level = 130, slot = "feet", name = "ultimate mana potion"},

-- Druid Gear
""", """-- Paladin Potions
{itemid = 7443, count = {5, 15}, level = 0, slot = "feet", name = "bullseye potion"},
{itemid = 7618, count = {40, 100}, level = 8, slot = "feet", name = "health potion"},
{itemid = 7588, count = {40, 100}, level = 50, slot = "feet", name = "strong health potion"},
{itemid = 7589, count = {40, 100}, level = 50, slot = "feet", name = "strong mana potion"},
{itemid = 8472, count = {40, 100}, level = 80, slot = "feet", name = "great spirit potion"},
{itemid = 26030, count = {40, 100}, level = 130, slot = "feet", name = "supreme health potion"},
{itemid = 26031, count = {40, 100}, level = 130, slot = "feet", name = "ultimate spirit potion"},

-- Paladin gear
""", """-- Knight Potions
{itemid = 7439, count = {5, 15}, level = 0, slot = "feet", name = "berserk potion"},
{itemid = 7618, count = {40, 100}, level = 8, slot = "feet", name = "health potion"},
{itemid = 7620, count = {40, 100}, level = 0, slot = "feet", name = "mana potion"},
{itemid = 7588, count = {40, 100}, level = 50, slot = "feet", name = "strong health potion"},
{itemid = 7591, count = {40, 100}, level = 80, slot = "feet", name = "great health potion"},
{itemid = 8473, count = {40, 100}, level = 130, slot = "feet", name = "ultimate health potion"},

-- Knight Gear
"""]

filename = "Voc_list"
movements_path = "PATH_TO_MOVEMENTS.XML"
items_path = "PATH_TO_ITEMS.XML"

output_list = []  # Raw list with all the movements

vocList = []  # List with all indexed vocations
itemIdList = []  # List of indexed ID numbers
nameList = []  # List of indexed item names
slotList = []  # List of indexed item slots
levelReqList = []  # List of indexed level requirements

main_output_list = []  # Combinated list with all indexed lists


#  Function to read a XML file and turn it's content into a list
#  with each line in the XML as one item in the list
def make_raw_list(path):
    with open(path, "r") as m:
        m_content = m.read()
        split_cont = m_content.split("\n")
        tmp_work_list = []
        for i2 in split_cont:
            func_stripped_line = i2.strip()
            i2_list = func_stripped_line.split("\n")
            tmp_work_list.append(i2_list)
    out_list = []
    for i3 in tmp_work_list:
        out_list += i3
    return out_list


def count_vocation_index(all_vocations):
    last_voc = ""
    indexes = []
    for key2, value2 in enumerate(all_vocations):
        if not last_voc == value2:
            indexes.append(key2)
            last_voc = value2
    return indexes


#  Function to export final list with format to fit the final code
def export(npc_name):
    voc_indexes = count_vocation_index(vocList)
    count = 1
    with open(f"{npc_name}.lua", "w") as npc_out:
        for key3, grej in enumerate(main_output_list):
            for key22, grej2 in enumerate(voc_indexes):
                if int(key3) == int(grej2):
                    npc_out.write("[" + str(count) + "] = {\n")
                    npc_out.write(pots[count - 1])
                    npc_out.write(grej)
                    npc_out.write("\n")
                    count += 1
                    break
                elif int(key3) == int(grej2) - 1:
                    npc_out.write(grej.strip(","))
                    npc_out.write("\n},\n\n")
                    break
                elif int(key3) == len(main_output_list) - 1:
                    npc_out.write(grej.strip(","))
                    npc_out.write("\n")
                    break
                elif int(key22) == 3:
                    npc_out.write(grej)
                    npc_out.write("\n")
        npc_out.write("},\n")


#  Reading the files and turning them into lists
#  with the make_raw_list function
movements_list = make_raw_list(movements_path)
items_list = make_raw_list(items_path)

#  This code pairs each vocation to each item.
vocations = ["Sorcerer", "Druid", "Paladin", "Knight"]
for i in vocations:
    antal = 1
    for k, v in enumerate(movements_list):
        if v.__contains__(f'<vocation name="{i}"'):
            save = False
            index = k
            while not save:
                if movements_list[index].__contains__('itemid="') or movements_list[index].__contains__('fromid="'):
                    output_list.append(f"{i}: {movements_list[index]}")
                    vocList.append(i)
                    save = True
                else:
                    index -= 1

# The next tast is to format the output string in this way:
# {itemid = 18410, count = {1}, voc = "knight", level = 150, slot = "shield", name = "prismatic shield"},


for i in output_list:
    itemId = i.split(" ")[3].strip('"')[8:]
    itemIdList.append(itemId)

itemNamnStringList = []  # Temporary list to sort item names
for i in itemIdList:
    for rad in items_list:
        if rad.__contains__(f'id="{i}"'):
            itemNamnStringList.append(rad)

for i in itemNamnStringList:
    nameList.append(i[i.find("name=") + 6:- 2])

for i in output_list:
    for sak in i.split(" "):
        if sak.__contains__('slot="'):
            slotList.append(sak[6:-1])

for i in output_list:
    if i.__contains__('level="'):
        for sak in i.split(" "):
            if sak.__contains__('level="'):
                levelReqList.append(sak[7:-1])
    else:
        levelReqList.append("0")

for key, value in enumerate(output_list):
    main_output_list.append('{itemid = ' + itemIdList[key]
                            + ', count = {1}, '
                            #                            + 'voc = "' + vocList[key].lower() + '", '  # Remove this comment to include a vocation variable in the code
                            + 'level = ' + levelReqList[key]
                            + ', slot = "' + slotList[key]
                            + '", name = "' + nameList[key]
                            + '"},')

export(filename)
#  print(count_vocation_index(vocList))


I'll throw in my daily quest reward system aswell, with an empty reward list, since this one is created through the python code.
Lua:
function onUse(cid, item, fromPosition, itemEx, toPosition, isHotkey)
    local config = {
        storage = item.uid,
        exstorage = item.uid + 1,
        rewards = {
        -- Just paste in the result from the python code in here
        }
   
    }
    local player = Player(cid)
    local q = player:getLevel()

    -- Add quests here with [NUMBER] being the UID of the used item.
    local lvLi = {
        [43200] = {li = {1, 45}, expMod = 1},--Ghoul
    }
    if (player:getLevel() < lvLi[item.uid].li[1]) or (player:getLevel() > lvLi[item.uid].li[2]) then
        return player:sendTextMessage(MESSAGE_INFO_DESCR, "You do not meet the level requirements for this reward.")
    end

    local x = config.rewards[player:getVocation():getBase():getId()]

-- This part can be added if you want a common pool of items for all vocations, and that pool would be added as [5] = {List} in the config.rewards above.
--    if math.random(100) > 50 then
--       x = config.rewards[5]
--    end
    local loop = true
    local c = 1
        while loop do
        c = math.random(#x)
        if  (x[c].level <= q and (x[c].level >= (q / 3)) or x[c].level == 0) then
            loop = false
        end
    end


    -- EXPERIENCE REWARD (random amount, from 0.5 to 2 full levels * the expMod (EXP modifier) from the lvLi variable above )
    local one_level = 50 * ((q * q) - (5 * q) + 8)
    local xp = math.floor(math.random(one_level / 2, one_level * 2))-- math.floor is a way to round the numbers down to avoid crazy floats.
    xp = xp * lvLi[item.uid].expMod

    -- Each UID can be used once a day
    if player:getStorageValue(config.storage) == tonumber(os.date("%w")) and player:getStorageValue(config.exstorage) > os.time() then
        return player:sendTextMessage(MESSAGE_INFO_DESCR, "The chest is empty, come back tomorrow for a new reward.")
    end

    -- This just gets a reward from the list. Pretty standard stuff.
    local info, count = ItemType(x[c].itemid),
        x[c].count[2] and math.random(x[c].count[1], x[c].count[2]) or x[c].count[1]
    if count > 1 then
        text = count .. " " .. info:getPluralName()
    else
        text = info:getArticle() .. " " .. info:getName()
    end
   
    local itemx = Game.createItem(x[c].itemid, count)
    if player:addItemEx(itemx) ~= RETURNVALUE_NOERROR then
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        text = "You have found a reward weighing " .. itemx:getWeight() ..
                   " oz. It is too heavy or you have not enough space."
    else
        text = "You have received " .. text .. ". Come back tomorrow for a new reward."
        player:setStorageValue(config.storage, tonumber(os.date("%w")))
        player:setStorageValue(config.exstorage, os.time() + 24 * 60 * 60)
        doPlayerAddExp(cid, xp)

    end
    player:sendTextMessage(MESSAGE_INFO_DESCR, text .. " You have also recieved " .. xp .. " experience points!")
    return true
end

and ofc you need some way to run the script. I use an actionid in an onUse actions declared in actions.xml, like

XML:
<action actionid="13337" script="quests/INSERT_NAME_OF_CODE.lua" />


The output of the python code should look something like this:
pythonOutputSnippet.png

Hopefully this can help someone out :)
 
Very nice! This is actually super awesome for someone looking to make custom ot or even just custom equipment, as it lets them know all the currently registered items, thanks man, much appreciated!
 
Well, almost. It only takes any registered item that has a vocation dependancy. I am using a separate code to find the rest of the registered items, but it is kind of entangled with the first code, and it runs with imports and stuff so it's less self contained.

But here's a version of the code that finds all the registered items. It was easy enough to convert.

Python:
def no_voc_list():
    movements_path = "PATH_TO_movements.xml"
    items_path = "PATH_TO_items.xml"
    with open(movements_path, "r") as m:
        f_content = m.read()
        split_content = f_content.split("\n")
        temp_work_list = []
        for i in split_content:
            stripped_line = i.strip()
            i_list = stripped_line.split("\n")
            temp_work_list.append(i_list)

    movements_list = []
    for i in temp_work_list:
        movements_list += i

    with open(items_path, "r") as item:
        f_content = item.read()
        split_content = f_content.split("\n")
        temp_item_list = []
        for i in split_content:
            stripped_line = i.strip()
            i_list = stripped_line.split("\n")
            temp_item_list.append(i_list)

    items_list = []
    for i in temp_item_list:
        items_list += i

    output_list = []  # En lista med alla movements, med en massa trams.
    for k, v in enumerate(movements_list):
        if v.__contains__(f'event="Equip" itemid="') or v.__contains__(f'event="Equip" fromid="'):
            output_list.append(f"{i}: {movements_list[k]}")

    # Format everything like this {itemid = 2514, count = 1, level=0}
    item_ids = []  # Alla ID nummer utan en massa trams    ID
    for i in output_list:
        item_id = i.split(" ")[3].strip('"')[8:]
        item_ids.append(item_id)

    item_namn_string_list = []
    for i in item_ids:
        for rad in items_list:
            if rad.__contains__(f'id="{i}"'):
                item_namn_string_list.append(rad)

    name_list = []  # En lista med alla itemnamn utan en massa trams.     NAMN
    for i in item_namn_string_list:
        name_list.append(i[i.find("name=") + 6:- 2])

    #  for key, value in enumerate(nameList):
    #      print(f'{output_list[key]} Name={value}"')

    slot_list = []  # En lista med alla slots utan en massa trams    SLOT
    for i in output_list:
        for sak in i.split(" "):
            if sak.__contains__('slot="'):
                slot_list.append(sak[6:-1])

    level_req_list = []  # En lista med alla levelReqs utan en massa trams    LEVEL
    for i in output_list:
        if i.__contains__('level="'):
            for sak in i.split(" "):
                if sak.__contains__('level="'):
                    level_req_list.append(sak[7:-1])
        else:
            level_req_list.append("0")

    main_output_list = []
    for key, value in enumerate(output_list):
        main_output_list.append('{itemid = ' + item_ids[key] + ', count = {1}, level = '
                                + level_req_list[key] + ', slot = "' + slot_list[key] + '", name = "' +
                                name_list[key] + '"},')
    return main_output_list


def export(npc_name):
    with open(f"{npc_name}.lua", "w") as npc_out:
        for grej in no_voc_list():
            npc_out.write(grej)
            npc_out.write("\n")


export("allRegisteredItems")

I apologize for the mess and the swedish comments.
 
Last edited:
Back
Top