Saving the party for later (craziest idea ever)

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Saving the party for later (craziest idea ever)

Post by Xanathar »

Hi guys :)
I had this problem: I wanted to save the stats of my party (sadly the exact exp can't be restored, but the rest at least) so that I can recreate them in code in the editor.

Which basically means, use the grimrock character creator to prototype them, and then generate the code which recreates them at runtime.

This however requires writing a file from lua. Impossible ? I thought so, but then I realized that we are able to write something to a specific file from lua scripts.... :twisted:

Paste this code into a scripting entity called "dumper" in a new dungeon (let's use a new dungeon for now ;) ):

Code: Select all

g_CrashWith = nil

function append(m, v)
	return m .. "_" .. v;
end

function dumpChampion(idx)
	local C = party:getChampion(idx)

	local skills = { "air_magic", "armors", "assassination", "athletics", "axes", "daggers", "dodge", "earth_magic", "fire_magic", "ice_magic", "maces", "missile_weapons", "spellcraft", "staves", "swords", "throwing_weapons", "unarmed_combat" }
	
	local stats = { "health", "energy", "strength", "dexterity", "vitality", "willpower", "protection", "evasion", "resist_fire", "resist_cold", "resist_poison", "resist_shock" }
	
	local traits = { "aggressive", "agile", "athletic", "aura", "cold_resistant", "evasive", "fire_resistant", "fist_fighter", "head_hunter", "healthy", "lightning_speed", "natural_armor", "poison_resistant", "skilled", "strong_mind", "tough" }
	
	local m = "Y";
	m = append(m, C:getLevel());
	m = append(m, C:getRace());
	m = append(m, C:getClass());
	m = append(m, C:getSex());
	m = append(m, C:getSkillPoints());
	
	for _, s in ipairs(skills) do
		m = append(m, C:getSkillLevel(s));
	end
	
	for _, s in ipairs(stats) do
		m = append(m, C:getStatMax(s));
	end
	
	local tr = "";
	for _, s in ipairs(traits) do
		if (C:hasTrait(s)) then
			tr = tr .. "1";
		else
			tr = tr.."0";
		end
	end
	m = append(m, tr);
	return m
end

function dumpParty()
	local str = dumpChampion(1) 
	str = append(str, dumpChampion(2))
	str = append(str, dumpChampion(3))
	str = append(str, dumpChampion(4))

	return str
end

function test()
	print(dumpParty())
end

function dump()
	g_CrashWith = dumpParty()
end

function onDrawGui(g)
	if (g_CrashWith ~= nil) then
		g.drawImage("mod_assets/dump/" .. g_CrashWith .. ".dump", 0, 0)
	end
end

Then, add this to init.lua:

Code: Select all

cloneObject{
	name='party',
	baseObject='party',
	onDrawGui = function(g)
	   dumper.onDrawGui(g)
	end, 
}
When you are ready, type into the console "dumper.dump()"

The game will crash. Now, open the error.log file :twisted: and it will say something like:

Code: Select all

[string "Gui.lua"]:0: Could not find asset processor for file: mod_assets/dump/X_Human_Fighter_male_0_0_2_0_2_0_0_0_0_0_0_0_0_0_0_3_0_0_65_52_16_14_12_11_1_0_0_0_0_0_0010000000000100_X_Minotaur_Fighter_male_0_0_0_0_0_0_0_0_0_0_0_1_0_0_0_0_0_0_92_45_20_7_17_8_0_0_0_0_0_0_0000000010000001_X_Human_Rogue_female_0_0_0_0_0_0_2_1_0_0_0_0_1_0_0_0_3_0_52_55_11_16_13_12_0_0_0_0_0_0_0100000000000100_X_Human_Mage_male_0_0_0_0_0_0_0_0_0_2_0_0_0_2_0_0_0_0_42_70_10_12_13_18_0_0_25_0_0_0_0000001000000010.dump ....
Copy the file name (and only the filename, no extension, no path) and paste it into this web page: http://www.mastropaolo.com/resurrect.html . Click "process" and you have the code to recreate the party 8-)

Let me know if you have any problems - probably nobody needs this specific thing, but I thought it was funny and maybe a reusable technique for other problems :)

I'll go to hell for this.
Last edited by Xanathar on Wed Apr 10, 2013 10:12 pm, edited 1 time in total.
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: Saving the party for later (craziest idea ever)

Post by Xanathar »

Just a last second addition: remove dumper from the dungeon before shipping, as it would bea possible security hole!
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
User avatar
Dr.Disaster
Posts: 2876
Joined: Wed Aug 15, 2012 11:48 am

Re: Saving the party for later (craziest idea ever)

Post by Dr.Disaster »

Xanathar wrote:I had this problem: I wanted to save the stats of my party (sadly the level and exp can't be restored, but the rest at least) so that I can recreate them in code in the editor.
It may not be possible to keep the exact amount of exp but with the functions Champion:getLevel(), Champion:gainExp(amount) and the help of an exp->lvl table is should be at least possible to recreate their level.
User avatar
msyblade
Posts: 792
Joined: Fri Oct 12, 2012 4:40 am
Location: New Mexico, USA
Contact:

Re: Saving the party for later (craziest idea ever)

Post by msyblade »

This is a bit above my pay grade, but if I am understanding correctly: Diarmuid developed a way to store many character variables, in the first characters name string. He is able to import skills, exp, and even inventory. There is some work to be done before it is usable, but the concept is sound. I truly cannot impart any more information on it, as I actually dont understand.
Currently conspiring with many modders on the "Legends of the Northern Realms"project.

"You have been captured by a psychopathic diety who needs a new plaything to torture."
Hotel Hades
User avatar
thomson
Posts: 337
Joined: Thu Sep 13, 2012 9:55 pm
Location: R'lyeh
Contact:

Re: Saving the party for later (craziest idea ever)

Post by thomson »

Xanathar wrote:(sadly the level and exp can't be restored, but the rest at least)
There is a way. champion:setClass() has a bug and you can exploit it. When you change a class of your champion, it has funny side effects. In particular, it sets level to 1 and experience to 0. With a bit of code, you can use it to level down.

I think gw_party from grimwidgets uses this.

p.s.
This bug exists in 1.3.7. Please don't tell Petri or Antti, as they may fix it :)
[MOD] Eye of the Beholder: Waterdeep sewers forum sources; Grimtools (LoG1 -> LoG2 converter) sources
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: Saving the party for later (craziest idea ever)

Post by Xanathar »

Champion:getLevel()
I don't know exactly why but today I checked 326532856837 times and always missed champion:getLevel(), even when looking specifically for it! :D
Diarmuid developed a way to store many character variables, in the first characters name string. He is able to import skills, exp, and even inventory.
I guess that would require to import the party using the import dialog.. this thing is to work in the editor and help creating the code needed to create an NPC or to help development/testing in the editor.
This bug exists in 1.3.7. Please don't tell Petri or Antti, as they may fix it
I hope not, as my dungeon relies on that "bug" to set the level of NPCs :)

Thanks to all of you :)
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
User avatar
Komag
Posts: 3659
Joined: Sat Jul 28, 2012 4:55 pm
Location: Boston, USA

Re: Saving the party for later (craziest idea ever)

Post by Komag »

masterful work, I applaud! :D 8-)
Finished Dungeons - complete mods to play
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: Saving the party for later (craziest idea ever)

