How would I be able to require both MeleeEntity and RangedEntity without a Cylic Module Dependency"?

Heya Everyone!!

I’m working on an entity system which controls the enemies in the game. Since I’m fairly new to OOP, I’ve decided to use this DevForum post as a stepping stone for my system.

How the system works is that there is a module script named EntityHandler and, as said in the title, two more module scripts named MeleeEntity and RangedEntity respectively. EntityHandler is essentially the base of both the two aforementioned module scripts, containing both the spawning and despawning of the entity alongside universal movement stuff like pathfinding.
MeleeEntity and RangedEntity is actually the module scripts to give specific functionality to the enemies (Sorry if the wording feels weird). For example, RangedEntity would contain all the functions of a ranged entity such as firing or some type of evade ability like strafing or backing away.

The issue is that I’m stuck on trying to require the two entity modules since doing will cause a “Cycle Module Dependecy” error. How would I fix that?

EntityHandler

--[[SERVICES]]--
local ServerStorage = game:GetService("ServerStorage")
local Debris = game:GetService("Debris")

--[[ENEMY FOLDERS & REQURED ENTITY SCRIPTS]]--
local EnemiesFolder = ServerStorage:WaitForChild("Enemies")
local CommonEnemiesFolder = EnemiesFolder.Commons
local BossesFolder = EnemiesFolder.Bosses

--[[MODULE]]--
local EntityModule = {}
EntityModule.__index = EntityModule


--//Spawning & Despawning
function EntityModule:SpawnEntity(EntityName)
	--//Finding the entity.
	local RequestedEntity = CommonEnemiesFolder:FindFirstChild(EntityName) if not RequestedEntity then
		RequestedEntity = BossesFolder:FindFirstChild(EntityName) if not RequestedEntity then
			warn("Cannot find: "..EntityName..".")
			return
		end
	end

	local self = setmetatable({}, EntityModule)

	self.Entity = RequestedEntity:Clone()
	self.Humanoid = self.Entity:FindFirstChildWhichIsA("Humanoid")
	self.Entity.Parent = game.Workspace.AliveEnemies

	--//Assigning enemy type functionality & despawn function
	
	self:DespawnEntity()

	return self
end

function EntityModule:DespawnEntity()
	if self.Humanoid.Health >= 0 then
		Debris:AddItem(self.Entity,5)
	end
end

--//General Movement
function EntityModule:SearchNearbyTargets()
	--//TBA
end

function EntityModule:Pathfind(Target)
	--//TBA
end

function EntityModule:Circle(EntityModule)
	--//TBA
end

return EntityModule

RangedEntity/MeleeEntity (They’re almost the same as the time I’m writing this.)

--[[REQUIRED MODULES]]--
local EntityHandler = require(script.Parent)

--[[MODULE]]--
local RangedEntityModule = {}
setmetatable(RangedEntityModule,RangedEntityModule)
RangedEntityModule.__index = EntityHandler

--What I'm planning to do here is to make the EntityHandler use this function to get the Entity model via the Body.
function RangedEntityModule:OnReady(Body)
	--//TBA
end

function RangedEntityModule:FireAtTarget()
	--//TBA
end

return RangedEntityModule

i like to store my modules in _G (specifically as a .Modules table within _G) ((_G.Modules = {}))

you can then call/create references to the modules where you need them
pseudocode V

-- main (boot script)
local modules = {}
for _, Module in script.Modules:GetChildren() do
	modules[Module.Name] = require(Module)
end
_G.Modules = modules
-- module A
local ModuleB = _G.Modules.ModuleB
-- module B
local ModuleA = _G.Modules.ModuleA

(do note that the modules in the given examples would fail given as is, since one of them will be require()d before the other, and the other would be nullified. the boot structure below accounts for this)

here’s a more advanced boot structure by Suphi

there are a lot of devs that absolutely hate _G, but like, do whatever works for you

If you encounter cyclical dependencies, it means you structured your code wrong. Don’t use _G or some hacky way to do it. Change the structure of the modules so that you only need to require one way.

Sorry for the late reply, but this is what I have currently. How would I reformat this?

Are you using inheritance for these classes? From what I read, MaleeEntity and RangedEntity should inherit from EntityHandler.

I haven’t actually considered using inheritance for the classes. Anyway, how would I be able to do so?

Since you are new to OOP, you may not be familiar with the concept of inheritance, but what you are doing right now with EntityHandler, MaleeEntity, and RangedEntity is basically inheritance, just structured in a more complex way.

You can check Programming in Lua : 16.2 out, but I also recommend learning the concept of OOP in a different language such as Java first! There are many resources online, so get started with some research. :+1:

Also in your example, the sub-class would need to require the super-class, but the super-class should not need to require the sub-class at all. When creating an object of the sub-class, you should ultimately be able to require the sub-class only. I just took a closer look at your code and I am confused because the superclass is not requiring the sub-classes anywhere. Maybe you set the wrong metatable, will need to check more.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.