Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Black Market sell UI is laggy #1397

Open
faanlez opened this issue Oct 11, 2024 · 5 comments
Open

Black Market sell UI is laggy #1397

faanlez opened this issue Oct 11, 2024 · 5 comments

Comments

@faanlez
Copy link

faanlez commented Oct 11, 2024

UIBlackMarket_Sell has multiple functions that cause it to fetch GameStates from History too often. This is the main cause of it getting laggy with high number of items.

The main culprits are these two functions but there are many other improvements possible as well:

function int SortByInterest(BlackMarketItemPrice BuyPriceA, BlackMarketItemPrice BuyPriceB)
{
	local XComGameState_BlackMarket BlackMarketState;
	local XComGameState_Item ItemA, ItemB;

	History = `XCOMHISTORY;
	BlackMarketState = XComGameState_BlackMarket(History.GetGameStateForObjectID(BlackMarketReference.ObjectID));
	ItemA = XComGameState_Item(History.GetGameStateForObjectID(BuyPriceA.ItemRef.ObjectID));
	ItemB = XComGameState_Item(History.GetGameStateForObjectID(BuyPriceB.ItemRef.ObjectID));

	if(BlackMarketState.InterestTemplates.Find(ItemB.GetMyTemplateName()) != INDEX_NONE &&
	   BlackMarketState.InterestTemplates.Find(ItemA.GetMyTemplateName()) == INDEX_NONE)
	{
		return -1;
	}
	
	return 0;
}

SortByInterest() is used inside PopulateData() as the sorting function to sort all sellable items. Due to the array being sorted only containing object references it has to fetch the item states from history.

simulated function bool IsInterested(X2ItemTemplate ItemTemplate)
{
	local array<XComGameState_Item> Interests;
	local int i;

	Interests = class'UIUtilities_Strategy'.static.GetBlackMarket().GetInterests();

	for( i = 0; i < Interests.Length; i++ )
	{
		if( Interests[i].GetMyTemplate() == ItemTemplate )
			return true;
	}
	return false;
}

IsInterested() gets called from PopulateItemCard() every time selection changes (which also triggers when you click the +/- buttons) and gets the list of interests again every time which fetches GameStates for all sellable items from history.

I had very good results from caching Interests as a class variable and using that for both of these functions. CachedInterests can be fetched in InitScreen() which will also help with clicking the sell button resulting in another call of PopulateData().

var array<XComGameState_Item> CachedInterests;

simulated function InitScreen(XComPlayerController InitController, UIMovie InitMovie, optional name InitName)
{
	super.InitScreen(InitController, InitMovie, InitName);

	History = `XCOMHISTORY;

	CachedInterests = XComGameState_BlackMarket(History.GetGameStateForObjectID(BlackMarketReference.ObjectID)).GetInterests();

	BuildScreen();
	MC.FunctionVoid("AnimateIn");
	Show();
}
function int SortByInterest(BlackMarketItemPrice BuyPriceA, BlackMarketItemPrice BuyPriceB)
{
	local int i;
	local bool FoundItemB;

	FoundItemB = false;

	for (i = 0; i < CachedInterests.Length; i++)
	{
		if (CachedInterests[i].ObjectID == BuyPriceA.ItemRef.ObjectID)
		{
			return 0;
		}

		if (CachedInterests[i].ObjectID == BuyPriceB.ItemRef.ObjectID)
		{
			FoundItemB = true;
		}
	}

	if (FoundItemB)
	{
		return -1;
	}

	return 0;
}
simulated function bool IsInterested(X2ItemTemplate ItemTemplate)
{
	local int i;

	for( i = 0; i < CachedInterests.Length; i++ )
	{
		if( CachedInterests[i].GetMyTemplate() == ItemTemplate )
			return true;
	}
	return false;
}
@faanlez
Copy link
Author

faanlez commented Oct 11, 2024

Forgot to mention UIBlackMarket_SellItem also doing things that could be cached in its PopulateData(). This all could get moved to InitListItem() and making Item a class variable.

ItemPrice = BuyPrice;
ItemRef = BuyPrice.ItemRef;
Price = BuyPrice.Price + BuyPrice.Price * (class'UIUtilities_Strategy'.static.GetBlackMarket().BuyPricePercentIncrease / 100.0f);
Item = GetItem();
ItemTemplate = Item.GetMyTemplate();

ItemCost = class'UIUtilities_Strategy'.static.GetCostQuantity(ItemTemplate.Cost, 'Supplies');
if (ItemCost > 0) // Ensure that the sell price of the item is not more than its cost from engineering
{
	Price = Min(Price, ItemCost);
}

@faanlez
Copy link
Author

faanlez commented Oct 12, 2024

Something that would also help is XComGameState_BlackMarket caching the output of GetInterests() instead of the array being built every time the function is called. This should be done whenever InterestTemplates also gets built.

@faanlez
Copy link
Author

faanlez commented Oct 12, 2024

Also in PopulateData() the UI has to get item states to validate item quantities because it has been given a list that can have zero quantity item states.

foreach Items(Item)
{
	// Don't display if none in your inventory to sell
	InventoryItem = XComGameState_Item(`XCOMHISTORY.GetGameStateForObjectID(Item.ItemRef.ObjectID));
	if( InventoryItem.Quantity > 0 )
	{
		Spawn(class'UIBlackMarket_SellItem', List.itemContainer).InitListItem(Item);
		if(Item == PrevItem)
			List.SetSelectedIndex(List.GetItemCount() - 1);
	}
}

What should be done instead is that XComGameState_HeadquartersXCom should skip the zero quantity items already.

function array<StateObjectReference> GetTradingPostItems()
{
	local XComGameStateHistory History;
	local XComGameState_Item ItemState;
	local array<StateObjectReference> TradingPostItems;
	local int idx;

	History = `XCOMHISTORY;
	TradingPostItems.Length = 0;

	for(idx = 0; idx < Inventory.Length; idx++)
	{
		ItemState = XComGameState_Item(History.GetGameStateForObjectID(Inventory[idx].ObjectID));

		if(ItemState != none)
		{
			if(ItemState.GetMyTemplate().TradingPostValue > 0 && !ItemState.GetMyTemplate().bInfiniteItem && !ItemState.IsNeededForGoldenPath())
			{
				TradingPostItems.AddItem(ItemState.GetReference());
			}
		}
	}

	//<workshop> BLACK_MARKET_FIXES, BET, 2016-04-22
	//INS:
	class'XComGameState_Item'.static.FilterOutGoldenPathItems(TradingPostItems);
	//</workshop>

	return TradingPostItems;
}

This way it would cut down on fetching the same GameStates from History twice which is slowing down the initial opening of the sell screen and the update after clicking the sell button a lot.

@faanlez
Copy link
Author

faanlez commented Oct 16, 2024

I have put the changes so far into a mod:
https://steamcommunity.com/sharedfiles/filedetails/?id=3349050094

@faanlez
Copy link
Author

faanlez commented Oct 29, 2024

Related; If you sell your entire inventory off, you can't open the sell screen again until the next month. This is because the main UIBlackMarket screen checks BuyPrices from XComGameState_BlackMarket, but XComGameState_BlackMarket::UpdateBuyPrices() is only called on the sell screen and monthly reset. Same situation happens if the monthly reset happened while you had nothing to sell in your inventory.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants