Issues Creating a Merge System for a Tycoon

[Introduction] :smiley:

Hi!
I’m working on a little prototype of a tower-tycoon based game. [I apologize in advance, this is a long post lol]

If you’re unfamiliar with this type of game:

  • the game revolves around the idea of purchasing some sort of ‘object’ (unicorns, noobs, monkeys, etc), that drops a collectable item.
  • Once you’ve purchased 3 of these objects you can merge them together, which removes the three objects and inserts a new object of a higher ‘rarity’.

Here is an example game you can play: Unicorn Tycoon 🦄 - Roblox

It seemed like a fun challenge to create my own version of these games. So far everything is going smoothly!

I succesfully created:

  • a base system [player is assigned a base upon joining]
  • collectable drop system [objects drop collectables, can be picked up]
  • buying objects + tower mechanic [able to buy a new base rarity object, once a floor is filled it creates a new floor to begin placing them on]

I’m currently missing 2 major mechanics. But today I’m focusing on completeing the Merging aspect of the game. I’ve tried a series of different methods to try and complete it. But I just cannot wrap my brain around it.

[Problem] :exclamation:

[I’ll be refering to the objects as ‘spawners’ from here on, to make it clearer what I’m referring to!]

Alright lets dissect my approach…

First off, I’ve already initialized a module script containing the dictionary of information relevant to the objects,

local spawnerInfo = {
	-- all spawners are worth 3 of the previous type
	[1] = {
		["Name"] = "Basic",
		["Worth"] = 1, -- how much 'money' u get for picking up
		["Bonus"] = 1, -- drop size * bonus = final revenue
		["Drop Size"] = Vector3.new(1,1,1), -- size of drop
		["Drop Color"] = BrickColor.new("White") -- color of drop		
	},

	[2] = { 
		["Name"] = "Red",
		["Worth"] = 6,
		["Bonus"] = 1,
		["Drop Size"] = Vector3.new(1,1,1),
		["Drop Color"] = BrickColor.new("Grey")		
	}	
}
return spawnerInfo

So these contain important information about each rarity of spawners. I initialized these dictionaries to integer indexes. So the ‘rarities’ are sequential.

Proceeding…

I have a Server script that handles everything. With the module scripts contained inside of the scripts. [see image below]

Screenshot 2023-02-04 at 2.59.35 PM

Inside of the Server script I have a function connected to the event of a RemoteEvent being fired from the client once the player steps on the physical merge button located within their base.
Here is how that looks [nothing crazy]:

merge.OnServerEvent:Connect(function(player, base)
	spawners.Merge(base)
end)

And here is how the hierarchy is setup inside of a base:

Screenshot 2023-02-04 at 3.00.59 PM

Everytime a spawner is purchased, they’re placed inside of the Spawners folder.

Alright, lastly here are the methods I tried, and why they didn’t work :frowning:

First Method [Trying to use Recursion]

function spawners.Merge(base, index)
	-- iterate through spawners (ipairs)
	-- if 3 of the same type exist, remove them, add a new spawner of the next rarity
	-- use recursion until there are no more spawners to merge
	
	if index == 0 then
		print("recursion complete!")
		return -- we finished woohoo
	else

		local tSpawners= base:WaitForChild("Spawners")
		local myTable = {}
		local prev = nil
		
		print("current recursion loop, index: " .. index)
		
		for i,v in ipairs(tSpawners:GetChildren()) do	
			if #myTable == 3 then
				
				local newSpawnerRarity, newSpawnerInfo = next(spawnerInfo, 1)	
				
				for i,v in pairs(myTable) do
					spawners.DeleteSpawner(base, v)
				end		
				
				--spawners.Rearrange(base)
				spawners.CreateSpawner(base, newSpawnerRarity)

				
				return spawners.Merge(base, index-3)
				
			else
				
				if prev == nil then -- did we just start iterating?
					prev = v
					table.insert(myTable, v)
				elseif v.Name == prev.Name then -- make sure its the same type
					table.insert(myTable, v)
				else
					return spawners.Merge(base, index-1)
				end
		
			end	
		end
	end
end

This method was weird, in my head, since the spawners are displayed sequentially decreasing in rarity, I thought by using ipairs I could just transfer the objects into another dictionary → (myTable), to keep track of the amount of a certain rarity. In hind-sight this was kind of a stupid idea. The issue is, the spawners would merge properly and create a new spawner of the next rarity. But I couldn’t do it again.

I used another method called rearrange(), which also isn’t working currently because it wouldn’t move the excess spawners closer to the open spot nearest to the start of the line (bottom left of the tower). But I’m not worrying about that right now. I’m more concerned with this merging aspect.

So that didn’t work, here is my second attempt…

Second Method

function spawners.Merge(base)
	--[[
	Merge spawners in base, for every 3 same type, it will remove them and insert a new spawner of the next rarity
	
	@param base : the player's base
	]]
	local spawnerCounts = {}
	local spawners = base.Spawners
	
	if spawners then
		
	
		for i, spawner in ipairs(spawners:GetChildren()) do
			
			-- count spawners
			if spawnerCounts[spawnerInfo[tonumber(spawner.Name)]] == nil then
				spawnerCounts[spawnerInfo[tonumber(spawner.Name)]] = 1
			else
				spawnerCounts[spawnerInfo[tonumber(spawner.Name)]] += 1
			end
			
			print(unpack(spawnerCounts))

			if spawnerCounts[spawnerInfo[tonumber(spawner.Name)]] == 3 then
				
				-- lets check to see if there is another rarity next..
				local nextSpawnerRarity = spawnerInfo[tonumber(spawner.Name) + 1]
				local indexToRemove
				
				if nextSpawnerRarity == nil then 
					-- there isn't another spawner, don't do anything
					print("YOU REACHED MAX LEVEL WOOHOO!!!")
					return
				end
				
				for j, obj in ipairs(spawners:GetChildren()) do
					if obj.name == spawner.name then
						if indexToRemove == nil then
							indexToRemove = j
						else
							table.remove(spawners:GetChildren(), j)
							spawnerCounts[tonumber(obj.name)] = spawnerCounts[tonumber(obj.name)] - 1
						end
					end
				end
				
				
				table.remove(spawners:GetChildren(), indexToRemove)
				spawners.CreateSpawner(base, nextSpawnerRarity)
					
			end
			
			
		end
		
			
	else
		print("Spawners don't exist?")
	end
end

Honestly not even sure what I was trying to do. But it didn’t work.

Final

TLDR: I’m trying to iterate through a dictionary, find 3 objects with matching types, remove them and insert a new object with the next type

Hopefully a more knowledgeable coder can swoop in and save the day by enlightening me on how to properly create this system :sob: !! Any help is much appreciated! Leave down any ideas or suggestions below! :smiley:

The first table you show, spawnerInfo, would normally be called a list or array, since it is indexed by sequential numbers. Usually a dictionary means something indexed not by numbers, such as a string, which is internally hashed and thus has an unknowable order. In lua this is important because lists are read using ipairs() and dictionaries using pairs(). This may prevent some confusion later.
A good way to do this is to temporarily calculate another table, an actual dictionary, which counts items of each type. This appears to be what you’re doing in that last example. However

table.remove(spawners:GetChildren(), j)

doesn’t seem right. GetChildren() will give you a new table, removing something from that new table is probably not what you want. Did you mean obj:Remove()?

1 Like

did u ever find out how to do it? lol
bc im having the same problem now