ID:1244337
 
Keywords: passive, skills
(See the best response by Dariuc.)
Problem description:

I am trying to find out the most effective way to do passive skills, by passive skills i mean a skill that always gives me a bonus without me having to cast anything.

Reason i want this is so that i can add the "passive" to the skilltree as if it was a normal skill and then it will give a boost based on its level e.g. 2% increase to all dmg at lev 2.

Now i can do this easy enough with making it an active skill and a buff system and i just "cast" the skill. which is all fine and good im happy with making some skills that give a boost work that way but id like some that are not active (castable) skills.

easiest example would be wow lets say priest holy tree they have alot of talents that provide increases/chances/deductions to various feeds without requiring something to be casted --
(from WoW) Holy Specialization (5 points) - Increases the critical effect chance of your Holy spells by 1%/2%/3%/4%/5%. Works for both healing and damage spells.


What would be the easiest way to handle this?

Currently my skills are a datum named skills with all the details contained in procs before actually being casted.. cost/cooldown checks > removing of stats > activate so pretty standard.
Best response
Your use of datums will simplify this.

You could add a variable called passive_bonus

and then with any calculations you could include that var into your calcs.

//determine critical
Holy_Skill
var
skill=5
passive_bonus=5

proc
DetermineSkill()
var/total=Holy_Skill.skill+passive_bonus
//do whatever else


This way it won't matter if the passive bonus is 0-- the formula never changes, and it has the benefit of you not needing to alter the formula when a player unlocks a passive benefit.
Not quire sure on that one i get where you are sort of coming from but at the same time i don;t quite see what you are trying to show, lets take a stab.

proc
ifindaskill(skilltype)
var/skill/S = locate(skilltype) in src.skills
return (S)

icalcdmg()
var/dmg = max(0,int*0.5)
if(ifindaskill(/skill/magician/dmgbuff))
var/skill/s = ifindaskill(/skill/magician/dmgbuff)
dmg = max(0,dmg+(dmg*(s.maxlev/100)))
return dmg

skill
fire
fireball
maxlev = 5
magician
dmgbuff
maxlev = 5
//1% per lev


i assume you mean something like this as personally i don't want to put a var inside the skill that is altered for extra dmg/bonuses unless i really need to id rather keep whats saved to a bare minimum and having a changing var is extra storage.

Alternatively i can have just one list with a bunch of "passives" which i was planning to do anyway but i was thinking of having those as "general" ones and the ones im trying to decide on are more race/elemental specific ones..

E.g. everyone can have the passive (thats found on its own page) called strup,intup, etc but only a mage can have advanced int studies, which would be a passive shown on the mage's skilltree page. (going for each class having their own page) also intup would not affect any of the skills "unlocking" procedures whereas advanced int studies would (may need it to be lev 2 alongside fireball lev 3 to get Large Fireball)

hope that made sense ;p
In response to Midgetbuster
In your example you are using datums as a technique.
In my example when I refer to skill, it means the ability of the player to use a technique.

A mage might have, destruction magic skill, under which they can use fire, lightning or water techniques or spells- all of which are grouped under "destruction skill magic".


Skill//datum declaration
var
name
skill=5
passive_bonus=5


mob/Player//players or mobs
var
Skill/Flame_Wielding=new//your flame skill.
Skill/Ice_Wielding=new//your skill with ice, and so on.
Skill/Holy_Wielding=new

proc
GetSkillDamage(var/Skill/S)//this is the formula.
var/dmg=S.skill+S.passive_bonus
return dmg

//---
//use
verb
FlameStrike()
for(var/mob/M in get_step(src,src.dir))
M.LoseHealth(GetSkillDamage(src.Flame_Wielding)+rand(5,15))

In the above example, I chose to give each element it's own "school" of magic. Flame spells will fall under flame wielding, and so on.

Also in my example , "skills" aren't techniques(while they can be anything you want)-- but rather a measure of a player's ability to use techniques/spells/skills(whatever you want to call it).

