[2.0] WCS - A combat system framework

u are a genious, i can not believe i made this mistake and i spent over 1 day trying to figure it out. I hope you do well in life!

1 Like

Can anyone tell whatā€™s wrong with this code? Iā€™m trying to handle inputs for holdable skills but if you start holding one holdable skill button, and then hold another, if you let go of the second one then it will end the first skill. The intended behavior would either be for the second skill input to be ignored if it canā€™t be used at the same time or for the first and second held skills to have separately processed inputs.

Code
UserInputService.InputBegan:Connect(function(Input, GameProcessed)
	if GameProcessed then return end
	if Input.UserInputState ~= Enum.UserInputState.Begin then return end
	
	local keys = {
		Enum.KeyCode.One, 
		Enum.KeyCode.Two, 
		Enum.KeyCode.Three, 
		Enum.KeyCode.Four,
		Enum.KeyCode.R,
		Enum.UserInputType.MouseButton1, 
		Enum.KeyCode.F,
		Enum.KeyCode.Q, 
		Enum.KeyCode.G}
	
	local function processInput(Input)

		local inputType

		if Input.UserInputType == Enum.UserInputType.Keyboard then
			inputType = Input.KeyCode

		elseif Input.UserInputType then
			inputType = Input.UserInputType

		end

		return inputType

	end
	
	local function getSkillIndex(skillInput)
		
		local skillIndex = table.find(keys, skillInput)
		if not skillIndex then return end
		
		return skillIndex
		
	end
	
	local function getSkill(skillIndex)
		
		local currentWCS_Character = getCurrentWCS_Character()
		if not currentWCS_Character then return end

		local currentMoveset = currentWCS_Character:GetMoveset()
		if not currentMoveset then return end

		local constructor = currentMoveset.Skills[skillIndex]
		local skill = currentWCS_Character:GetSkillFromConstructor(constructor)
		
		return skill
		
	end
	
	local skillInput = processInput(Input)
	if not skillInput then return end
	
	print(skillInput)
	
	local skillIndex = getSkillIndex(skillInput)
	if not skillIndex then return end
	
	print(skillIndex)
	
	local skill = getSkill(skillIndex)
	if not skill then return end
	
	local isHoldableSkill = skill:GetSkillType()
	if isHoldableSkill then
			
		skill:Start()
		
		UserInputService.InputEnded:Connect(function(skillInput, GameProcessed)
			skill:End()
		end)
		
	else
		
		skill:Start()
		
	end
	
end)

Now that I think about it, iā€™m wondering if all those functions inside the InputBegan event should be outside of it instead. Does it have to create these functions from scratch every time you make an input? Sounds bad.

1 Like

Highly recommend this framework, amazing design around it, I have a lot of projects, and different codebases can be stressful. This helps me implement any combat system I make into any framework or design.

4 Likes
3 Likes

omgā€¦ that amazing! itā€™s will help me ALOT thank you so much!

new tip..
image

but question :thinking:
is there way that i can clear the skills?

2 Likes

thanks for support! :heart: you can manually iterate through the skill map and call the destroy method:

character:ClearMoveset()
for _, skill in character:GetSkills() do
     skill:Destroy()
end
2 Likes

Iā€™m making my own combat framework currently and Iā€™ve been considering using this resource, however there are some aspects of it that make me reluctant to implement it. One aspect that iā€™m iffy on is lag compensation. For example, in my own framework, I let the client perform any action they want (regardless of legitimacy) and send the playerā€™s inputs and states off to the server as a ā€œcommandā€ every heartbeat to check for integrity. If the check passes, the states of the player are replicated to the rest of the players where they can be interpreted (and properly compensated for lag including character position). Besides the obvious security concerns of my system, Iā€™m just unsure if WCS is the way to go in my case due to the strict accuracy my game demands. Iā€™d be happy to hear anyone out though!

1 Like

Hi, Iā€™ve had issue with making an holdableSkill Sprint, this skill canā€™t be used again after used one time, sorry to asking this I already try read docs and api but no one help (my english kinda bad).

main.client.ts

