Trying to make a game on Roblox (Devlog)

This is the IssuesAreMinor devlog because I’m bored and way too ambitious.

Day 1:


What I’ve done:

I’m mostly starting with game mechanics to make sure that everything works before it looks good. Despite that, I wanted to make sure that avatars were blocky, so I added this simple script into StarterCharacterScripts:

for i = 1, 5, 1 do
	local unblockified = script.Parent:WaitForChild("CharacterMesh")
	unblockified:Destroy()
	wait()
end
script:Destroy()


This is the current foundation of weapons in my game:
Screenshot 2024-07-28 112436

Screenshot 2024-07-28 113327

Trigger:

-- Services
local cas = game:GetService("ContextActionService")
local Players = game:GetService("Players")

local player = Players.LocalPlayer
local chara = player.Character or player.CharacterAdded:Wait() -- Chara from Innertail?! :0 :O :o

local humanoid = chara:WaitForChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")

local itemList = require(game.ReplicatedStorage.BaseWeaponProperties)

local ACTION_BASIC_ATTACK = "Basic Attack"

-- Equipped/swinging or not
local tool = script.Parent
local finishSwing = false
local comboCount = 1

local function handleAction(actionName, inputState, _inputObject)
	if script.CooldownValue.Value == 0 then
		if actionName == ACTION_BASIC_ATTACK and inputState == Enum.UserInputState.Begin then
			if finishSwing == false then
				print(comboCount)
				local swingAnim = Instance.new("Animation")
		
				if comboCount == 1 then
					swingAnim.AnimationId = "rbxassetid://18652765616"
				elseif comboCount == 2 then
					swingAnim.AnimationId = "rbxassetid://18652917464"
				end
			
				local animTrack = animator:LoadAnimation(swingAnim)
				
				animTrack:Play()
			
				finishSwing = true
			
				task.wait(itemList.Data.BasicSword.baseAttackDuration)
			
				finishSwing = false

				
				comboCount += 1
				if comboCount > itemList.Data.BasicSword.comboLength then
					comboCount = 1
				end

				-- final checks
				local checked = false
				
				if comboCount == 1 and checked == false then
					while script.CooldownValue.Value < itemList.Data.BasicSword.baseCooldown do
						script.CooldownValue.TimerTick:FireServer()
						wait(.1)
					end
				script.CooldownValue.TimerTick:FireServer()
				end
			end
		end
	end
end

tool.Equipped:Connect(function()
	cas:BindAction(ACTION_BASIC_ATTACK, handleAction, false, Enum.UserInputType.MouseButton1)	
end)

tool.Unequipped:Connect(function()
	cas:UnbindAction(ACTION_BASIC_ATTACK)
end)
It binds an action to left click, plays an animation when I use the action, makes a combo, and adds a cooldown at the end of a combo.

Timer:

local cd = script.Parent
local ticks = cd.TimerTick

local itemList = require(game.ReplicatedStorage.BaseWeaponProperties)

local function increase()
	print("Fired")
	if cd.Value < itemList.Data.BasicSword.baseCooldown then
		cd.Value += .1
	elseif cd.Value >= itemList.Data.BasicSword.baseCooldown then
		cd.Value = 0
	end
end

ticks.OnServerEvent:Connect(increase)

It increases CooldownValue’s value by .1 every time it runs and sets it to zero if it overflows. Since it registers as slightly lower when you run it, causing it to not reset, I make the code fire it an extra time so that it always resets.


BaseWeaponProperties:

local Weapons = {}

Weapons.Data = {
	["BasicSword"] = {
		["baseDamage"] = 10,
		["comboLength"] = 2,
		["baseAttackDuration"] = .8,
		["baseCooldown"] = 1,
		["statusApplied"] = nil,
		["UniqueAttacksUsed"] = nil
	},
}
return Weapons
A table of all the weapon properties, pretty self-explanatory.

What I plan on doing / Help that I need:

  • Implement a combo cancel (help needed): Every other attack is the combo finisher, which isn’t intended. A combo is supposed to cancel when you take too long to continue it, so if anyone knows how I could reasonably fix it, that would be appreciated.

  • Make attacks do damage, knockback, stuns, and not allow for movement (not started yet): I decided to make my combat sort of like Deepwoken’s in terms of weapons, but it’ll be easier because I’m not going to make a game that I’ll suck at (skill issue moment). The main reason for the decision was because of running attacks since I would need to redo some animation. I can just make the player move a little forward in whatever direction they’re facing instead of moving freely with more Deepwoken style M1 combat.

  • Work on lore: This is private for now, but I might reveal some to people who help me with my code.