I got the impression that's what you were asking for- reason being you gave the example of having a boost towards using holy magic. If that's not what you wanted then you will have to provide code so that the problem can be understood a bit better(since there's dozens of ways to handle what you are doing tbh).

Judging from your reply it seems like you may just be using datums for all of your techniques/spells. So if you have a bunch of random techniques that aren't affiliated with the skill of the player, that would be where the misunderstanding takes place and also could explain why you are having difficulty setting up a passive bonus / "skill" system.
In response to Midgetbuster
In regards to if you have been using datum/Skills as the actual info for a spell or technique-

The simplest way to add functionality for your skills is to do something like this:

Skill//datum declaration
var
name
damage=5
cost=55
passive_boost=0
proc
Boost()//empty.


Lightning
damage=150
cost=100
Boost()//called when the player chooses to increase this skill
damage*=1.25
cost/=1.5
passive_boost+=2
//boost is overrided for each skill, in this way you can make each skill do
//whatever you want when increased-- or you can write one boost proc that
//does something similiar for each skills if you want.
One way I've used is to keep a list of buffs in the player.
buff
var
stat = "Health"
amount = 0.5
amount_buffed = 0
mob/parent
New(mob/m, stat="Health", amt = 0.5)
if(m) parent=m
else del src
src.stat = stat
amount = amt
proc
buff()
amount_buffed = amount<=1 ? parent.vars[stat]+(parent.vars[stat]*amount) : parent.vars[stat] + amount
parent.vars[stat] += amount_buffed

unbuff()
parent.vars[stat] -= amount_buffed
amount_buffed = 0

mob
var/buffs[0]
proc/add_buff(stat="Health", amt=0)
var/buff/b = new(src,stat,amt)
buffs.Add(b)
b.buff()


Make sure you loop through the buffs and debuff before saving stats.

If you're dealing with a lot of buffs, I'd recommend giving each buff a name, so you can find a certain one in the list.
In response to Albro1
Im already using this Albro for skills that give a buff for a short duration,

What im trying to do is a have a skill that you level that does not cast like the rest but will be checked to see if it exists when calculating damage. Yes i could use the buff system to do this but it would require me basically keeping a permanent buff being saved and loaded constantly where as i only want a skill reference or passive reference (if i have to use a separate list for storing them)

I had this previously in one of my old sources but it required me to make 2 lists inside the mob one for skills and one for passives that was related to the skilltree and it would check both lists when seeing if the skill could be learned or not, i was basically just trying to see if there was a more effective way of making "Permanent Bonuses" without altering stats directly or using the "buff placers" (as they save then i would need to save a reference to the passive)
In response to Dariuc
I see what you are getting at now but i dont think this will help out.

I dont want to have a lot of vars for the skills just sitting in my mobs id prefer to have a list containing links to all my skills then i simply display that list to a grid and voola players can use skills nice and easy, without having verbs to deal with.

with your one i still cant help but think i would have to either change the value of passive bonus for all my skills or search up the skill using a dif proc, to use your method.

I plan to have say 6 skills per class/element and only one of those skills might be a passive for that class/element but it might affect every skill that player has (which could be 10+)

so making sure the passive is easily readable for any skill thats being cast is an absolute must if i decide to put this kind of system in, which i would really like as it gives the skilltree's of players more depth, instead of it just being a lot of skills they can get.
Adds blank buff to a list named Passives, then check for a certain one when you need it.
In response to Midgetbuster
Adding a variable doesn't really add much overhead. I think you may want to reconsider limiting yourself if that is your only motivation for not proceeding with your plans.

Regardless, I'm saying that the first method I described is what is best for accomplishing this in my opinion.

The best way to keep track of the passive bonus' of a "skill" with using certain types of magic is to attach it to the datum -aka the flame wielding example.

Likewise as an example, I have about 300 saved vars attached to players in one of my games and about 5-6 different datum types saved as vars-- when I save my player without any objects or weapons,etc, the save file is around 56 kb.

As far as accomplishing what you want to do-- classifying your skills into groups and giving the player a measure of skill at using said group will fix the problem you are having. (1st example I gave)
In response to Midgetbuster
Midgetbuster wrote:
What im trying to do is a have a skill that you level that does not cast like the rest but will be checked to see if it exists when calculating damage.
You can do this with a list of passives. Why should you have to loop through a bunch of objects/datums to calculate things when your buff system can modify your variables and make calculations easier? Common sense would point to that being the easier and better way.

Yes i could use the buff system to do this but it would require me basically keeping a permanent buff being saved and loaded constantly where as i only want a skill reference or passive reference (if i have to use a separate list for storing them)

So you want a separate system to save your system. While this may be easier in some cases where you need something simpler to save needed information - which is not the case with something as simple as buffs. If you believe your buff system is too complicated then I'd like you to post it so we can try to rectify that.

i was basically just trying to see if there was a more effective way of making "Permanent Bonuses" without altering stats directly or using the "buff placers" (as they save then i would need to save a reference to the passive)

So long as you properly account for debuffing (Especially when saving), directly modifying stats wouldn't be a problem.
In response to Albro1
Thanks for the help guys, What ive got so far is like this.

Warning; This is somewhat a code-wall

skill //these are all the vars defined by my "skill" system.
var
ispassive //not even sure if i need this one in the actual /skill oh well
element
name
cost_health
cost_stamina
list/cost_items
cast_time
base_charge
charging
charge
default_cooldown
cooldown
level = 1

proc //some of these are incomplete and do basically nothing other then return a default value
canuse(mob/human/user)
if(cooldown > 0)
error(user, "Cooldown Time ([cooldown] seconds)")
return 0
else if(user.health < cost_health(user))
error(user, "Insufficient Health ([user.health]/[cost_health(user)])")
return 0
else if(user.stamina < cost_stamina(user))
error(user, "Insufficient Stamina ([user.stamina]/[cost_stamina(user)])")
return 0
return 1

cooldown(mob/human/user)
return default_cooldown //like this one, but it will be updated later.

cost_stamina(mob/human/user)
return cost_stamina

cost_health(mob/human/user)
return cost_health

Use(mob/human/user) //The main proc that i edit for launching of skills

casttime(mob/user)
var/time = cast_time
return time

docooldown(mob/human/user, resume = 0)
if(!resume) cooldown = cooldown(user)

var/list/cards = user.skillview.slots

var/HudObject/current

for(var/HudObject/card in cards)
if(card.name == name)
current = card //need to pull the right hudobject to give a "dulled" olay.

current.overlays -= image('icons/skills/skillcards.dmi', "dull")

if(!cooldown) return

current.overlays += image('icons/skills/skillcards.dmi', "dull")

while(cooldown > 0)
sleep(10)
--cooldown

current.overlays -= image('icons/skills/skillcards.dmi', "dull")

Activate(mob/human/user)
if(!user.CanUseSkills() || !canuse(user)) return
if(user && user.CanUseSkills())
Use(user)
if(!user) return

docooldown(user)

SkillTreeHud //i use this to display the "Skills"
parent_type = /HudBox

var
list/class = list()
list/classtree = list(/skilltree/tests/TestSkill1 = list(160,160), /skilltree/tests/TestSkill2 = list(160,120), /skilltree/tests/TestSkill3 = list(120,80))

tmp
image
img_maxed
img_locked
img_selection
img_available
locked = 0


New(mob/m)
..()

container = m

hide()

box(14,14,"options") //Background


img_maxed = image('icons/skills/skillcards.dmi', "max")
img_available = image('icons/skills/skillcards.dmi', "avail")
img_locked = image('icons/skills/skillcards.dmi', "dull")
img_selection = image('icons/skills/skillcards.dmi', "select")

img_maxed.layer = 100
img_locked.layer = 100
img_available.layer = 100
img_selection.layer = 100

//Begin Skill placing



for(var/a in classtree)
var/skilltree/skilly = new a

var/HudObject/b = add(classtree[a][1], classtree[a][2], "[skilly.name]", icon = 'icons/skills/skillcards.dmi')
class += b
b.name = skilly.name
b.value = skilly

//adjust position 16 from bottom and sides (total 32x32 gap)
pos(16, 16)

show()
..()
Refresh()

//and at last the full skilltree.

skilltree
parent_type = /obj
icon = 'icons/skills/skillcards.dmi'

var
//Change per group
element
class

//Change per skill
ispassive
giveskill //Link to what skill you are giving
lev_max //Max the skill can go to.
cost = 0 //Cost of skill in points
list/reqs_skills //The skills needed before you can unlock this skill list("skill" = lev)
list/reqs_items //Needs this item and amount to unlock list("firebook" = 1)

proc
Action(mob/human/container)
if(!container) return
var/skill/current = container.Grab_Skill(giveskill)
if(!current) //They do NOT have this skill
if(Checker(container)) return //insufficient requirements
current = new giveskill()

if(alert(container, "Would you like to obtain [src.name] for [cost] Skill Points?","Skill Controls","No","Yes") == "Yes")
if(container.skillpoints >= cost)
if(Checker(container)) return
if(reqs_items)
for(var/a in reqs_items)
container.remove_item(a,reqs_items[a])

container.skillpoints -= cost
container.Give_Skill(giveskill)

if(current.ispassive)
for(var/skill/b in container.skills)
if(b.type == current.type)
b.Use(container)

else
container << "System: You do not have the required skill points for [src.name] needs: [container.skillpoints]/[cost]"

else //Skill was found lets level it
if(current.level >= lev_max) return //Already maxed
if(alert(container, "Would you like to level up [src.name] to [((current.level)+1)] for [cost] Skill Points?","Skill Controls","No","Yes") == "Yes")
if(container.skillpoints >= cost)
container.skillpoints -= cost
current.level++
container << "System: You have leveled up [src.name]."

if(current.ispassive)
world << "DBG - Ispassive"
for(var/skill/b in container.skills)
world << "DBG - [b]"
if(b.type == current.type)
world << "DBG - [b.type]"
b.Use(container)

else
container << "System: You do not have the required skill points for [src.name] needs: [container.skillpoints]/[cost]."
container.skilltree.Refresh()


I added in a var in both the skilltree and skill datum called ispassive, also added in some extra lines in the "Action" proc (Noted by the world << "DBG x") that makes it (if its a passive) launch its "Use" proc. which calls the buffing system to add a buff, if you happen to level up the skill it will re-launch use. re-do the buff which all handles itself inside the buff system for the removal of the old buff.

Fairly sure you were both pointing at something like this so i gave it a decent stab to see how this worked out.

As it stands the "buff" or passive placeholder in this case just saves in the buffs list nothing fancy, so for future ones if it has a "Damage" bonus i can just search my buffs list for said buff and add the dmg modifications onto that.

Thoughts?
-Midge

EDIT; Fri 26 Apr 8;47: Doubt anyone is going to post on this anymore but anyways, i just launch the skills "use" proc now and alter a list im going to be using, I think it will be alot more tactical this way as im not using/saving an object.