import { Players, ReplicatedStorage, UserInputService } from "@rbxts/services";
import { Character, CreateClient } from "@rbxts/wcs";
import { Sprint } from "shared/skills/sprint";

const Client = CreateClient();
Client.RegisterDirectory(ReplicatedStorage.TS.movesets);
Client.RegisterDirectory(ReplicatedStorage.TS.skills);
Client.RegisterDirectory(ReplicatedStorage.TS.statusEffects);
Client.Start();

function getCurrentWCS_Character() {
	const characterModel = Players.LocalPlayer.Character;
	if (!characterModel) return;

	return Character.GetCharacterFromInstance(characterModel) || Character.CharacterCreated.Wait()[0];
}

UserInputService.InputBegan.Connect((Input, GameProcessed) => {
	if (GameProcessed) return;
	if (Input.UserInputState !== Enum.UserInputState.Begin) return;

	const character = getCurrentWCS_Character();

	if (Input.KeyCode === Enum.KeyCode.LeftControl) {
		character?.GetSkillFromConstructor(Sprint)?.Start();
	}
});

UserInputService.InputEnded.Connect((Input, GameProcessed) => {
	if (GameProcessed) return;

	const character = getCurrentWCS_Character();

	if (Input.KeyCode === Enum.KeyCode.LeftControl) {
		character?.GetSkillFromConstructor(Sprint)?.End();
	}
});

sprint.ts

import { HoldableSkill, SkillDecorator } from "@rbxts/wcs";
import { SpeedBoost } from "shared/statusEffects/speedBoost";
import { Stun } from "shared/statusEffects/stun";

@SkillDecorator
export class Sprint extends HoldableSkill {
	protected MutualExclusives = [Stun];
	private speedBoost = new SpeedBoost(this.Character);

	public OnConstructServer() {
		this.SetMaxHoldTime(5);
	}

	public OnStartServer() {
		this.speedBoost.Start();
		this.ApplyCooldown(10);
	}

	public OnEndClient() {
		this.speedBoost.Stop();
	}
}

speedBoost.ts

import { StatusEffect, StatusEffectDecorator } from "@rbxts/wcs";

@StatusEffectDecorator
export class SpeedBoost extends StatusEffect {
	public OnStartServer() {
		this.SetHumanoidData({ WalkSpeed: [25, "Set"] });
	}

	public OnEndServer() {
		this.SetHumanoidData({ WalkSpeed: [16, "Set"] });
	}
}
1 Like

nvm, Iā€™ve found out the issueā€¦ It shouldā€™ve more than 29 characters? :sob:

1 Like

Hello,

I had a minor question on some custom-types in wcs. I want to use StatusEffectImp, Character (type) in files outside of the package, how can I do it?
E.g:

local ReplicatedStorage = game:GetService(ā€œReplicatedStorageā€)
local WCS = require(ReplicatedStorage.Packages.wcs)

export type PlayPhaseStatusType = {
OriginalStatus: string,
Status: StatusEffectImp ā€“ cannot find the type
}

Hello,
When I am attempting to call the name of an ability it keeps returning as Nilā€¦ however, yesterday I had it working and calling the correct nameā€¦ didnā€™t really change anythingā€¦ and suddenly itā€™s not working.

--References--
local skillsRef = WCSFolder:WaitForChild("skills")
local skills = {
	Fireball = require(skillsRef.Fireball),
	Frost_Shard = require(skillsRef.Frost_Shard),
	Basic_Blast = require(skillsRef.Basic_Blast)
}

My skills are in fact registered:

Players.PlayerAdded:Connect(function(Player)
	print("Player Added", Player)
	Player.CharacterAdded:Connect(function(CharacterModel)
		print("Character Added Gonna make skills")
		-- apply the wrap when character model gets created
		local WCS_Character = Character.new(CharacterModel)
		print(WCS_Character, CharacterModel.Name)

		-- apply our freshly made skill
		skills.Fireball.new(WCS_Character)
		skills.Frost_Shard.new(WCS_Character)
		skills.Basic_Blast.new(WCS_Character)

		ConstantSkills.Shield.new(WCS_Character)

		-- destroy it when humanoid dies
		local Humanoid = CharacterModel:WaitForChild("Humanoid")
		Humanoid.Died:Once(function()
			WCS_Character:Destroy()
		end)
	end)
end)

