1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

DLL objects and saved games

Discussion in 'Civ5 - SDK / LUA' started by Drawmeus, Dec 26, 2012.

  1. Drawmeus

    Drawmeus Emperor

    Joined:
    Jul 25, 2007
    Messages:
    1,213
    I've created a new class in the DLL - "CvInvestments". An investment is an agreement between a CvPlayer and a CvMinorCivAI which performs certain logic (increasing your per-turn expenses, for example, and giving the Minor Civ bonuses each turn, and increasing your influence every turn). There's a small amount of data - 8 or 9 variables - which can be completely initialized from four pieces of data - PlayerTypes eMajorCiv, PlayerTypes eMinorCiv, int iSize, int iType. CvPlayer instances initialize Investments (they form when a CvPlayer decides to create one).

    I don't know a ton about this and I'm muddling my way through. I'm sure I can figure it out on my own... eventually, but if someone with some experience doing this can point me in the right direction, I would be grateful.

    I assume the Read() and Write() functions in CvPlayer are the functions that do the work of actually saving out and reading back in the data. Would it be reasonable to save out that CvPlayer's investments and reading them back in through the CvPlayer Read/Write functions? Will I need to define stuff for my CvInvestments class (Read/Write functions? This "namespace FSerialization {}" stuff I see in a lot of the files?)? Will I need to worry about overloading an operator (">>" and/or "<<")?

    Any help appreciated!
     
  2. Drawmeus

    Drawmeus Emperor

    Joined:
    Jul 25, 2007
    Messages:
    1,213
    Looking into it, it's NOT straightforward, but part of that is my use of Firaxis's FFreeListTrashArray class (? - object of some kind anyway).

    Next thing I'm going to try will be to create a temp array of arrays capturing the four variables needed to re-initialize the investments, and write functions to actually do the re-initialization, and then update the CvPlayer read/write functions to acquire that data and write it in a two-dimensional array, and read it in and reconstruct the investments.

    This doesn't seem like an ideal solution, but I don't really know how to write a better solution at the moment, so let's see if this works!



    UPDATE: I did it another way. I wrote write() and read() functions for my CvInvestments class, and in CvPlayer's write functions, I'm writing the number of investments that player has, followed by iterating down the list of investments and calling into the write function for each investment (and doing the opposite in the read functions, using that # of investments to set up a for loop). I've successfully saved and loaded data, but made some silly mistakes, testing now.
     
  3. Drawmeus

    Drawmeus Emperor

    Joined:
    Jul 25, 2007
    Messages:
    1,213
    Sorry for the triple post, but - IT WORKS!

    Still working out a few minor details (I forgot to have the load function call into the treasury and give it the gold per turn charge) but it's loading and it's working.
     
  4. Genghis.Khan

    Genghis.Khan Person

    Joined:
    Jun 9, 2012
    Messages:
    934
    Location:
    Somewhere
    Can you post thee code so I can at least understand how do you do those things? Investiments sound like a great idea maybe you shpuld create it for Major Civs as well (gaining friendship instead of influence)
     
  5. Drawmeus

    Drawmeus Emperor

    Joined:
    Jul 25, 2007
    Messages:
    1,213

    In CvPlayer::read(FDataStream& kStream)
    Spoiler :
    Code:
    	// STRABO - READ
    	int iNumInvestments;
    	kStream >> iNumInvestments;
    	for (int iInvestmentsRead = 0; iInvestmentsRead < iNumInvestments; iInvestmentsRead++)
    	{
    		CvInvestments* pInvestment = addInvestment();
    		pInvestment->read(kStream);
    	}
    


    In CvPlayer::Write(FDataStream& kStream):
    Spoiler :
    Code:
    	int iNumInvestments = getNumInvestments();
    	kStream << iNumInvestments;
    	writeInvestments(kStream);
    


    writeInvestments(kStream) just iterates over each active investment and calls the function CvInvestments::write(FDataStream& kStream) for each one.

    CvInvestments::read(FDataStream& kStream)
    Spoiler :
    Code:
    	uint uiVersion = 0;
    	kStream >> uiVersion;
    	kStream >> m_iID;
    	kStream >> m_eMajorCiv;
    	kStream >> m_eMinorCiv;
    	kStream >> m_iInvestmentType;
    	kStream >> m_iInvestmentSize;
    	kStream >> m_iGoldCost;
    	kStream >> m_iExpectedInfluenceChange;
    	kStream >> m_eEra;
    


    CvInvestments::write(FDataStream& kStream)
    Spoiler :
    Code:
    	uint uiVersion = 1;
    
    	kStream << uiVersion;
    	kStream << m_iID;
    	kStream << m_eMajorCiv;
    	kStream << m_eMinorCiv;
    	kStream << m_iInvestmentType;
    	kStream << m_iInvestmentSize;
    	kStream << m_iGoldCost;
    	kStream << m_iExpectedInfluenceChange;
    	kStream << m_eEra;
    


    The only tricky part was figuring out how to run my loop over my list of CvInvestments in CvPlayer from the context of a function declared with const - in the end (and with help from a senior-level programmer friend), that magic happens in CvPlayer::writeInvestments(FDataStream& kStream)

    The loop code:
    Spoiler :
    Code:
    CvInvestments* CvPlayer::firstInvestment(int* pIterIdx, bool bRev)
    {
    	return !bRev ? m_investments.BeginIter(pIterIdx) : m_investments.EndIter(pIterIdx);
    }
    
    //	--------------------------------------------------------------------------------
    const CvInvestments* CvPlayer::firstInvestment(int* pIterIdx, bool bRev) const
    {
    	return !bRev ? m_investments.BeginIter(pIterIdx) : m_investments.EndIter(pIterIdx);
    }
    
    //	--------------------------------------------------------------------------------
    CvInvestments* CvPlayer::nextInvestment(int* pIterIdx, bool bRev)
    {
    	return !bRev ? m_investments.NextIter(pIterIdx) : m_investments.PrevIter(pIterIdx);
    }
    
    //	--------------------------------------------------------------------------------
    const CvInvestments* CvPlayer::nextInvestment(int* pIterIdx, bool bRev) const
    {
    	return !bRev ? m_investments.NextIter(pIterIdx) : m_investments.PrevIter(pIterIdx);
    }
    
    //	--------------------------------------------------------------------------------
    int CvPlayer::getNumInvestments() const
    {
    	return m_investments.GetCount();
    }
    
    
    //	--------------------------------------------------------------------------------
    CvInvestments* CvPlayer::getInvestment(int iID)
    {
    	return(m_investments.GetAt(iID));
    }
    
    //	--------------------------------------------------------------------------------
    const CvInvestments* CvPlayer::getInvestment(int iID) const
    {
    	return(m_investments.GetAt(iID));
    }
    
    
    //	--------------------------------------------------------------------------------
    CvInvestments* CvPlayer::addInvestment()
    {
    	return(m_investments.Add());
    }
    
    //	--------------------------------------------------------------------------------
    void CvPlayer::deleteInvestment(int iID)
    {
    	getInvestment(iID)->updateCost(0);
    	m_investments.RemoveAt(iID);
    }
    


    Example usage:
    Spoiler :
    Code:
    void CvPlayer::doInvestmentTurns()
    {
    	int iLoop;
    	for(CvInvestments* pInvestment = firstInvestment(&iLoop); NULL != pInvestment; pInvestment = nextInvestment(&iLoop))
    	{
    		pInvestment->DoTurn();
    	}
    }
    
    Basically, create a CvInvestments* variable, set it to the first item in the loop, call that item's DoTurn() function, then find the next one, if that's not null, call it's DoTurn(), and so on until it IS null.


    Finally, the CvPlayer::writeInvestments(FDataStream& kStream) code:
    Spoiler :
    Code:
    const void CvPlayer::writeInvestments(FDataStream& kStream) const
    {
    	int iLoop;
    	const CvInvestments* pFirstInvestment = firstInvestment(&iLoop);
    	if (pFirstInvestment != NULL)
    	{
    		pFirstInvestment->write(kStream);
    	}
    	else
    	{
    		return;
    	}
    	for (; ; )
    	{
    		const CvInvestments* pInvestment = nextInvestment(&iLoop);
    		if (pInvestment == NULL)
    		{
    			return;
    		}
    		pInvestment->write(kStream);
    	}
    }
    
    I had to do it that way because the write function is declared const, which means it can't call non-const functions, and my pInvestment pointer needs to be const, which means it can't change - even to iterate through the for loop. This code is less efficient, but I'm not sure that I have another option available (you could get it working with const_cast - but don't use const_cast).
     
  6. Drawmeus

    Drawmeus Emperor

    Joined:
    Jul 25, 2007
    Messages:
    1,213
    Oh yeah! You'll have noticed that the variables are in the same order in the read() and write() functions - that is an absolute requirement. If they're not in the same order, saving and loading your game will corrupt it.

    I also had to update read() and write() for a number of other classes, but it's more of the same - just reading and writing one or two more variables.

    I'm sure a real programmer would find plenty of things to critique (and incidentally, real programmers, if you see things to critique, point them out, I want to get better at this), but these functions do work as written.
     

Share This Page