Post by Diarmuid »

Ok, I think it's time I enter the arena indeed with some super-tech mechanic that I was keeping since January for LoTNR, but which might never get used since the scope is being reduced for LoG1. So...

There is one other obvious place where you can save info: the save game!

But how to pass information from a save game into another brand new dungeon? Well, character name enables you indeed to save a string, so you can serialize/deserialize everything you want, including entire dungeon states.

At the following link : https://docs.google.com/file/d/0B-rVman ... sp=sharing you can download two test dat files, called IO Dungeon 1 and IO Dungeon 2.

Start in IO dungeon 1, and see if you can find the goromog statuette in IO dungeon 2!

To transfer between dungeons, you must go back to the exit and receive the "save data exported" message.
Then: - Save your game
- Return to main menu
- Custom Dungeon -> the other dungon
- Choose "import" and select the save you just made.

Everything in the dungeon should be as you left it, doors, items, monsters, puzzles.... The whole thing should take about 10 minutes, so give it a go. 8-)

Sources for both go here: https://docs.google.com/file/d/0B-rVman ... sp=sharing.

I also realized this could be used to save games that can be reimported after a dungeon .dat has been updated, preserving progress.

And the io.lua script entity, for the curious who can't wait to download the source (with some Xanathar code inside):
SpoilerShow

