ID:1392148
 
In this tutorial I will show you how you can expand on the types of attacks that players can use, and how to keep it tied up in a single proc.

For this example, we're only going to need a few variables, so lets enter them in and give them some values.
mob/var
Hp = 10 //Health
MaxHp = 20//Max Health
Mp = 5 //Mana
MaxMp = 10//Max Mana
Atk = 30 //Attack
Def = 34 //Defense


Great, now that we have those, we can move on to our Attack proc. I'll break this down into sections. First, we will define out proc and give it some input variables.
proc/Attack(mob/User, mob/Target, Type="Normal", Value, Stat="HP", Extra, Cure, Chance=100, Chain)


I know it looks like a lot, but these variables will determine how much damage you do. Let me explain what the purpose of each one is.
  • User
    Person Causing Damage
  • Target
    Person Taking Damage
  • Type
    Type of Attack (Default: Normal)
  • Value
    Used for Specific Values of Damage
  • Stat
    Target Stat to damage (Default: HP)
  • Extra
    Percentage of Damage to use
  • Cure
    Turns damage into a negative for healing effects
  • Chance
    Percentage Chance of Attack being successful (Default: 100)
  • Chain
    Percentage Chance of repeating the Attack

The next thing we want to do is define our damage variable. We will alter this within the attack proc and ultimately determine the damage caused to the Target.
    var/damage  //The Damage User will cause Target


Before we go setting the damage, we will want to check if Chance has been set. If it has, then the attack has a chance of failing, so we want to do that first.
    //If Chance of succeeding fails, stop the proc
if(!prob(Chance)) return


Now we know that if the proc gets to this point, either Chance was not set, or it has succeeded. The next thing we want to do is determine the Type of attack. This is where you can make up your own formulae to determine the damage.
    switch(Type)
if("Normal")
//100% of User Attack - 50% of Target Defense
damage = User.Atk - 0.5*(Target.Def)
if("Blunt")
//80% User Attack - 10% of Target Defense
damage = 0.8*(User.Atk) - 0.1*(Target.Def)
if("Sharp")
//130% User Attack - 100% of Target Defense
damage = 1.3*(User.Atk) - Target.Def
if("Pure")
//100% of User Attack
damage = User.Atk
if("Specific")
//Equal to Value
damage = Value
if("HP")
//Current User HP
damage = User.Hp
if("HP~")
//Amount of User HP lost
damage = User.MaxHp - User.Hp
if("MP")
//Current User MP
damage = User.Mp
if("MP~")
//Amount of User MP lost
damage = User.MaxMp - User.Mp


There we have 9 basic attacks, each one having a different effect on the damage. Once we have our damage set, we have to check to see if we need to alter it in any way. Extra and Cure will alter damage, so we need to check those. We will also need to make sure that mobs can never unintentionally cause negative values of damage that will increase a Stat.
    //If Extra has been set
if(Extra)
//Set Damage to [Extra]%
damage = (damage/100)*Extra

//If damage is lower than 0, set it to 0
damage = max(0,damage)

//If Cure has been set
if(Cure)
//Change damage to a negative value
damage *= -1


Now it's time to actually cause the damage to the Target. We will check the Stat, and decrease it by damage. If the proc was used to Heal the Target, then damage will be a negative value, thus the following will increase the stat by damage.
    //Check which Target Stat to Damage
switch(Stat)
if("HP")
Target.Hp -= damage
if("MP")
Target.Mp -= damage


At this point, we will output the results of the attack to both the User and Target.
    //If the Attack is to cure Target, output the following
if(Cure)
//If the User is the Target, and is also a Player
if(User == Target && User.client)
User << "You restore your [Stat] by [-damage] points!"
//Otherwise
else
//If User is a Player
if(User.client)
User << "You restore [Target]'s [Stat] by [-damage] points!"
//If Target is a Player
if(Target.client)
Target << "[User] restored your [Stat] by [-damage] points!"

//If the Attck is to damage the Target, output this instead
else
if(User == Target && User.client)
User << "You attack your [Stat] for [damage] Damage using a [Type] attack!"
else
if(User.client)
User << "You attack [Target]'s [Stat] for [damage] Damage using a [Type] attack!"
if(Target.client)
Target << "[User] attacked your [Stat] for [damage] Damage using a [Type] attack!"


Like all attacks, we need to check and see if we have killed our Target. So we need to add in our DeathCheck() proc. I'll add an example DeathCheck() into this post.
    //If Target is Dead, end the proc
if(DeathCheck(User,Target)) return


The very last thing we will want to check is if Chain was defined. If so, then the User has a chance to repeat the attack all over again. We add this in after the DeathCheck() as there is no point in hitting a corpse.
    //If the Attack can Chain Hit and succeeds
if(Chain && prob(Chain))
//Repeat the attack
Attack(User,Target,Type,Value,Stat,Extra,Cure,Chance)


And there we have our little Attack() proc. So now we'll look at how to call it, but first, we will add in that DeathCheck() proc.
//Simple DeathCheck: Reset HP, announce death.
//Returns 1 if Attackee dies.
//Returns 0 if Attackee lives.
proc/DeathCheck(mob/Attacker, mob/Attackee)
if(Attackee.Hp < 1)
Attackee.Hp = Attackee.MaxHp
Attackee << "<b>You Died!</b>"
Attacker << "<b>You killed [Attackee]!<b>"
return 1
return 0