When I am trying to call the name, Itā€™s more official name is meant to be applied to a Gui, and the reference of the module is meant to then be applied to another dictionary.

--Spell Changing Click Once "T"
		elseif Input.KeyCode == Enum.KeyCode.T then
			local character = getCurrentWCS_Character()
			if not character then return end
			print("The Character is", character)
			local Num 
			local NumOfSkills = 0
			local loopNum = 0
			for i,v in pairs(skills) do
				NumOfSkills = NumOfSkills + 1
				print("Got SKill", i, v, NumOfSkills)
			end
 			if Information.skillsListNum ~= NumOfSkills then
				Num = Information.skillsListNum + 1
			else
				Num = 1
			end
			print("The Num is", Num)
			for spell,SpellCert in pairs(skills) do
				loopNum = loopNum + 1
				print("Looking Through Spelld", spell, SpellCert)
				if loopNum == Num then
					print("Found the spell")
					Information.Current_Spell = SpellCert --Here is where the problem is
					Information.skillsListNum = Num
					SpellID_GUI.SpellIdentifier.Text = SpellCert.Name --Here is where the problem is
					break
				elseif SpellCert.Name == nil then
					print("Spell Name is Nil!..", spell)
				end
			end
			print(NumOfSkills, Num, skills[Num],Information.Current_Spell)
		end
	end

It was working yesterdayā€¦ dont know what changed.
Please help!
Thanks,
Manelin

Iā€™m stuggling for some reason with the TakeDamage Method

	local DamageContainer = Shock_Blast.CreateDamageContainer(15)
	
	local targetCharacter = WCS.Character.GetCharacterFromInstance(Victim.Character)
	print(DamageContainer.Damage, DamageContainer.Source, "Is the Damage values")
	targetCharacter:TakeDamage(DamageContainer)
end

Please help!

Update, I managed to get it workingā€¦ silly me. But Iā€™m confused about why thereā€™s still an error here that the 'DamageDealt" is nil, yet itā€™s still running the damage function.

local function OnIntersection(Player: Player, Direction: Vector3, Part: string, Victim: Player?, Position: Vector3, Character: Model, Extra: any)
	local Humanoid: Humanoid? = Character and Character:FindFirstChild("Humanoid") :: Humanoid
	if not Humanoid or Humanoid.Health <= 0 then
		return
	end
	
	local targetCharacter = WCS.Character.GetCharacterFromInstance(Victim.Character)

	targetCharacter.DamageTaken:Connect(function(container)
		print("Gonna Take Damage in Shock_Blast")
		targetCharacter.Instance.Humanoid:TakeDamage(container.Damage)
	end)


	local DamageContainer = SkillScript:CreateDamageContainer(15)

	print(DamageContainer.Damage, DamageContainer.Source.Character, "Is the Damage values")
	targetCharacter:TakeDamage(DamageContainer)
	--SkillScript:EffectOnTargetCharacter(Player,Direction,Victim,Position,Character)
	print(`Intersected {Victim or Character}'s {Part}`)
end

are you running that on client?

I managed to figure it out. It was because I was failing to call the registered skill within the player rather than just callilng a require on the modulescript.

Iā€™m having anew issue now, where my registeredHoldableSkills are returning as ā€œdefaultā€ when I call the ā€œGetSkillType()ā€ method.
I have put some checks within the source code when registering a holdable skill, and it appears that all of the skills are being processed properly as a holdable skillā€¦ but there must be something wrong with how Iā€™m calling holdable skills? Let me provide the output data as well as the area of code that iā€™m using.

Output:

  1. Pink Highlight = A print I am generating from the ā€˜HoldableSkillā€™ module within the WCS framework right after it classifies the skill type as holdable, and it presents it back as the correct vale. So I assume It must be something wrong with How iā€™m using the framework in my own code.
  2. Yellow Highlight = The print I am generating with ā€˜GetSkillType()ā€™