Code: Select all

-- ================================================================================
--                                SAVE IO SCRIPT
-- ================================================================================

-- Main Data Object
data = {}


-- ===================================================================================================
--                                GLOBAL SAVE/LOAD FUNCTIONS
-- ===================================================================================================

function onSavePoint()
	hudPrint("You have reached an exit zone. LotNR game data is now stored; if you")
	hudPrint("wish, you can save your game and import the save in another dungeon.")
	save()
	data.system.savePoint = true
end

function offSavePoint()
	data.system.savePoint = false
end

function autoexec()
	data[dungeon.id] = {}
	data.system = {}
	data[dungeon.id].mod = {}
	load()
	data.system.savePoint = false
	save()
end

function load()
	local sData = importData()
	if sData then
		data = {}
		data = sData
		loadDungeonState()
		hudPrint("LotNR game data loaded from dungeon "..data.system.origin.name)
	end
end

function save()
	saveDungeonState()
	exportData()
end

function saveDungeonState()
	savePartyData()
	saveSystemData()
	if not(data[dungeon.id]) then data[dungeon.id] = {} end
	saveItemsData()
	saveObjectsData()
	saveMonstersData()
end

function loadDungeonState()
	if data[dungeon.id] then
		if data[dungeon.id].items then
			loadItemsData()
		end
		loadMonstersData()
		loadObjectsData()
	end
	if data.party then
		loadPartyData()
	end
end

-- ===================================================================================================
--                                DUNGEON IMPORT/EXPORT FUNCTIONS
-- ===================================================================================================

function savePartyData()
	data.party = {}
	-- Save inventory items
	if not(data.party.items) then data.party.items = {} end
	for i = 1,4 do
		data.party.items[i] = {}
		for j = 1, 31 do
			local item = party:getChampion(i):getItem(j)
			if item then
				data.party.items[i][j] = saveItem(item)
			else
				data.party.items[i][j] = "nil"
			end
		end
	end
	-- Save mouse item
	local item = getMouseItem()
	if item then
		data.party.mouse = saveItem(item)
	else
		data.party.mouse = "nil"
	end
end

function loadPartyData()
	-- Load inventory items
	local items = data.party.items
	for i, v in ipairs(items) do
		for slot, item in pairs(v) do
			if item ~= "nil" then
				if checkItemName(item.n) then
					party:getChampion(i):insertItem(slot, loadItem(item))
				else
					print("Warning, "..item.n.." is not defined in this Dungeon")
				end
			else
				party:getChampion(i):removeItem(slot)
			end
		end
	end
	-- Load mouse item
	local item = data.party.mouse 
	if item ~= "nil" then
		if checkItemName(item.n) then
			setMouseItem(loadItem(item))		
		else
			print("Warning, "..item.n.." is not defined in this Dungeon")
		end
	else
		setMouseItem(nil)
	end
end

