Adding skills
From VbGORE Visual Basic Online RPG Engine
In vbGORE, skills must be fully coded - no scripting or anything of the sort. This is done because of flexibility. Scripted spells are very limited, while hard-coded spells will let you do what ever you want to do. The problem that comes with this, though, is difficulty.
Keep in mind that skills are so diverse in what they accomplish, from buffs and healing to damage attacks and maybe even map-altering effects. All the code displayed here is just suggestions on how certain tasks can be accomplished. Do not just "copy and paste" the code thinking it will work in all conditions.
Contents |
[edit] Writing the spell code
[edit] Overview
The most important part of the spell code is the spell code itself. These are all held in the server's Skills module. Most of the default vbGORE spells offer support for casting from PC->NPC, NPC->NPC, and PC->PC. Also, constants are added to the top to easily define certain values. These are not required, but it is recommended for ease-of-use. Its all about personal style, though.
[edit] Checking cast conditions
The first part of the skill sub is checking for valid conditions. This depends on if the caster or target is a NPC or PC. Casting between two PCs will have the most checks.
Start with making sure the user is even in the state to cast the spell or be casted on, such as if they are online (or the NPC is alive). For example, from PC->PC:
If UserList(CasterIndex).flags.UserLogged = 0 Then Exit Sub If UserList(TargetIndex).flags.UserLogged = 0 Then Exit Sub If UserList(CasterIndex).Counters.SpellExhaustion > 0 Then Exit Sub
The next step is to make sure the user who is casting the skill knows it. If it is a NPC, it is often safe to assume that if the routine is being called, they are supposed to be casting it, so checks on NPC for this is not required:
'Check if the caster knows the skill If UserList(CasterIndex).KnownSkills(SkID.Bless) = 0 Then Data_Send ToIndex, CasterIndex, cMessage(37).Data Exit Sub End If
After this, make sure the caster (NPC or PC) has enough mana, stamina or health to use the skill if any is needed. For example:
'Check for enough mana to cast If UserList(CasterIndex).Stats.BaseStat(SID.MinMAN) < Int(UserList(CasterIndex).Stats.ModStat(SID.Mag) * Bless_Cost) Then Data_Send ToIndex, CasterIndex, cMessage(38).Data Exit Sub End If
For users, it is often important to, just in case, put in a distance check before the skill is used if it is target-based:
'Check for a valid target distance If Server_CheckTargetedDistance(CasterIndex) = 0 Then Exit Sub
[edit] Using the skill
We now know the user passes all conditions to use the skill. From here on, we assume the skill will be used successfully, so reduce the user's stats if needed (ie mana):
'Reduce the mana UserList(CasterIndex).Stats.BaseStat(SID.MinMAN) = UserList(CasterIndex).Stats.BaseStat(SID.MinMAN) - Int(UserList(CasterIndex).Stats.ModStat(SID.Mag) * Bless_Cost)
For ailments or buff spells, like a Curse or Blessing, you will often want to make sure that the user doesn't already have the skill applied on them with a stronger power. The reason for this is you don't want someone to be able to have a weaker buff casted on them by their enemy to cancel theirs out. A good idea is to allow the skill to be casted on them either way if the one casted on them is about to run out - this will allow users to avoid that "unbuffed time" between when the old buff runs out, and the new one is casted. The below example doesn't do this, though:
'Cast on the target If UserList(TargetIndex).Counters.BlessCounter > 0 Then If UserList(TargetIndex).Skills.Bless > UserList(CasterIndex).Stats.ModStat(SID.Mag) Then
Next is to deal with the icons displayed above the caster and target, if any. The most common on is the spell exhaustion (time that must be waited between casting skills). The below is an example that will display the bless icon:
'Display the bless icon (only if it isn't already displayed) If UserList(TargetIndex).Skills.Bless = 0 Then ConBuf.PreAllocate 4 ConBuf.Put_Byte DataCode.Server_IconBlessed ConBuf.Put_Byte 1 ConBuf.Put_Integer UserList(TargetIndex).Char.CharIndex Data_Send ToMap, CasterIndex, ConBuf.Get_Buffer, UserList(CasterIndex).Pos.Map, PP_StatusIcons End If
And spell exhaustion (icon plus adding the spell exhaustion time):
'Add the spell exhaustion and display it UserList(CasterIndex).Counters.SpellExhaustion = timeGetTime Bless_Exhaust ConBuf.PreAllocate 4 ConBuf.Put_Byte DataCode.Server_IconSpellExhaustion ConBuf.Put_Byte 1 ConBuf.Put_Integer UserList(CasterIndex).Char.CharIndex Data_Send ToMap, CasterIndex, ConBuf.Get_Buffer, UserList(CasterIndex).Pos.Map, PP_StatusIcons
Keep in mind that times are calculated as CurrentTime Length (in milliseconds), not just length. This helps reduce CPU load for timer-based calculations, along with much more accuracy.
Theres a few more display tasks left. The below is an example of how to tell the caster and target that bless was casted:
'Send the message to the caster If TargetIndex <> CasterIndex Then ConBuf.PreAllocate 3 Len(UserList(TargetIndex).Name) ConBuf.Put_Byte DataCode.Server_Message ConBuf.Put_Byte 40 ConBuf.Put_String UserList(TargetIndex).Name Data_Send ToIndex, CasterIndex, ConBuf.Get_Buffer 'Face the caster to the target UserList(CasterIndex).Char.Heading = Server_FindDirection(UserList(CasterIndex).Pos, UserList(TargetIndex).Pos) UserList(CasterIndex).Char.HeadHeading = UserList(CasterIndex).Char.Heading ConBuf.PreAllocate 4 ConBuf.Put_Byte DataCode.User_Rotate ConBuf.Put_Integer UserList(CasterIndex).Char.CharIndex ConBuf.Put_Byte UserList(CasterIndex).Char.Heading Data_Send ToMap, CasterIndex, ConBuf.Get_Buffer, UserList(CasterIndex).Pos.Map End If 'Send the message to the target ConBuf.PreAllocate 5 Len(UserList(CasterIndex).Name) ConBuf.Put_Byte DataCode.Server_Message ConBuf.Put_Byte 41 ConBuf.Put_String UserList(CasterIndex).Name ConBuf.Put_Integer UserList(CasterIndex).Skills.Bless Data_Send ToIndex, TargetIndex, ConBuf.Get_Buffer
Now, for the display effects. This has to be handled more on the client which we will get to later. If you want to display a spell effect, you will have to pass the skill ID, then specific parameters for that skill ID. Often, this is just the character index of the target and caster:
'Display the effect ConBuf.PreAllocate 6 ConBuf.Put_Byte DataCode.User_CastSkill ConBuf.Put_Byte SkID.Bless ConBuf.Put_Integer UserList(CasterIndex).Char.CharIndex ConBuf.Put_Integer UserList(TargetIndex).Char.CharIndex Data_Send ToMap, CasterIndex, ConBuf.Get_Buffer, UserList(CasterIndex).Pos.Map, PP_DisplaySpell
Finally, the very last thing is the sound. The sound requires just sending the sound number (such as 6 for 6.wav) along with the tile position:
'Play sound effect ConBuf.PreAllocate 4 ConBuf.Put_Byte DataCode.Server_PlaySound3D ConBuf.Put_Byte Bless_Sfx ConBuf.Put_Byte UserList(CasterIndex).Pos.X ConBuf.Put_Byte UserList(CasterIndex).Pos.Y Data_Send ToPCArea, CasterIndex, ConBuf.Get_Buffer, , PP_Sound
[edit] Adding the skill ID
The skill ID (SkillID) is what is used to let the server and client tell each other what skill is being used without passing the name. The skill ID is held in the DataIDs module, which is part of the Common Code, which means the client and server use the same module. If you change the code in the server, for example, it will be shown in the client when you load it up.
Scroll down to:
Public Type SkillID Bless As Byte Protection As Byte Strengthen As Byte Warcry As Byte Heal As Byte IronSkin As Byte SpikeField As Byte SummonBandit As Byte End Type Public SkID As SkillID 'Skill IDs Public Const NumSkills As Byte = 8
Anywhere in the SkillID UDT, add the name of your skill, such as:
MySkill As Byte
Make sure you update the NumSkills constant with the number of skills you have. In most cases, this will be the same number as there are variables in the SkillID UDT.
Public Const NumSkills As Byte = 9 'Change to highest skill ID
Now apply the value to the new skill by finding:
With SkID .Bless = 1 .Heal = 2 .IronSkin = 3 .Protection = 4 .Strengthen = 5 .Warcry = 6 .SpikeField = 7 .SummonBandit = 8 End With
And adding in the next free number for your skill, such as:
.MySkill = 9
[edit] SkillIDto...
If you have v1.0.2 or earlier, please apply this fix first!
Please delete this message after v1.0.3 is out
While the server only needs to know the SkillID number and what skill subs it relates to, the client needs to be able to convert the skill ID into the skill name and grh to display the skill information on the client. These subs are Engine_SkillIDtoGRHID and Engine_SkillIDtoSkillName. The usage for them are very self explanatory, you just have to enter the Grh number and skill name.
After following these steps, if your user knows the skill, they should be able to see it in their skill selection list, which can be brought up by Shift LeftClick on a quick bar slot.
[edit] Extending the skills
[edit] Status effects and timers
A huge part about spells is adding ailments and timers. For example, if you want to have a Curse spell that lowers all of the user's stats and lasts for 5 minutes, will need a timer variable to say it lasts for 5 minutes, and a variable to say to tell the server the spell is on the user, and how powerful the spell is.
On the server, look for:
Type UserCounters
You will see a lot of skill counters in here already. As stated before, the value stored in this counter is not how long the skill lasts, but at what time the skill will run out, which is found by timeGetTime Length, and is represented in milliseconds. Keep all counters as a Long.
Next, for adding the information of the skill to the user, search for:
Type Skills
This holds the power of the skill. How you use and calculate this value is up to you. Some skills, like Iron Skill, is just either on or off, while the others are held by value, where a higher value = higher power.
Next, you want to make sure you are checking to see if the counter runs out. Plenty of examples of this can be found if you look for the code:
'*** Update the counters ***
For example, here is bless:
If UserList(UserIndex).Counters.BlessCounter > 0 Then If UserList(UserIndex).Counters.BlessCounter < timeGetTime Then UserList(UserIndex).Skills.Bless = 0 ConBuf.PreAllocate 4 ConBuf.Put_Byte DataCode.Server_IconBlessed ConBuf.Put_Byte 0 ConBuf.Put_Integer UserList(UserIndex).Char.CharIndex Data_Send ToMap, UserIndex, ConBuf.Get_Buffer, UserList(UserIndex).Pos.Map, PP_StatusIcons User_UpdateModStats UserIndex End If End If
The basic concept of this is if the spell ran out, set the skill value to 0 (turn off the ailment), turn off the icon and update their mod stats.
The final step is to make use of the ailment. This is done in User_UpdateModStats. For example:
'Protection If UserList(UserIndex).Skills.Protect > 0 Then Log "User_UpdateModStats: Updating effects of skill/spell Protection", CodeTracker '//\\LOGLINE//\\ .ModStat(SID.DEF) = .ModStat(SID.DEF) UserList(UserIndex).Skills.Protect End If
This should be self explanatory. :) Make sure you use ModStat and BaseStat appropriately.