Next: A fragment of my code:

Here is where I am indexing my required calls for all of my skill modules:

Here is the code in the script I am trying to use to call the Holdable Skill

--USER INPUT FUNCTION--
UserInputService.InputBegan:Connect(function(Input, GameProcessed)
	if GameProcessed then return end
	if Input.UserInputType == Enum.UserInputType.MouseButton1 then-->Run Spell Casting
		if Input.UserInputState ~= Enum.UserInputState.Begin then return end
		local character = getCurrentWCS_Character()
		local SkillConstructed = character:GetSkillFromConstructor(Information.Current_Spell)

		if not character or not Information.BattleReady then return end
		if Information.Button_Down or Information.ChargeHold then return end
		print(SkillConstructed:GetSkillType(), "Is the skilltype")
		if SkillConstructed:GetSkillType() == "Holdable" then -----------------IS RETURNING AS DEFAULT INSTEAD OF HOLDABLE, BUT REGISTERED AS HOLDABLE!
			Information.ChargeHold = true
			ChargePrep(1.5)
			character:GetSkillFromConstructor(Information.Current_Spell):Start()
			local runRelease = UserInputService.InputEnded:Connect(function(NewInput, NewGameProcessed)
				if NewInput.UserInputType == Enum.UserInputType.MouseButton1 then  
					Information.ChargeHold = false
					ChargePrep(1.5)
					if Information.Charged then
						Information.Charged = false
						character:GetSkillFromConstructor(Information.Current_Spell):Stop()
					end
				end
			end)
			repeat wait() until not Information.ChargeHold
			runRelease:Disconnect()		
		else
			local MouseHit = Mouse.Hit.Position
			character:GetSkillFromConstructor(Information.Current_Spell):Start(MouseHit)
		end


--HOLDABLE SHIELD SPELL-------->
	elseif Input.UserInputType == Enum.UserInputType.Keyboard then-->Block Spell Casting + Change Spell

		if Input.KeyCode == Enum.KeyCode.Q then	-->Block is Click and Hold
			local character = getCurrentWCS_Character()
			local SkillConstructed = character:GetSkillFromConstructor(Skill_Data.ConstantSkills.Shield)
			print(SkillConstructed:GetSkillType(), "Is the skilltype", SkillConstructed, Skill_Data.ConstantSkills.Shield) ---------------------------IS PRINTING AS DEFAULT INSTEAD OF HOLDABLE!!

			if not character or not Information.BattleReady then return end
			character:GetSkillFromConstructor(Skill_Data.ConstantSkills.Shield):Start()
			Information.Button_Down = true
			local runRelease = UserInputService.InputEnded:Connect(function(NewInput, NewGameProcessed)
				if NewInput.KeyCode ~= Enum.KeyCode.Q then repeat wait() until NewInput.KeyCode == Enum.KeyCode.Q end
				print()
				character:GetSkillFromConstructor(Skill_Data.ConstantSkills.Shield):Stop()
				Information.Button_Down = false
			end)
			repeat wait() until not Information.Button_Down
			runRelease:Disconnect()

Here is the Shield Module that iā€™m trying to get the Holdable type from,
The Yellow Highlight at the bottom is not calling at allā€¦ Iā€™m asuming this might ahve something to do with it?

Iā€™m still learning to use this Framework so please bare with my questions. Iā€™ll let you know if I figure it out before someone responds. But, if someone could help Please!

oh yeah, this is a bug and been fixed recently, the version just isnā€™t released yet. you can downgrade temporarily or add _internal_ prefix to the corresponding variable in a module named ā€œholdableSkillā€

Interesting. Thanks. I also discovered that you can simply get the skill from the character and add .skillType, which will return the skill type.

The issue is now fixed in version 2.4.1: Release āš™WCS 2.4.1āš™ Ā· wad4444/WCS Ā· GitHub

1 Like

Legendary! Thanks a million!
-Manelin

What are starter params? Iā€™m not sure what they are and how to use themā€¦