I should not be posting my entire source code publicly but if anyone is stealing my shitty code then they’re more stupid than I am.

5 Likes

Btw, why not make a formula for damage and attack speed like AO, thus making gears and armors relevant and indispensable.

If you looked at the variables in the module script, you’d see that it says baseDamage, baseAttackDuration, and baseCooldown. I want to prioritize making them work over making them complicated. That is good thinking though.

well what I like to do for combos is give each player an IntValue and have it subtract itself by a certain amount every 0.1 seconds. To let the player make a combo you can set the value to however high you want it to be and have it refresh every time the player lands a hit. If it reaches 0 or lower the combo should be cancelled (however you choose to implement that), The combo can also be set to 0 once it’s finished too.

Let me try that to see if it works. I’m doing it every 0.01 seconds because I’m expecting to wait 0.5 seconds and 0.1 isn’t precise enough.

Would I have to use a server-side script for that?

yeah but you can use remote events and functions if you want to do the inputting part on the client

I think I’ve got it figured out.

Got it working!

New ancestry or whatever it’s called:
Screenshot 2024-07-28 152947


New code:
ComboHandler:

local refresh = script.ComboRefresh
local timerSet = script.TimerStart
local reset = script.ComboEnd
local timerRan = script.CountingDown.Value

local function timerRunning()
	timerRan = true
	while timer > 0 and timerRan == true do
		print(timer)
		timer -= 1
		wait(.01)
	end
	if timer == 0 then
		timer = 51
	end
end

timerSet.OnServerEvent:Connect(timerRunning)

refresh.OnServerEvent:Connect(function()
	timer = 50
end)

reset.OnServerEvent:Connect(function()
	timerRan = false
end)

Trigger (I only added the part that I changed to make the post shorter):

local function handleAction(actionName, inputState, _inputObject)
	if script.CooldownValue.Value == 0 then
		if actionName == ACTION_BASIC_ATTACK and inputState == Enum.UserInputState.Begin then
			if finishSwing == false then
				script.Parent.ComboHandler.ComboEnd:FireServer()
				print(comboCount)
				local swingAnim = Instance.new("Animation")

				if comboCount == 1 then
					swingAnim.AnimationId = "rbxassetid://18652765616"
				elseif comboCount == 2 then
					swingAnim.AnimationId = "rbxassetid://18652917464"
				end

				local animTrack = animator:LoadAnimation(swingAnim)

				animTrack:Play()

				finishSwing = true

				comboCount += 1
				if comboCount > itemList.Data.BasicSword.comboLength then
					comboCount = 1
				end
				
				task.wait(itemList.Data.BasicSword.baseAttackDuration)
				
				if comboCount ~= 1 then
					script.Parent.ComboHandler.TimerStart:FireServer()
				end
				
				if comboCount == 1 then
					while script.CooldownValue.Value < itemList.Data.BasicSword.baseCooldown do
						script.CooldownValue.TimerTick:FireServer()
						wait(.1)
					end
					script.CooldownValue.TimerTick:FireServer()
				end
				
				finishSwing = false
			end
		end
	end
end

It has a cooldown and a combo reset thanks to @Actravaz, who I’m going to private message a snippet of my lore to so I can dangle the chance of knowing more about my god awful worldbuilding in front of your faces as long as you help the autistic dumbass that I am with coding a game.

1 Like

guys quick before he flags this post, in the lore jesus christ comes back and fights the CIA

I’m going to find where you live for that.

Quick announcement, I started a summer camp that goes from 8 AM to 4 PM and I’ll be there for the week, so development will be a lot slower. I’ll still try getting on the forums every day and giving updates on my progress.

Day 2:

Ancestry:
Screenshot 2024-07-30 182635


Code:

The code for the combo cancel and cooldown after a combo were actually wrong, correct code:

Trigger:

local cas = game:GetService("ContextActionService")
local Players = game:GetService("Players")

