MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

The role-playing games (I-X) that started it all and the various spin-offs (including Dark Messiah).

Moderator: Moderators

StormyG7
Leprechaun
Leprechaun
Posts: 24
Joined: 18 Nov 2016

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby StormyG7 » Oct 22 2021, 21:20

I'm trying to create a way to export/import a party member to get around the 50 player limit by making MMExtension lua scripts in MM8 (specifically the World of Enroth MM 6,7,8 merge; community master branch). Problem is, I don't know if I have access to all of the elements of a party member's data tables needed to make the script I want. I discovered 'Party' is the player sorta/kinda struct that contains from which I can modify or get player data, e.g. "Party[#]" appended with .Name or .Experience, seems to indicate it's the array struct for party members.

In Greyface's help page for MMExtension v2.2, notably "dump(Party[0].Stats)" is given as an example to output player stats. I wish I'd seen that first because I was taking shots in the dark browsing deeper in the help page, forums, and MM8 script files. Anyway, if that contains everything, then I should be able to make it work; unless someone smarter here sees a reason why it's doomed to fail?

dstar
Leprechaun
Leprechaun
Posts: 4
Joined: 17 Oct 2021

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby dstar » Oct 23 2021, 15:29

DaveHer wrote: Oct 17 2021, 7:59
dstar wrote: Oct 17 2021, 3:35 I think I'm missing something. I want to install MM8 choose party, which requires MMExtension, but I'm not sure how to install MMExtension? Where do I find the installation instructions?
Place the folds into the folder where MM8 is installed (where mm8.exe is). You must also put the folders of the MMEditor there.
David
Thanks! I was overthinking it, I guess.

StormyG7
Leprechaun
Leprechaun
Posts: 24
Joined: 18 Nov 2016

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby StormyG7 » Oct 26 2021, 7:05

I've been working on a script for the MM 6,7,8 merge to get around the 50 party member limit of MM8 by importing and exporting them to text files. So far, this is what I've got. I'm still in my first year of school for software engineering, so it's not great, or completely finished, but as is, it mostly works. The problem is, I have to use an entry on roster.txt in order to import/export. I have no idea how to implement reserving a space on the roster for importing characters from text files.

Code: Select all