function saveSystemData()
	if not(data.system) then data.system = {} end
	local system = data.system
	system.origin = {}
	system.origin.id = dungeon.id
	system.origin.name = dungeon.name
	system.origin.version = dungeon.version
	system.origin.saveLevel = party.level
	system.origin.saveX = party.x
	system.origin.saveY = party.y
	system.origin.saveFacing = party.facing
end

function saveItemsData()
	-- Save Torchholders Data
	data[dungeon.id].torchHolders = {}
	for i in grimq.fromAllEntitiesInWorld():where("class", "TorchHolder"):toIterator() do
		if i:hasTorch() then
			for j in entitiesAt(i.level, i.x, i.y) do
				if j.name == "torch" then
					if not(data[dungeon.id].torchHolders[i.id]) then
						data[dungeon.id].torchHolders[i.id] = {j.id, j:getFuel()}
					end
				end
			end
		end
	end
	-- Save Alcoves & Altars data
	data[dungeon.id].alcoves = {}
	for i in grimq.fromAllEntitiesInWorld():toIterator() do
		if i.class == "Alcove" or i.class == "Altar" then
			if i:getItemCount() > 0 then
				data[dungeon.id].alcoves[i.id] = {}
				for j in i:containedItems() do
					table.insert(data[dungeon.id].alcoves[i.id], saveItem(j))
				end
			end
		end
	end
	-- Save Items Data
	data[dungeon.id].items = {}
	local items = data[dungeon.id].items
	for i in grimq.fromAllEntitiesInWorld():where("class", "Item"):toIterator() do
		local contained = false
		for _, v in pairs(data[dungeon.id].torchHolders) do
			if v[1] == i.id then
				contained = true
			end
		end
		for _, alcoveItems in pairs(data[dungeon.id].alcoves) do
			for _, w in ipairs(alcoveItems) do
				if w.i == i.id then
					contained = true
				end
			end
		end
		if not(contained) then
			table.insert(items, {
				i = saveItem(i),
				l = i.level, 
				x = i.x,
				y = i.y,
				f = i.facing
				}
			)
		end
	end
end

function loadItemsData()
	-- Destroy existing items
	for i in grimq.fromAllEntitiesInWorld():where("class", "Item"):toIterator() do
		i:destroy()
	end
	-- Load Torches Data
	local torches = data[dungeon.id].torchHolders
	for id, t in pairs(torches) do
		findEntity(id):addItem(spawn("torch"):setFuel(t[2]))
	end	
	-- Load Alcoves Data
	local alcoves = data[dungeon.id].alcoves
	for id, alcove in pairs(alcoves) do
		for _, item in ipairs(alcove) do
			if checkItemName(item.n) then
				findEntity(id):addItem(loadItem(item))
			else
				print("Warning, "..item.n.." is not defined in this Dungeon")
			end
			
		end
	end	
	-- Load Items Data
	local items = data[dungeon.id].items
	for _, item in ipairs(items) do
		if checkItemName(item.n) then
			loadItem(item.i, item.l, item.x, item.y, item.f)
		else
			print("Warning, "..item.n.." is not defined in this Dungeon")
		end
		
	end
end

