Hello, thank you for taking the time to read my post. I would consider myself a beginner programmer and only recently began the process of seriously trying to learn. I have yet to make anything substantial, but I am now attempting to.
I have recently created a system that allows players to get into combat with enemy NPCs. I am trying to create a turn-based combat game, with mechanics similar to Fear and Hunger, i,e limb dismemberment. Just providing this explanation in case anyone wonders why combat is started via a .Touched event, as it is like that in the aforementioned game.
Here is a brief overview of the relevant portions of my script(s).
Main and only ServerScript:
-- Loop through the AreaOneTable and connect each model's HumanoidRootPart to the .Touched function inside the EnemyTouched Module
for _, v in ipairs(EnemyTouched.AreaOneTable) do
v:FindFirstChild("HumanoidRootPart").Touched:Connect(function(partTouched)
if partTouched.Parent:FindFirstChild("Humanoid") then
EnemyTouched.AreaOneEnemy(partTouched.Parent, v)
end
end)
end
The corresponding EnemyTouched Module’s relevant sections:
function EnemyTouched.AreaOneEnemy(partTouched, enemy : Model)
if not areaOneDB then
areaOneDB = not areaOneDB
local enemyTable = EnemyDatabase.GetAndReturnVariations(enemy.Name)
-- Define Needed Variables
local rig : Model = partTouched
local humanoid : Humanoid = partTouched:FindFirstChild("Humanoid")
local humanoidRootPart : Part = rig:FindFirstChild("HumanoidRootPart")
local player : Player = game:GetService("Players"):GetPlayerFromCharacter(rig)
if player then
RemoteEventModule.event:FireClient(player, RemoteEventModule.eventTable1)
end
task.wait(1.5)
teleportPlayer(humanoidRootPart, enemy, enemyTable,"Area1")
enemy.Parent = ReplicatedStorage.EnemyStorage
task.wait(.5)
areaOneDB = not areaOneDB
elseif areaOneDB then
end
end
The remote event that fires starts what is essentially a “loading screen”.
The corresponding teleportPlayer() function that I would like the most feedback on:
local function teleportPlayer(humanoidRoot : Part, enemy : Model, enemyTable, whichArea : string)
if whichArea == "Area1" then
local number = enemy:GetAttribute("NumberOfEnemies")
local baseEnemy = ReplicatedStorage.EnemiesToClone[enemy.Name]:FindFirstChild(enemy.Name)
-- Clone the base enemy
local enemyClone = baseEnemy:Clone()
local cloneRoot = enemyClone:FindFirstChild("HumanoidRootPart")
enemyClone.Parent = workspace.FightAreas.Area1.Battleground1.Enemies
local posStr = "EnemySpawn" .. tostring(1)
cloneRoot.CFrame = CFrame.new(workspace.FightAreas.Area1.Battleground1[posStr].Position) * CFrame.Angles(0, 0, 0)
-- Attempt to spawn some variations
for i = 2, number do
print(i)
local spawnVariation = math.random(0, 3) >= 2 -- 66% chance to spawn a variation
if spawnVariation then
local randomVariationName = getRandomKey(enemyTable.Variations)
local varToClone = ReplicatedStorage.EnemiesToClone[enemy.Name].Variations:FindFirstChild(randomVariationName)
-- Check if the variation exists:
if varToClone then
local clone = varToClone:Clone()
local root = clone:FindFirstChild("HumanoidRootPart")
clone.Parent = workspace
local posStr = "EnemySpawn" .. tostring(i)
root.CFrame = CFrame.new(workspace.FightAreas.Area1.Battleground1[posStr].Position) * CFrame.Angles(0, 0, 0)
else
warn("This variation was not found!")
end
else
print("Spawn the base enemy...")
local enemyClone = baseEnemy:Clone()
local cloneRoot = enemyClone:FindFirstChild("HumanoidRootPart")
enemyClone.Parent = workspace.FightAreas.Area1.Battleground1.Enemies
local posStr = "EnemySpawn" .. tostring(i)
cloneRoot.CFrame = CFrame.new(workspace.FightAreas.Area1.Battleground1[posStr].Position) * CFrame.Angles(0, 0, 0)
end
end
-- Teleport the player
humanoidRoot.CFrame = CFrame.new(workspace.FightAreas.Area1.Battleground1.PlayerSpawn1.Position) * CFrame.Angles(0, math.rad(180), 0)
end
end
This is the main function that I have concerns about. As seen above, I want to have a system in my game in which running into one enemy in the overworld has a chance to spawn more than one on a battlefield, in addition to specific variations for each enemy.
For one, as I plan on having different areas (or locations where the player fights), that eventually this one function will have possibly too many “if area == fightArea then” statements, and thought I am not certain, I would think this is inefficient.
Speaking of inefficient, I have noticed it is definitely not as fast as I would want it to be, and have had to compensate the task.wait()s in other functions to lengthen the loading screen to avoid players “loading in” when not teleported or not all enemies have spawned.
I also plan on having all combat calculations done on the server, so there will likely have to be another function and/or bindable event that sends all enemy stats (alongside the player’s) to a separate ModuleScript which handles combat, which I would think makes performance worse.
Lastly, it does feel a bit redundant having to type out the process of cloning various models at least three separate times in the same function. I considered using local functions instead of having it all typed out like this, but I am unsure if that would really improve anything.
Lastly, I did have some concerns about how this would scale up when using custom models, that I do intend to implement in the future. As it stands now, this has only been tested using R15 rigs.
Really, I would just be curious to hear thoughts on how certain things could be done better, or how you specifically would handle any portion of this process.
Thanks again,