--EXPORT-----------------------------------
function EPM()--TO DO: Create a way to export party members
    
    local filename = "test" --TO DO: Rename the file using character specific info
    local filepath = string.format(".\\Adventurers\\%s.txt", filename)

    local file = io.open(filepath,"w")

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++START Character Info\n")
    file:write( string.format("Name:\t%s\n", Party[Game.CurrentPlayer].Name) )
    file:write( string.format("Class #:\t%d\n", Party[Game.CurrentPlayer].Class) )
    file:write( string.format("Race #:\t%d\n", Party[Game.CurrentPlayer].Attrs.Race) )
    file:write( string.format("Birth Year:\t%d\n", Party[Game.CurrentPlayer].BirthYear) )
    file:write( string.format("Biography:\t%s\n", Party[Game.CurrentPlayer].Biography) )
    file:write( string.format("Level:\t%d\n", Party[Game.CurrentPlayer].LevelBase) )
    file:write( string.format("Experience:\t%d\n", Party[Game.CurrentPlayer].Experience) )
    file:write( string.format("Face #:\t%d\n", Party[Game.CurrentPlayer].Face) )
    file:write( string.format("Voice #:\t%d\n", Party[Game.CurrentPlayer].Voice) )
    
    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Attributes\t\n")
    file:write( string.format("Might:\t%d\n", Party[Game.CurrentPlayer].MightBase) )
    file:write( string.format("Intellect:\t%d\n", Party[Game.CurrentPlayer].IntellectBase) )
    file:write( string.format("Personality:\t%d\n", Party[Game.CurrentPlayer].PersonalityBase) )
    file:write( string.format("Endurance:\t%d\n", Party[Game.CurrentPlayer].EnduranceBase) )
    file:write( string.format("Accuracy:\t%d\n", Party[Game.CurrentPlayer].AccuracyBase) )
    file:write( string.format("Speed:\t%d\n", Party[Game.CurrentPlayer].SpeedBase) )
    file:write( string.format("Luck:\t%d\n", Party[Game.CurrentPlayer].LuckBase) )
    file:write( string.format("Hit Points:\t%d\n", Party[Game.CurrentPlayer].HP) )
    file:write( string.format("Spell Points:\t%d\n", Party[Game.CurrentPlayer].SP) )

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Resistances\n")
    file:write( string.format("Fire Resistance:\t%d\n", Party[Game.CurrentPlayer].FireResistanceBase) )
    file:write( string.format("Air Resistance:\t%d\n", Party[Game.CurrentPlayer].AirResistanceBase) )
    file:write( string.format("Water Resistance:\t%d\n", Party[Game.CurrentPlayer].WaterResistanceBase) )
    file:write( string.format("Earth Resistance:\t%d\n", Party[Game.CurrentPlayer].EarthResistanceBase) )
    file:write( string.format("Mind Resistance:\t%d\n", Party[Game.CurrentPlayer].MindResistanceBase) )
    file:write( string.format("Body Resistance:\t%d\n", Party[Game.CurrentPlayer].BodyResistanceBase) )
    file:write( string.format("Spirit Resistance:\t%d\n", Party[Game.CurrentPlayer].SpiritResistanceBase) )
    local r = 0
    while r<11 do
        file:write( string.format("Resistance #%d:\t%d\n", r+1, Party[Game.CurrentPlayer].Resistances[r].Base) )
        r = r + 1
    end

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Skills\n")
    file:write( string.format("Skill Points to Spend:\t%d\n", Party[Game.CurrentPlayer].SkillPoints) )
    local sk = 0
    while sk<39 do
        file:write( string.format("Skill #%d:\t%d\n", sk+1, Party[Game.CurrentPlayer].Skills[sk]) )
        sk = sk + 1
    end

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Condition\n")
    file:write( string.format("Additional Years Aged:\t%d\n", Party[Game.CurrentPlayer].AgeBonus) )
    file:write( string.format("Cursed:\t%d\n", Party[Game.CurrentPlayer].Conditions[0]) )
    file:write( string.format("Weak:\t%d\n", Party[Game.CurrentPlayer].Conditions[1]) )
    file:write( string.format("Asleep:\t%d\n", Party[Game.CurrentPlayer].Conditions[2]) )
    file:write( string.format("Afraid:\t%d\n", Party[Game.CurrentPlayer].Conditions[3]) )
    file:write( string.format("Drunk:\t%d\n", Party[Game.CurrentPlayer].Conditions[4]) )
    file:write( string.format("Insane:\t%d\n", Party[Game.CurrentPlayer].Conditions[5]) )
    file:write( string.format("Poison1:\t%d\n", Party[Game.CurrentPlayer].Conditions[6]) )
    file:write( string.format("Disease1:\t%d\n", Party[Game.CurrentPlayer].Conditions[7]) )
    file:write( string.format("Poison2:\t%d\n", Party[Game.CurrentPlayer].Conditions[8]) )
    file:write( string.format("Disease2:\t%d\n", Party[Game.CurrentPlayer].Conditions[9]) )
    file:write( string.format("Poison3:\t%d\n", Party[Game.CurrentPlayer].Conditions[10]) )
    file:write( string.format("Disease3:\t%d\n", Party[Game.CurrentPlayer].Conditions[11]) )
    file:write( string.format("Paralyzed:\t%d\n", Party[Game.CurrentPlayer].Conditions[12]) )
    file:write( string.format("Unconscious:\t%d\n", Party[Game.CurrentPlayer].Conditions[13]) )
    file:write( string.format("Dead:\t%d\n", Party[Game.CurrentPlayer].Conditions[14]) )
    file:write( string.format("Stoned:\t%d\n", Party[Game.CurrentPlayer].Conditions[15]) )
    file:write( string.format("Eradicated:\t%d\n", Party[Game.CurrentPlayer].Conditions[16]) )
    file:write( string.format("Zombie:\t%d\n", Party[Game.CurrentPlayer].Conditions[17]) )
    file:write( string.format("Unkown1:\t%d\n", Party[Game.CurrentPlayer].Conditions[18]) )
    file:write( string.format("Unkown2:\t%d\n", Party[Game.CurrentPlayer].Conditions[19]) )

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Equipment\n")
    file:write( string.format("Left Hand:\t%d\n", Party[Game.CurrentPlayer].ItemExtraHand) )
    file:write( string.format("Right Hand:\t%d\n", Party[Game.CurrentPlayer].ItemMainHand) )
    file:write( string.format("Bow:\t%d\n", Party[Game.CurrentPlayer].ItemBow) )
    file:write( string.format("Armor:\t%d\n", Party[Game.CurrentPlayer].ItemArmor) )
    file:write( string.format("Head:\t%d\n", Party[Game.CurrentPlayer].ItemHelm) )
    file:write( string.format("Belt:\t%d\n", Party[Game.CurrentPlayer].ItemBelt) )
    file:write( string.format("Cloak:\t%d\n", Party[Game.CurrentPlayer].ItemCloak) )
    file:write( string.format("Gloves:\t%d\n", Party[Game.CurrentPlayer].ItemGountlets) )
    file:write( string.format("Boots:\t%d\n", Party[Game.CurrentPlayer].ItemBoots) )
    file:write( string.format("Amulet:\t%d\n", Party[Game.CurrentPlayer].ItemAmulet) )
    file:write( string.format("Ring 1:\t%d\n", Party[Game.CurrentPlayer].ItemRing1) )
    file:write( string.format("Ring 2:\t%d\n", Party[Game.CurrentPlayer].ItemRing2) )
    file:write( string.format("Ring 3:\t%d\n", Party[Game.CurrentPlayer].ItemRing3) )
    file:write( string.format("Ring 4:\t%d\n", Party[Game.CurrentPlayer].ItemRing4) )
    file:write( string.format("Ring 5:\t%d\n", Party[Game.CurrentPlayer].ItemRing5) )
    file:write( string.format("Ring 6:\t%d\n", Party[Game.CurrentPlayer].ItemRing6) )

    local i = 0
    local j = 0
    local k = 0
    local l = 0
    local m = 0
    local n = 0
    local o = 0
    local p = 1
    local q = 0

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Inventory\n")
    while j<126  do
        file:write( string.format("Row %d/9, Slot %d/14:\t%d\n", math.ceil((j+1) / 14), (j+14) % 14 + 1, Party[Game.CurrentPlayer].Inventory[j]) )
        j = j + 1
    end

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Item Data\n")
    while p<138 do
        file:write( string.format("Item %d Body Location:\t%d\n", p, Party[Game.CurrentPlayer].Items[p].BodyLocation) )
        file:write( string.format("Item %d Bonus 1:\t%d\n", p, Party[Game.CurrentPlayer].Items[p].Bonus) )
        file:write( string.format("Item %d Bonus 2:\t%d\n", p, Party[Game.CurrentPlayer].Items[p].Bonus2) )
        file:write( string.format("Item %d Bonus Expiration Time:\t%d\n", p, Party[Game.CurrentPlayer].Items[p].BonusExpireTime) )
        file:write( string.format("Item %d Bonus Strength:\t%d\n", p, Party[Game.CurrentPlayer].Items[p].BonusStrength) )
        file:write( string.format("Item %d Broken:\t%s\n", p, tostring(Party[Game.CurrentPlayer].Items[p].Broken)) )
        file:write( string.format("Item %d Charges:\t%d\n", p, Party[Game.CurrentPlayer].Items[p].Charges) )
        file:write( string.format("Item %d Condition:\t%d\n", p, Party[Game.CurrentPlayer].Items[p].Condition) )
        file:write( string.format("Item %d Hardened:\t%s\n", p, tostring(Party[Game.CurrentPlayer].Items[p].Hardened)) )
        file:write( string.format("Item %d Identified:\t%s\n", p, tostring(Party[Game.CurrentPlayer].Items[p].Identified)) )
        file:write( string.format("Item %d Max Charges:\t%d\n", p, Party[Game.CurrentPlayer].Items[p].MaxCharges) )
        file:write( string.format("Item %d Number:\t%d\n", p, Party[Game.CurrentPlayer].Items[p].Number) )
        file:write( string.format("Item %d Owner:\t%d\n", p, Party[Game.CurrentPlayer].Items[p].Owner) )
        file:write( string.format("Item %d Stolen:\t%s\n", p, tostring(Party[Game.CurrentPlayer].Items[p].Stolen)) )
        file:write( string.format("Item %d Temporary Bonus:\t%s\n", p, tostring(Party[Game.CurrentPlayer].Items[p].TemporaryBonus)) )
        p = p + 1
    end
    
    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Spell Book\n")
    while i<137 do
        file:write( string.format("Knows Spell %d:\t%s\n", i+1, tostring(Party[Game.CurrentPlayer].Spells[i]) ) )
        i = i + 1
    end
    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Spell Vars\n")
    file:write( string.format("Current Spell Book Page:\t%d\n", Party[Game.CurrentPlayer].SpellBookPage) )
    file:write( string.format("Current Quick Spell:\t%d\n", Party[Game.CurrentPlayer].QuickSpell) )
    file:write( string.format("Armageddon Casts:\t%d\n", Party[Game.CurrentPlayer].ArmageddonCasts) )
    file:write( string.format("Divine Intervention Casts:\t%d\n", Party[Game.CurrentPlayer].DevineInterventionCasts) )
    file:write( string.format("Fire Spike Casts:\t%d\n", Party[Game.CurrentPlayer].FireSpikeCasts) )

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Lloyd's Beacons\n")
    while n<5 do
        file:write( string.format("Beacon %d Active:\t%d\n", n+1, Party[Game.CurrentPlayer].Beacons[n].Active) )
        file:write( string.format("Beacon %d Direction:\t%d\n", n+1, Party[Game.CurrentPlayer].Beacons[n].Direction) )
        file:write( string.format("Beacon %d Expire Time:\t%d\n", n+1, Party[Game.CurrentPlayer].Beacons[n].ExpireTime) )
        file:write( string.format("Beacon %d Look Angle:\t%d\n", n+1, Party[Game.CurrentPlayer].Beacons[n].LookAngle) )
        file:write( string.format("Beacon %d Map:\t%s\n", n+1, tostring(Party[Game.CurrentPlayer].Beacons[n].Map) ) )
        file:write( string.format("Beacon %d Map Index:\t%d\n", n+1, Party[Game.CurrentPlayer].Beacons[n].MapIndex) )
        while o<3 do
            file:write( string.format("Beacon %d Pos %d:\t%d\n", n+1, o+1, Party[Game.CurrentPlayer].Beacons[n].Pos[o]) )
            o = o + 1
        end
        file:write( string.format("Beacon %d X:\t%d\n", n+1, Party[Game.CurrentPlayer].Beacons[n].X) )
        file:write( string.format("Beacon %d Y:\t%d\n", n+1, Party[Game.CurrentPlayer].Beacons[n].Y) )
        file:write( string.format("Beacon %d Z:\t%d\n", n+1, Party[Game.CurrentPlayer].Beacons[n].Z) )
        n = n + 1
        o = 0
    end

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Black Potions Used\n")
    while k<7 do
        file:write( string.format("Black Potion %d:\t%s\n", k+1, tostring(Party[Game.CurrentPlayer].UsedBlackPotions[k+264]) ) )
        k = k + 1
    end

    --TO DO: Figure out how to get the number of potential promotion awards there are to make this loop complete the number of iterations needed
    --[[file:write("\nPromotion Awards\n")
    while l< #Party[Game.CurrentPlayer].Attrs.PromoAwards do
        file:write( string.format("Promotion Award d%:\t%s\n", l+1, tostring(Party[Game.CurrentPlayer].Attrs.PromoAwards[l]) ) )
        l = l + 1
    end--]]

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++General Awards\n")
    while m<104 do
        file:write( string.format("Award %d:\t%s\n", m+1, tostring(Party[Game.CurrentPlayer].Awards[m]) ) )
        m = m + 1
    end

    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++Player Bits\n")
    while q<511 do
        file:write( string.format("Player Bit %d:\t%s\n", q+1, tostring(Party[Game.CurrentPlayer].PlayerBits[q]) ) )
        q = q + 1
    end
    file:write("\t++++++++++++++++++++++++++++++++++++++++++++++END")
    file:close()