function saveObjectsData()
	-- Save Levers Data
	data[dungeon.id].levers = {}
	for i in grimq.fromAllEntitiesInWorld():where("class", "Lever"):toIterator() do
		local state
		if i:getLeverState() == "activated" then
			state = "a"
		else
			state = "d"
		end
		data[dungeon.id].levers[i.id] = state
	end
	-- Save Teleporters Data
	data[dungeon.id].tel = {}
	for i in grimq.fromAllEntitiesInWorld():where("class", "Teleporter"):toIterator() do
		local state
		if i:isActivated() then
			state = "a"
		else
			state = "d"
		end
		data[dungeon.id].tel[i.id] = state
	end
	-- Save Timers Data
	data[dungeon.id].timers = {}
	for i in grimq.fromAllEntitiesInWorld():where("class", "Timer"):toIterator() do
		local state
		if i:isActivated() then
			state = "a"
		else
			state = "d"
		end
		data[dungeon.id].timers[i.id] = state
	end
	-- Save Counters Data
	data[dungeon.id].counters = {}
	for i in grimq.fromAllEntitiesInWorld():where("class", "Counter"):toIterator() do	
		data[dungeon.id].counters[i.id] = i:getValue()
	end
	-- Save WallText Data
	data[dungeon.id].wTexts = {}
	for i in grimq.fromAllEntitiesInWorld():where("class", "WallText"):toIterator() do	
		data[dungeon.id].wTexts[i.id] = i:getWallText()
	end
	-- Save Doors Data
	data[dungeon.id].doors = {}
	for i in grimq.fromAllEntitiesInWorld():where(grimq.isDoor):toIterator() do
		local state
		if i:isOpen() then
			state = "o"
		else
			state = "c"
		end
		data[dungeon.id].doors[i.id] = state
	end
	-- Save Pits Data
	data[dungeon.id].pits = {}
	for i in grimq.fromAllEntitiesInWorld():where("class","Pit"):toIterator() do
		local state
		if i:isOpen() then
			state = "o"
		else
			state = "c"
		end
		data[dungeon.id].pits[i.id] = state
	end
end

function loadObjectsData()
	-- Load Levers Data
	if data[dungeon.id].levers then
		for id, state in pairs(data[dungeon.id].levers) do
			if findEntity(id) then
				if state == "a" then
					findEntity(id):setLeverState("activated")
				else
					findEntity(id):setLeverState("deactivated")
				end
			end
		end
	end
	-- Load Teleporters Data
	if data[dungeon.id].tel then
		for id, state in pairs(data[dungeon.id].tel) do
			if findEntity(id) then
				if state == "a" then
					findEntity(id):activate()
				else
					findEntity(id):deactivate()
				end
			end
		end
	end
	-- Load Timers Data
	if data[dungeon.id].timers then
		for id, state in pairs(data[dungeon.id].timers) do
			if findEntity(id) then
				if state == "a" then
					findEntity(id):activate()
				else
					findEntity(id):deactivate()
				end
			end
		end	
	end
	-- Load Counters Data
	if data[dungeon.id].counters then
		for id, value in pairs(data[dungeon.id].counters) do
			if findEntity(id) then
				findEntity(id):setValue(value)
			end
		end	
	end
	-- Load Wall Texts Data
	if data[dungeon.id].wTexts then
		for id, value in pairs(data[dungeon.id].wTexts) do
			if findEntity(id) then
				findEntity(id):setWallText(value)
			end
		end
	end
	-- Load Doors Data
	if data[dungeon.id].doors then
		for id, state in pairs(data[dungeon.id].doors) do
			if findEntity(id) then
				if state == "o" then
					findEntity(id):setDoorState("open")
				else
					findEntity(id):setDoorState("closed")
				end
			end
		end
	end
	-- Load Pits Data
	if data[dungeon.id].pits then
		for id, state in pairs(data[dungeon.id].pits) do
			if findEntity(id) then
				if state == "o" then
					findEntity(id):setPitState("open")
				else
					findEntity(id):setPitState("closed")
				end
			end
		end
	end
end

function saveMonstersData()
	data[dungeon.id].monsters = {}
	local monsters = data[dungeon.id].monsters
	for i in grimq.fromAllEntitiesInWorld():where("class", "Monster"):toIterator() do
		table.insert(monsters, {
			i = i.id,
			n = i.name,
			l = i.level, 
			x = i.x,
			y = i.y,
			f = i.facing,
			v = i:getLevel(),
			h = i:getHealth(),
			}
		)
	end
end