local player = Players.LocalPlayer
local chara = player.Character or player.CharacterAdded:Wait() -- Chara from Innertail?! :0 :O :o

local humanoid = chara:WaitForChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")

local itemList = require(game.ReplicatedStorage.BaseWeaponProperties)

local ACTION_BASIC_ATTACK = "Basic Attack"

-- Equipped/swinging or not
local tool = script.Parent
local finishSwing = false
local comboCount = 1

local function handleAction(actionName, inputState, _inputObject)
	if script.CooldownValue.Value == 0 then
		if actionName == ACTION_BASIC_ATTACK and inputState == Enum.UserInputState.Begin then
			if finishSwing == false then
				script.Parent.ComboHandler.ComboEnd:FireServer()
				print(comboCount)
				local swingAnim = Instance.new("Animation")

				if comboCount == 1 then
					swingAnim.AnimationId = "rbxassetid://18652765616"
				elseif comboCount == 2 then
					swingAnim.AnimationId = "rbxassetid://18652917464"
				end

				local animTrack = animator:LoadAnimation(swingAnim)

				animTrack:Play()

				finishSwing = true

				comboCount += 1
				if comboCount > itemList.Data.BasicSword.comboLength then
					comboCount = 1
				end

				task.wait(itemList.Data.BasicSword.baseAttackDuration)

				if comboCount ~= 1 then
					script.Parent.ComboHandler.TimerStart:FireServer()
				end

				if comboCount == 1 then
					while script.CooldownValue.Value < itemList.Data.BasicSword.baseCooldown do
						script.CooldownValue.TimerTick:FireServer()
						wait(.1)
					end
					script.CooldownValue.TimerTick:FireServer()
				end

				finishSwing = false
			end
		end
	end
end
local function resetCombo()
	comboCount = 1
end

tool.Equipped:Connect(function()
	cas:BindAction(ACTION_BASIC_ATTACK, handleAction, false, Enum.UserInputType.MouseButton1)	
end)

tool.Unequipped:Connect(function()
	cas:UnbindAction(ACTION_BASIC_ATTACK)
end)

script.ResetCombo.OnClientEvent:Connect(resetCombo)

Basically just fires the script for combo resets and when it gets fired it resets the combo.


ComboHandler:

local timer = script.ComboTimer.Value
local refresh = script.ComboRefresh
local timerSet = script.TimerStart
local reset = script.ComboEnd
local timerRan = script.CountingDown.Value

local function timerRunning(player)
	timerRan = true
	while timer > 0 and timerRan == true do
		print(timer)
		timer -= 1
		wait(.01)
	end
	if timer == 0 then
		timer = 25
		script.Parent.Trigger.ResetCombo:FireClient(player)
	end
end

timerSet.OnServerEvent:Connect(timerRunning)

refresh.OnServerEvent:Connect(function()
	timer = 25
end)

reset.OnServerEvent:Connect(function()
	timerRan = false
end)
Makes a timer that runs when a weapon is used for the first time, refreshes every time the weapon is used, and gets set back to the start when a combo is finished.

Plans:

  • Make damage and knockback work.
  • Work on lore.

ah yes, fellow coder, that code is now “ours” da comrad?

1 Like

ooo cool, im gonna enjoy seeing this project evolve

you could add an state variable that increases every time you make an attack, and if you dont make an attack after a certain period of time resets back to 0.

ex:

local attackState = 0

function attack()
   attackState += 1
   local currentState = attackState
   ...
   wait(1)
   if currentState == attackState then attackState = 0 end
end

you could probably use your comboCount variable for this

oh wait nvm you already got that figured out lol

oh yeah also quick tip:

rather than nesting if statements you could check for the opposite condition and return if that is true.

ex:

-- nested statements like this
if thing1 == true then
   if thing2 == true then
      if thing3 == true then
         ...
      end
   end
end

-- turn into this
if not thing1 then return end
if not thing2 then return end
if not thing3 then return end

-- or alternatively this
if not (thing1 and thing2 and thing3) then return end

I’m probably going to try getting any posts with the code taken down when I’m finished so I can avoid stolen code and make it harder to exploit.

I personally prefer nesting to that, but if it would actually affect my code in some way other than workflow, please let me know.

Good luck on your game :+1:

1 Like