end
--IMPORT-----------------------------------
function IPM()




    
    local lastHired = -1
    for _, pl in Party do
        lastHired = lastHired + 1
    end

    HireCharacter(49)
    
    local filename = "test"
    local filepath = string.format(".\\Adventurers\\%s.txt", filename)

    local file = io.open(filepath,"r")
    if file ~= nil then
        local lines = {}

        for line in io.lines(filepath) do 
            lines[#lines + 1] = line
        end
        file:close()

        --Test to see everything is there
        --for k, v in pairs(lines) do
        --    print(k, v)
        --end

        for k, v in pairs(lines) do
            lines[k] = string.format(string.sub(string.sub(lines[k],string.find( lines[k], "\t"),string.len(lines[k])),2))
        end

        --Test to see everything is STILL there
        --for k, v in pairs(lines) do
        --    print(k, v)
        --end

        --Character Info Section
        Party[lastHired].Name = lines[2]
        Party[lastHired].Class = tonumber(lines[3])
        Party[lastHired].Race = tonumber(lines[4])
        Party[lastHired].BirthYear = tonumber(lines[5])
        Party[lastHired].Biography = lines[6]
        Party[lastHired].LevelBase = tonumber(lines[7])
        Party[lastHired].Experience = tonumber(lines[8])
        Party[lastHired].Face = tonumber(lines[9])
        Party[lastHired].Voice = tonumber(lines[10])

        --Stats
        Party[lastHired].MightBase = tonumber(lines[12])
        Party[lastHired].IntellectBase = tonumber(lines[13])
        Party[lastHired].PersonalityBase = tonumber(lines[14])
        Party[lastHired].EnduranceBase = tonumber(lines[15])
        Party[lastHired].AccuracyBase = tonumber(lines[16])
        Party[lastHired].SpeedBase = tonumber(lines[17])
        Party[lastHired].LuckBase = tonumber(lines[18])
        Party[lastHired].HP = tonumber(lines[19])
        Party[lastHired].SP = tonumber(lines[20])

        --Resistances
        Party[lastHired].FireResistanceBase = tonumber(lines[22])
        Party[lastHired].AirResistanceBase = tonumber(lines[23])
        Party[lastHired].WaterResistanceBase = tonumber(lines[24])
        Party[lastHired].EarthResistanceBase = tonumber(lines[25])
        Party[lastHired].MindResistanceBase = tonumber(lines[26])
        Party[lastHired].BodyResistanceBase = tonumber(lines[27])
        Party[lastHired].SpiritResistanceBase = tonumber(lines[28])
        local r = 0
        while r<11 do
            Party[lastHired].Resistances[r].Base = tonumber(lines[29+r])
            r = r + 1
        end
        
        --Skills
        Party[lastHired].SkillPoints = tonumber(lines[41])
        local i = 0
        while i<39 do
            Party[lastHired].Skills[i] = tonumber(lines[42+i])
            i = i + 1
        end

        --Conditions
        Party[lastHired].AgeBonus = tonumber(lines[82])
        local c = 0
        while c<20 do
            Party[lastHired].Conditions[c] = tonumber(lines[83+c])
            c = c + 1
        end
        
        --Equipment
        Party[lastHired].ItemExtraHand = tonumber(lines[104])
        Party[lastHired].ItemMainHand = tonumber(lines[105])
        Party[lastHired].ItemBow = tonumber(lines[106])
        Party[lastHired].ItemArmor = tonumber(lines[107])
        Party[lastHired].ItemHelm = tonumber(lines[108])
        Party[lastHired].ItemBelt = tonumber(lines[109])
        Party[lastHired].ItemCloak = tonumber(lines[110])
        Party[lastHired].ItemGountlets = tonumber(lines[111])
        Party[lastHired].ItemBoots = tonumber(lines[112])
        Party[lastHired].ItemAmulet = tonumber(lines[113])
        Party[lastHired].ItemRing1 = tonumber(lines[114])
        Party[lastHired].ItemRing2 = tonumber(lines[115])
        Party[lastHired].ItemRing3 = tonumber(lines[116])
        Party[lastHired].ItemRing4 = tonumber(lines[117])
        Party[lastHired].ItemRing5 = tonumber(lines[118])
        Party[lastHired].ItemRing6 = tonumber(lines[119])

        --Inventory
        local j = 0
        while j<126  do
            Party[lastHired].Inventory[j] = tonumber(lines[121+j])
            j = j + 1
        end

        --Item Data
        local p = 0
        local z = 0
        while p<137 do
            Party[lastHired].Items[p+1].BodyLocation = tonumber(lines[248+z])
            Party[lastHired].Items[p+1].Bonus = tonumber(lines[249+z])
            Party[lastHired].Items[p+1].Bonus2 = tonumber(lines[250+z])
            Party[lastHired].Items[p+1].BonusExpireTime = tonumber(lines[251+z])
            Party[lastHired].Items[p+1].BonusStrength = tonumber(lines[252+z])
            if string.match(tostring(lines[253+z]),"false") then
                Party[lastHired].Items[p+1].Broken = false
            else
                Party[lastHired].Items[p+1].Broken = true
            end
            Party[lastHired].Items[p+1].Charges = tonumber(lines[254+z])
            Party[lastHired].Items[p+1].Condition = tonumber(lines[255+z])
            if string.match(tostring(lines[253+z]),"false") then
                Party[lastHired].Items[p+1].Hardened = false
            else
                Party[lastHired].Items[p+1].Hardened = true
            end
            if string.match(tostring(lines[253+z]),"false") then
                Party[lastHired].Items[p+1].Identified = false
            else
                Party[lastHired].Items[p+1].Identified = true
            end
            Party[lastHired].Items[p+1].MaxCharges = tonumber(lines[258+z])
            Party[lastHired].Items[p+1].Number = tonumber(lines[259+z])
            Party[lastHired].Items[p+1].Owner = tonumber(lines[260+z])
            if string.match(tostring(lines[253+z]),"false") then
                Party[lastHired].Items[p+1].Stolen = false
            else
                Party[lastHired].Items[p+1].Stolen = true
            end
            if string.match(tostring(lines[253+z]),"false") then
                Party[lastHired].Items[p+1].TemporaryBonus = false
            else
                Party[lastHired].Items[p+1].TemporaryBonus = true
            end
            p = p + 1
            z = z + 15
        end

        --Spellbook
        i = 0
        while i<137 do
            Party[lastHired].Spells[i] = tonumber(lines[121+i])
            i = i + 1
        end

    else
        file:close()
        MessageBox("%s does not exist", filename)
    end

end

User avatar
GrayFace
Round Table Hero
Round Table Hero
Posts: 1562
Joined: 29 Nov 2005

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby GrayFace » Nov 4 2021, 0:46

StormyG7 wrote: Oct 22 2021, 21:20 I'm trying to create a way to export/import a party member to get around the 50 player limit by making MMExtension lua scripts in MM8 (specifically the World of Enroth MM 6,7,8 merge; community master branch).
The problem is lloyd beacon images and also any MMExt scripts that may have some info stored for each player based on index. Code patching would be required to deal with lloyd beacons.
The player structure can be backed up like this:
local pl = Party.PlayersArray[49]
vars.PlayerBackup = mem.string(pl, pl['?size'], true)

And restored like this:
local pl = Party.PlayersArray[49]
mem.copy(pl, vars.PlayerBackup)

[edit] Looks like CODE tag is broken
My patches: MM6 MM7 MM8. MMExtension. Tools. Also, I love Knytt Stories and Knytt Underground. I'm also known as sergroj.

StormyG7
Leprechaun
Leprechaun
Posts: 24
Joined: 18 Nov 2016

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby StormyG7 » Nov 4 2021, 19:15

GrayFace wrote: Nov 4 2021, 0:46
StormyG7 wrote: Oct 22 2021, 21:20 I'm trying to create a way to export/import a party member to get around the 50 player limit by making MMExtension lua scripts in MM8 (specifically the World of Enroth MM 6,7,8 merge; community master branch).
The problem is lloyd beacon images and also any MMExt scripts that may have some info stored for each player based on index. Code patching would be required to deal with lloyd beacons.
The player structure can be backed up like this:
local pl = Party.PlayersArray[49]
vars.PlayerBackup = mem.string(pl, pl['?size'], true)

And restored like this:
local pl = Party.PlayersArray[49]
mem.copy(pl, vars.PlayerBackup)

[edit] Looks like CODE tag is broken
I finished making the script and posted a Google Drive Link in the MM 6 7 8 Merge thread. I would like to see if the Adventurer's Inn could be changed to use my script to view exported party members that I have stored in text files and have the dismiss button also trigger my script to export a player.

I haven't encountered any problems with Lloyd's Beacon; probably because so far, only the main character has Lloyd's Beacon. For my own personal use, if the picture for the location doesn't work, I'm not too concerned. I would imagine that one could create a new script to handle the creation of Lloyd's Beacon Images instead of whatever already exists. Then, all that my export/import functions would need is to throw the images into a file system sorted by each exported player full of .bmp or .tga images.

You mention info stored based on the player index, but I have played for over 40 hours swapping characters in and out of text files with no major issues; the only minor issue is that it seems like the fountains and shrines that have onetime bonuses do not work on repeat encounters after swapping out a character using my script. I think this issue should persist with NPCMercenaries script since new mercenaries are using the same player roster index, but I haven't tested enough to see how the game remembers who's visited what. I'm not sure how to get around that, but for now, I just manually track who's visited shrines so I can add the bonus using the MMExt console.

User avatar
GrayFace
Round Table Hero
Round Table Hero
Posts: 1562
Joined: 29 Nov 2005

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby GrayFace » Nov 8 2021, 4:09

StormyG7 wrote: Nov 4 2021, 19:15the only minor issue is that it seems like the fountains and shrines that have onetime bonuses do not work on repeat encounters after swapping out a character using my script. I think this issue should persist with NPCMercenaries script since new mercenaries are using the same player roster index, but I haven't tested enough to see how the game remembers who's visited what. I'm not sure how to get around that, but for now, I just manually track who's visited shrines so I can add the bonus using the MMExt console.
That's because MMExt doesn't expose every aspect of a character and you probably don't save everything that in exposes. I gave you the code to use, you just need to save that string into a binary file instead of vars.PlayerBackup. Why don't you just save them into the save file though?
My patches: MM6 MM7 MM8. MMExtension. Tools. Also, I love Knytt Stories and Knytt Underground. I'm also known as sergroj.

StormyG7
Leprechaun
Leprechaun
Posts: 24
Joined: 18 Nov 2016

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby StormyG7 » Nov 8 2021, 14:55

GrayFace wrote: Nov 8 2021, 4:09 That's because MMExt doesn't expose every aspect of a character and you probably don't save everything that in exposes. I gave you the code to use, you just need to save that string into a binary file instead of vars.PlayerBackup. Why don't you just save them into the save file though?
This is all fairly new to me and right now the script I'm using is working fairly well. Now that you've told me MMExt doesn't give me all the character's data, I'm more interested in creating something that does, by implementing those four lines of code you posted. The problem is, I don't think I know how to create a binary file using lua, and I'm unsure why a binary is preferable to a script. About the saving characters into the save file, I am interested, but I like being able to open a character file in text format and change things easily. I don't know how to save them into the save file or how the save file is structured or even accessed.

I'm on a playthrough that might be my last for a long time before I move on to something else. Since you made MMExt, I'm just curious, besides the fountains, do you know what else is left out? Is it anything that matters? As I said, so far my current playthrough is working out great with this script. I import and export characters regularly and; but I would hate for it to be ruined and to have to start all over because I missed something important that won't get exported. It sure seems like I got everything that matters besides the fountains. I know I don't have the honorary promotion awards, because World of Enroth merge stores them in a way that I didn't take time to familiarize myself with and it didn't matter to me for my personal use.

EDIT I was wrong about the fountains and shrines. Somehow it must be in player bits or something I exported, but the game remembers who's visited what fountain from character to character using my export/import script.

Eksekk
Peasant
Peasant
Posts: 94
Joined: 19 Jul 2016

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby Eksekk » Nov 10 2021, 9:34

StormyG7 wrote: Nov 8 2021, 14:55 EDIT I was wrong about the fountains and shrines. Somehow it must be in player bits or something I exported, but the game remembers who's visited what fountain from character to character using my export/import script.
Yes, it's stored in player bits. You can check if a bit is set with evt.Cmp("PlayerBits", <number>) (will return true if set) and set a bit with evt.Set("PlayerBits", <number>). Idk how they exactly work though.

cthscr
Demon
Demon
Posts: 349
Joined: 12 Jan 2020

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby cthscr » Nov 10 2021, 17:44

Bits are packed into bytes, nothing surprising. So 512 player bits are stored as 64 bytes. There are procedures in mm8.exe that check/set specific bit of specific byte based on given number and offset of the field. Note in MMExtension 2.2 you have to subtract 1 from player bit to get its index in array ('Party[0].PlayerBits[3]' is 'evt.Cmp("PlayerBits", 4)').

User avatar
GrayFace
Round Table Hero
Round Table Hero
Posts: 1562
Joined: 29 Nov 2005

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby GrayFace » Nov 20 2021, 9:29

StormyG7 wrote: Nov 8 2021, 14:55The problem is, I don't think I know how to create a binary file using lua, and I'm unsure why a binary is preferable to a script.
Like this: io.SaveString([[c:\file.dat]], s), where s is that binary string: s = mem.string(pl, pl['?size'], true)
Then loading: s = io.LoadString([[c:\file.dat]])
StormyG7 wrote: Nov 8 2021, 14:55About the saving characters into the save file, I am interested, but I like being able to open a character file in text format and change things easily. I don't know how to save them into the save file or how the save file is structured or even accessed.
"vars" table is saved in save file, so you'd need to create a subtable like vars.SavedPlayers and organize players in it however you want.
StormyG7 wrote: Nov 8 2021, 14:55Since you made MMExt, I'm just curious, besides the fountains, do you know what else is left out?
No, I don't. Can't tell anything off the back of my head.
StormyG7 wrote: Nov 8 2021, 14:55EDIT I was wrong about the fountains and shrines. Somehow it must be in player bits or something I exported, but the game remembers who's visited what fountain from character to character using my export/import script.
They too are probably stored outside the player structure for MM6 and MM7. There might be a shortage of PlayerBits to fit all 3 games.
My patches: MM6 MM7 MM8. MMExtension. Tools. Also, I love Knytt Stories and Knytt Underground. I'm also known as sergroj.

cthscr
Demon
Demon
Posts: 349
Joined: 12 Jan 2020

Re: MMExtension v2.2 + MMEditor v2.1 Level Editor [June 4, 2019]

Postby cthscr » Nov 22 2021, 6:17

GrayFace wrote: Nov 20 2021, 9:29
StormyG7 wrote: Nov 8 2021, 14:55The problem is, I don't think I know how to create a binary file using lua, and I'm unsure why a binary is preferable to a script.
Like this: io.SaveString([[c:\file.dat]], s), where s is that binary string: s = mem.string(pl, pl['?size'], true)
Then loading: s = io.LoadString([[c:\file.dat]])
Won't you please add some compressor (LZMA maybe)?
GrayFace wrote: Nov 20 2021, 9:29
StormyG7 wrote: Nov 8 2021, 14:55EDIT I was wrong about the fountains and shrines. Somehow it must be in player bits or something I exported, but the game remembers who's visited what fountain from character to character using my export/import script.
They too are probably stored outside the player structure for MM6 and MM7. There might be a shortage of PlayerBits to fit all 3 games.
All three games combined use less than 100 PlayerBits. 512 is much more than that. But one can't speak about fountains in general - not every one is stored into PlayerBits.


Return to “Might and Magic”

Who is online

Users browsing this forum: No registered users and 14 guests