As hitting yourself is no fun, we're going to create a victim that we can attack, let's call him Billy.
//Set the mobs name when created
mob/New(n)
..()
if(n) name = n

//Billy will be our victim
var/mob/Billy = new("Billy")


Finally, let's see how to call some of these attacks and beat up poor Billy.
mob/verb/Attack_Billy()
//A basic Normal Attack
Attack(src,Billy,"Normal")

//A Normal Attack with a 50% Chance of Success
Attack(src,Billy,"Normal",Chance=50)

//A Blunt Attack with 75% Chance to Chain Hit
Attack(src,Billy,"Blunt",Chain=75)

//A Sharp Attack with 15% Chance to Chain Hit
Attack(src,Billy,"Sharp",Chain=15)

//A Pure Attack to Targets MP
Attack(src,Billy,"Pure",Stat="MP")

//Attack for 110% of Users Current HP
Attack(src,Billy,"HP",Extra=110)

//Attack for 80% of Users HP Lost
Attack(src,Billy,"HP~",Extra=80)

//Attack for 30% of Users Current MP
Attack(src,Billy,"MP",Extra=30)

//Attack for 50% of Users MP Lost
Attack(src,Billy,"MP~",Extra=50)

//Cure Target for 50
Attack(src,Billy,"Specific",50,Cure=1)

//Cure Targets MP for 20
Attack(src,Billy,"Specific",20,Stat="MP",Cure=1)

//Cure Your Own MP by the amount of HP Lost
Attack(src,src,"HP~",Stat="MP",Cure=1) //Cure Your MP by amount of HP Lost

src << "\n" //Output a blank line
Why not just define Chance in the proc, defaulted to 100?

proc/Attack(/*stuff*/, Chance=100, /*stuff*/)
if(!prob(Chance)) return


Saves a little space.
Overlooked that one, thanks, I'll edit the OP.
If you move this line above the cure modifier, you can just do damage = max(0,damage) rather than the three checks also making sure your heals won't hurt.

 if(!(Cure) && (!damage || damage < 1)) damage = 0
Definitely simpler, thanks Pirion, edited OP.
I understand this is suppose to be simple and easy to use, but if it were me, I'd break the proc down into it's segments.

In my game, the attack proc consists of 3 separate procs.
One to calculate accuracy.
One to calculate damage.
One to deal damage.
(It's actually more complicated than, this, but this is how is basically works)

This might be a little more complex to use, but it has a lot of advantages.
Firstly, the procs are reusable. If I for any reason just need to deal damage to something, I have a proc that handles all this that can be used in anyway I need.
Secondly, each of those procs is an individual entity and I can make changes to them without it having to effect anything else. Also, since each proc only handles a specific part of the attack, if something is wrong I know which proc it is related to.
Finally, inheritance. If a monster for example attacks in a different way than normal, lets just say it deals more damage if your defense is higher, I don't need to add a new proc and calculations all for this one monster. I simply just change the way it's damage calculation proc works for that one monster but nothing else has changed.
In response to The Magic Man
The Magic Man wrote:
In my game, the attack proc consists of 3 separate procs.
One to calculate accuracy.
One to calculate damage.
One to deal damage.
I get how this has it's advantages, I may try to break things down further in any future tutorials I may do. I'll keep this one as is though as opposed to doing such a large rewrite. Thanks.

The Magic Man wrote:
Finally, inheritance. If a monster for example attacks in a different way than normal, lets just say it deals more damage if your defense is higher, I don't need to add a new proc and calculations all for this one monster. I simply just change the way it's damage calculation proc works for that one monster but nothing else has changed.
It only takes two lines of code to add in new attacks.
    switch(Type)
if("Def Breaker")
//100% of User Attack + 150% of Target Defense
damage = User.Atk + 1.5*(Target.Def)

In response to Danny Roe
Inheritance is better and one of the advantages of languages like BYOND.

True enough your way works and it's only an extra line or two of code. But now imagine there is 100, 200 or even 1000 monsters with unique attacks. Suddenly your attack proc goes from being a line or two longer, to hundreds to thousands of lines longer, and much more CPU intensive.
I like the idea of accuracy and damage assessment being different procs just for ease of handling, and damage delivery being a separate proc just on principle.

That said, I think there's something to be said for Danny's all-in-one approach for calculating the damage amount, if it doesn't create a Frankenproc. I've seen a lot of games try to do one proc to handle every monster type and load it with a zillion if statements, which is obviously wrong when inheritance is on the table. However, this isn't so much an inheritance situation because he's doing different types of damage. The damage types are much more universal, so that each formula could reappear among dozens of monsters in a complex game; in that case defining it in one place as he does here is the better option.

However, my preferred approach to this would not be to send everything to the damage proc. Rather, I'd want each monster to have one or two attack types, where each attack would encapsulate this info: Relative hit chance, damage, specials, etc. That would reduce the args to the damage procs and keep things simpler.

Being an efficiency nut, I'd give each monster a string defining its attack types, and possibly its behavioral choices for using them (e.g., one is long range only, or the power attack gets used 25% of the time). Each attack would be written in a kind of shorthand that could be parsed and quickly developed into an /attack datum, which could even be looked up from a global list so the same datums are reused. (Efficiency!) In combat the string would be parsed and converted to a list if it hasn't been already.