function loadMonstersData()
	-- Destroy existing monsters
	for i in grimq.fromAllEntitiesInWorld():where("class", "Monster"):toIterator() do
		i:destroy()
	end
	-- Load monsters data
	local monsters = data[dungeon.id].monsters
	for _, i in ipairs(monsters) do
		spawn(i.n, i.l, i.x, i.y, i.f, i.id):setLevel(i.v):setHealth(i.h)
	end
end

-- Tracking functions
-- =====================

function initSecrets()
	--ToDO
end

function trackSecrets()
	--ToDO
end

-- Table serialization code
-- ========================

tags = {
	data = {"<data>","</data>"},
	var = {"<@","@>"},
	table = "@t",
	array = "@a",
}

function exportData()
	local save = party:getChampion(1):getName()
	save = string.sub(save, 0, string.find(save, "     "))
	save = save..voidLines..tags.data[1]
	for i, v in pairs(data) do
		save = save..serialize(i, v)
	end
	save = save..tags.data[2]
	print("Data lenght:",#save)
	party:getChampion(1):setName(save)
end

function importData()
	local sData = {}
	local load = party:getChampion(1):getName()
	if not(string.find(load, tags.data[1])) then
		return false
	end
	load = splitTags(load, tags.data[1], tags.data[2])
	load = splitTags(load[1], tags.var[1],tags.var[2])
	for i, v in ipairs(load) do
		local var, value = unserialize(v)
		sData[var] = value
	end
	return sData
end

function serialize(var, value)
	local resultString = ""
	if type(value) == "table" then
		resultString = resultString..tags.var[1]..var.."="
		local isArray = false
		for i, v in ipairs(value) do
			if i then
				isArray = true
			end
		end
		if isArray then
			resultString = resultString..tags.array
			for i, v in pairs(value) do
				resultString = resultString..serialize(i, v)
			end
		else
			resultString = resultString..tags.table
			for i, v in pairs(value) do
				resultString = resultString..serialize(i, v)
			end
		end
		resultString = resultString..tags.var[2]
	else
		resultString = resultString..tags.var[1]..tostring(var).."="..tostring(value)..tags.var[2]
	end
	return resultString
end

function unserialize(str)
	local var, value
	local equal = string.find(str, "=")
	var = string.sub(str, 0, equal-1)
	value = string.sub(str, equal+1)
	if grimq.strstarts(value, tags.table) then
		local tableValue = {}
		local tableData = string.sub(value, #tags.table+1)
		local tableData = splitTags(tableData, tags.var[1], tags.var[2])
		for i, v in ipairs(tableData) do
			local tVar, tValue = unserialize(v)
			if tonumber(tValue) then
				tValue = tonumber(tValue)
			end
			if tValue ~= "nil" then
				--print(tVar, tValue)
			end
			tableValue[tVar] = tValue
		end
		return var, tableValue
	elseif grimq.strstarts(value, tags.array) then
		local tableValue = {}
		local tableData = string.sub(value, #tags.array+1)
		local tableData = splitTags(tableData, tags.var[1], tags.var[2])
		for i, v in ipairs(tableData) do
			local tVar, tValue = unserialize(v)
			if tonumber(tValue) then
				tValue = tonumber(tValue)
			end
			if tValue ~= "nil" then
				--print(tVar, tValue)
			end
			table.insert(tableValue, tValue)
		end
		return var, tableValue
	else
		if tonumber(value) then
			value = tonumber(value)
		end
		return var, value
	end	
end

function splitTags(str, startTag, endTag)
	local t = {}
	local currentPos, startPos, endPos, nest = 0, 0, 0, 0
	local sTagStart, sTagEnd, eTagStart, eTagEnd
	local ctr, ctr2 = 0, 0
	while string.find(str, startTag, endPos) do
		sTagStart, sTagEnd = string.find(str, startTag, currentPos)
		eTagStart, eTagEnd = string.find(str, endTag, currentPos)
		if sTagStart < eTagStart then
			nest = nest + 1
			startPos = sTagEnd + 1
			currentPos = sTagEnd + 1
			endPos = eTagStart - 1
		else
			return ""
		end
		while nest > 0 do
			sTagStart, sTagEnd = string.find(str, startTag, currentPos)
			eTagStart, eTagEnd = string.find(str, endTag, currentPos)
			if sTagStart and sTagStart < eTagStart then
				nest = nest + 1
				currentPos = sTagEnd + 1 
			else
				nest = nest - 1
				currentPos = eTagEnd + 1
				if string.sub(str, currentPos, currentPos + #startTag) == startTag then
					currentPos = eTagEnd + 1 + #startTag
				end
				endPos = eTagStart - 1
			end
		end
		table.insert(t, string.sub(str, startPos, endPos))
	end
	return t
end
	
voidLines = [[      








































]]

-- ===================================================================================================
--                               ITEM FUNCTIONS (Based on Xanathar's Code)
-- ===================================================================================================

function saveItem(item)
   local itemTable = { }
   itemTable.i = item.id
   itemTable.n = item.name
   itemTable.s = item:getStackSize()
   itemTable.f = item:getFuel()
   itemTable.c = item:getCharges()
   itemTable.t = item:getScrollText()
   
   local idx = 0
   for subItem in item:containedItems() do
	  if (idx == 0) then
		 itemTable.sI = { }
	  end
	  
	  itemTable.sI[idx] = saveItem(subItem)
	  idx = idx + 1
   end
   
   return itemTable
end

function loadItem(itemTable, level, x, y, facing, id)
	local itemToSpawn = nil
	id = id or itemTable.i
	
	id = checkItemId(id)
	
	if (level ~= nil) then
		itemToSpawn = spawn(itemTable.n, level, x, y, facing,id)
	else
		itemToSpawn = spawn(itemTable.n,nil,nil,nil,nil,id)
	end
	if itemTable.s > 0 then
		itemToSpawn:setStackSize(itemTable.s)
	end
	if itemTable.c > 0 then
		itemToSpawn:setCharges(itemTable.c)
	end            
   
	if itemTable.t ~= nil then
		itemToSpawn:setScrollText(itemTable.t)
	end
	
	if type(itemTable.f) ~= "string" then
		itemToSpawn:setFuel(itemTable.f)
	end

	if (itemTable.sI ~= nil) then
		for _, subTable in pairs(itemTable.sI) do
			local subItem = loadItem(subTable)
			itemToSpawn:addItem(subItem, false)
		end
	end
   
	return itemToSpawn
end

function checkItemId(id)
	if findEntity(id) then
		return nil
	end
	for i = 1,4 do
		for j = 1, 31 do
			if party:getChampion(i):getItem(j).id == id then
				return nil
			end
		end
	end
	if getMouseItem().id == id then
		return nil
	end
	if string.find(id, "^%d+$") then
		return nil
	end
	return id
end

function checkItemName(name)
	for _, items in pairs(io_items.ioItemList) do
		for _,item in ipairs(items) do
			if name == item then
				return true
			end
		end
	end
	return false
end

User avatar
msyblade
Posts: 792
Joined: Fri Oct 12, 2012 4:40 am
Location: New Mexico, USA
Contact:

Re: Saving the party for later (craziest idea ever)

Post by msyblade »

Edit- pointless rambling. (I KNOW! I'm as surprised as you are!)
Currently conspiring with many modders on the "Legends of the Northern Realms"project.

"You have been captured by a psychopathic diety who needs a new plaything to torture."
Hotel Hades
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: Saving the party for later (craziest idea ever)

Post by Xanathar »

Edit- pointless rambling. (I KNOW! I'm as surprised as you are!)
No why ? The idea of talking on some faster media (you suggested skype, but also irc or others) would be good to progress further on frameworks etc. I just didn't have time to answer to your post :)
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
Post Reply