Hime-Works / Requests

Bug reports and requests that may require longer discussions and is not suitable to leave on the blog
http://himeworks.com/
GNU General Public License v2.0
7 stars 9 forks source link

Normal attack element modifier #149

Closed HimeWorks closed 10 years ago

HimeWorks commented 10 years ago

http://www.himeworks.com/2014/01/03/attack-element-modifiers/

This script basically assumes elements are based on the item's elements and doesn't take into consider features.

So if you had a weapon that had a fire attribute, and the skill was a "normal" element (which means it uses the elements defined by features), it isn't applied.

The problem is that while a skill can say it does 50% fire and 50% physical, how do you say that a weapon does 50% fire and 50% physical? What if you generalize it to an arbitrary number of features?

Roguedeus commented 10 years ago

I was going to create a plugin for your script later on when I needed weapons to matter. But my current project has skills learned via body parts (equipment) thus I skip most of the common expectations of weapon elements making any difference.

I forgot to mention that I scrapped Element-Ex for your script last week... It was much less of a headache keeping things straight. I plan on expanding it later.

I'm sure its obvious, but I figure I'll mention a few immediate examples.

I'd assume that any skill that uses normal attack elements would simply assume the weapons elements. Otherwise I can see it being simple, or more complex.

Simple

Get the damage total divide it by the number of element sources, and apply each sources element split to its fraction of the total.

Complex

Create arrays for each source, sum them, and divide by the source count.

Elements: 0=Phys, 1=Fire, 2=Ice, 3=Shock

Skill [50,50,0,0] = 100
Weapon [0,50,50,0] = 100
#---------------------------
Total [50,100,50,0] = 200
[50,100,50,0] * 0.5 
Result [25,50,25,0]
Skill [25,50,0,25]
Weapon [0,50,50,0]
#---------------------------
Total [25,100,50,25]
[25,100,50,25] * 0.5 
Result [12.5,50,25,12.5]

This assumes that each additional source totals an additional 100%.

Note

The complex version results in the same total value, per element, but it allows for further manipulation of each element with adjustments from equipment. Such as +50% Fire Damage [12.5,50,25,12.5] = [12.5,75,25,12.5], etc...

Its likely that I have missed something though. I am not good at imagining numbers.

HimeWorks commented 10 years ago

Hmm I think I get what you mean. I would implement it as such.

First, I take my note-tag and extend it to all feature objects. That is, I extend "attack element rate" to all objects. As discussed before, the default element rate feature is in fact the elemental resistance rate, so 80% fire element rate means you only take 80% fire damage.

Therefore, to avoid confusion, I will say attack element rate to specifically mean the percentage of the damage that is of a particular element type.

My skill has a "normal" element atk which means it pulls its atk elements from actor features. My weapon has attack element rates 30% fire and 70% physical. Therefore, atk element rate is simply 30% and 70% as usual.

HimeWorks commented 10 years ago

Let's make things more complicated with different atk element rates. Now suppose I am dual-wielding, and I'm attacking an enemy that is immune to physical damage.

Suppose my first weapon does 30% fire and 70% physical, and inflicts 100 base damage Suppose my second weapon does 80% fire and 20% physical, and inflicts 100 base damage.

Based on elemental immunity, the first weapon should only deal 30 damage, and the second weapon should deal 80 damage, for a total of 110 damage.

So you add the rates together and divide them by 2. Fire: (30% + 80%) / 2 gives you 55% Physical: (70% + 20%) / 2 gives you 45%

Doing the math, your total damage is supposed to be 200: 55% of it is fire, which gives 110, and 45% of it is physical, which gives 90.

The enemy would resist 90 damage as expected, leaving you a total of 110 fire damage. I am kind of curious why adding it up and dividing it by the number of sources actually works but both of our examples show that the math adds up.

I wonder if there's something missing...

HimeWorks commented 10 years ago

Let's say you had a guy that naturally emits fire, and everything he touches is fire elemental. You would note-tag the actor with an atk element rate of 100% fire. This means that EVERY skill he uses that has "normal" element type, will deal 100% fire damage.

The "atk element" feature would basically be replaced with my custom "atk element rate" data.

Roguedeus commented 10 years ago

Sound good to me. I am thinking the only reason that the numbers work out is that we are working with (100% * sources) / sources. If one of the sources came out to more than 100% it would wreck the numbers a bit. (I am guessing)

This would then require any bonuses to be applied after factoring the totals for each element. It may also require them to be a new note-tag.

HimeWorks commented 10 years ago

It definitely works with rates that are equal or less than 100%

If one weapon does 50% fire damage, and another weapon does 50% fire damage, then you expect the total fire rate to be 50% as well, because it's impossible that your final fire damage is greater than 50%.

If two weapons deal 100% fire damage, you expect your damage to be 100% fire damage.

If one weapon does 100% fire damage, and another weapon does 200% fire damage...now there is an issue. Because what does it even mean to deal 200% fire damage? For me it is difficult to reason with a concrete example what that means.

Roguedeus commented 10 years ago

I assumed the only issue would be totals above 100% for a source. :)

Right now the original script assumes that a total of more than 100% is a form of damage bonus. Though its moot considering that the skill itself is giving itself the bonus. Thus any update should consider a solution for desired bonuses granted by items. I gave such an example in my first feedback above.

Roguedeus commented 10 years ago

I'm looking at your updated script and I want to make sure that's your final solution?

No plans to support bonus element damage? No plans to allow for equip elements to mix with skill/item elements? (Other than the normal selection)

HimeWorks commented 10 years ago

Not sure how bonus element damage would be specified. If you specify that a skill is "Fire" type, under what circumstances should equip element rates mix with it?

HimeWorks commented 10 years ago

Oh, here's a simple example: Fire Ring that increases all fire-elemental damage by 20%. element_rate is the resistance rate, so we can't put it there.

The current logic doesn't provide any room for adding bonuses either. I would need to provide some abstraction for calculating the "atk element rate".

When you note-tag a weapon with <attack element: fire 0.5>, that means you do 50% fire damage. This is the "base" attack element rate. I would then provide a method that allows you to operate on this value, so you can create your own scripts that will increase the element rate.

Roguedeus commented 10 years ago

I would just add the skills element assignment to the equipment based mix used for normal attack element. But add a note tag with the option to override equipment assignments in mass or individually. (In the case of a special exclusive skill)

Example: If a 'Fire Ball' skill has 100% Fire Damage, and the caster is wearing a special Ice Wand that grants <attack element: ice 1.0> then the skill would have 50% Fire and 50% Ice. However if the 'Fire Ball' skill had normal attack element, then it would do 100% Ice damage from the wand, and without the wand it would do Physical or any other type... Defeating the idea of a 'Fire Ball'.

If the dev wanted the Fire Ball to be element exclusive, a <element exclusive> tag (or some such) would keep it only reacting to FIRE damage adjustments (or element splits assigned only to the skill).

Thus an <element adjust: fire +0.5> would make the 'Fire Ball' equivalent to 150% damage, with only fire element.

If I end up writing a plugin I would separate every element into an array and then recombine after adjustments.

HimeWorks commented 10 years ago

Then the fireball skill should just be Fire element.

Roguedeus commented 10 years ago

Exactly. Without any mixing with gear supplied elements. I thought we where looking for ways to mix skill elements assignments with gear/state assignments.

In the default mechanics the most effective of all available elements is used to apply the damage. In our solution to mix elements we have actually broken the ability for someone to 'add' an element to their attack during battle. (Unless the skill uses normal attack element.)

HimeWorks commented 10 years ago
In the default mechanics the most effective of all available elements is used to apply the damage.

That only applies to the normal attack element, which is what the "normal attack element" update does.

If you specify None element then it ignores elemental modifiers. If you specify a particular element, then it uses that.

I don't see a reason to mix skills with equips or other things. My fire ring example shows how bonuses could be applied to a fire-type skill or a normal-type skill that inherits fire elemental attacks, but those are separate bonuses and is not "mixing" skill elements with equip elements.

Roguedeus commented 10 years ago

Damn, I hadn't noticed. I stand corrected. ;)

I suppose you're done then? (With updating your script)

HimeWorks commented 10 years ago

Still have to provide some logic for those bonuses, since the current calculation provides no way to include them.

It will be an internal change for the most part, where I added a method or two to compute the "final" element rates, which is basically taking the base rate (specified by the skill or other stuff) and adds bonuses to it.

Roguedeus commented 10 years ago

I had always worked towards what I described without considering that skill element assignment was the immutable part of the formula.

When you are done I think I'll shift my plugin towards a gear focused exception. So if an item is flagged then it infiltrates, or overrides a skills assignments conditionally.

Roguedeus commented 10 years ago

I think your last update expanding the role of equipment on normal element attacks, broke state element rate application somehow. I didn't have time to test exactly how, but reverting to your previous version fixed the problem...

HimeWorks commented 10 years ago

"State element rate application"?

Roguedeus commented 10 years ago

Any States, element rate application. They don't factor in the results anymore.

Normal: item_element_rate

  #--------------------------------------------------------------------------
  # * Get Element Modifier for Skill/Item
  #--------------------------------------------------------------------------
  def item_element_rate(user, item)
    if item.damage.element_id < 0
      user.atk_elements.empty? ? 1.0 : elements_max_rate(user.atk_elements)
    else
      element_rate(item.damage.element_id)
    end
  end

New: item_element_rate

  alias :th_attack_atk_element_modifiers_item_element_rate :item_element_rate
  def item_element_rate(user, item)
    if item.damage.element_id < 0
      normal_atk_element_modifiers(user, item)
    else
      # Check if it uses multiple elements
      if item.atk_element_modifiers.empty?
        return th_attack_atk_element_modifiers_item_element_rate(user, item)
      else
        return item_multi_element_rate(user, item)
      end
    end
  end

Where are self's element rates collected and applied for normal element skills?

Roguedeus commented 10 years ago

Maybe I am misunderstanding something, but a defenders element rates only seem to be correctly factored in skills (not splitting their elements) that have an explicit element assignment in their item.damage.element_id

Roguedeus commented 10 years ago

I don't know why I have so much trouble visualizing code past the first few steps of abstraction but the way you reference element type names as keys and total a value has thrown me for a loop and I can't seem to figure it out.

Maybe I am just losing my patience...

Every time I think I am done coding another fucking quirk in the mechanics tosses me back into it, and my brain has trouble toggling from code to content in between sleep cycles.

/grumble

I am pretty sure that the issue is the way that normal_atk_element_modifiers only processes the targets element_rate for elements it finds in the note tags. Thus no note tags makes normal_atk_element_modifiers return 1 all the time. Skipping the targets element rates.

If the item has a defined item.damage.element_id with no note tags then the default item_element_rate is used without issue.

I have not gotten to item_multi_element_rate...

HimeWorks commented 10 years ago

Actors, classes, weapons, armors, states, and enemies are called feature objects, and there is a method defined in Game_BattlerBase called feature_objects that returns an array of all these things.

normal_atk_element_modifiers returns the final elemental multiplier for a "normal" element,

  def normal_atk_element_modifiers(user, item)
    count = 0
    rates = {}
    user.feature_objects.each do |obj| # <---------
      next if obj.atk_element_modifiers.empty?
      count += 1 
      obj.atk_element_modifiers.each do |id, val|
        rates[id] ||= 0
        rates[id] += val
      end
    end
    return 1 if rates.empty?
    return rates.inject(0) do |r, (id, val)|
      r + element_rate(id) / count * val
    end
  end
Roguedeus commented 10 years ago

I don't begrudge you your assumption that I am the one that's wrong whenever you don't immediately grasp what I am trying to explain, because I have made some pretty stupid mistakes.

Though it would often help a lot more if you where less cryptic in your responses when I make them. And a bit less quick to assume that I have made another.

You can start by trying to keep in mind that this is all really new to me and sometimes I simply don't know how best to explain what I find. And when I do, its ALMOST ALWAYS after I have already spent some time FAILING at figuring out how to fix it myself. Thus I am impatient and often very frustrated while I type it up.

For that I apologize.

...

I tried to point out that the SELF element rates are not being applied to the element rates found on the USERS feature objects, because your loop SKIPS any feature object that doesn't have a note tag, and then returns 1 when the rates are empty as a result.

 def normal_atk_element_modifiers(user, item)
    count = 0
    rates = {}
    user.feature_objects.each do |obj|
      next if obj.atk_element_modifiers.empty?      #<------- THIS
      count += 1 
      obj.atk_element_modifiers.each do |id, val|
        rates[id] ||= 0
        rates[id] += val
      end
    end
    return 1 if rates.empty?                        #<------- AND THIS
    return rates.inject(0) do |r, (id, val)|
      r + element_rate(id) / count * val            #<------- ENDS UP SKIPING THIS, when a normal element attack has no (atk_element_modifiers) note tags.
    end
  end

I really hope I am not wrong because if I am, I am going to feel really stupid.

Roguedeus commented 10 years ago

I don't mean to be a pain, and I know that you don't have to do what you do here. You have always been rather quick with helping when I needed it and I don't want to give you the impression that I am ungrateful. I realize that I can be real thick headed and argumentative when I think I know something and even worse when I just spent the better part of a day (or more) beating my head against the wall on something that seems to be simple, and probably should be, but for some reason I just can't see it.

So when you answer my questions with questions, or give me an answer I didn't ask for (even if its the right one, and I just don't know it) I might bite your head off or get rude without thinking.

I hope you don't hold that against me. Because believe me when I say that you have been the ONLY reason I have managed to get as far as I have and I have nothing but appreciation and heart felt thanks. My personality flaws just sometimes get the best of me.

HimeWorks commented 10 years ago

So you're saying if my enemy has a 50% fire element rate (eg: takes half damage from fire) and I have a skill with normal element, but none of my objects have an atk element rate, then the modifier is not calculated correctly because I don't take into consideration the target's elemental resistance?

I'm not sure why the target's element rates are relevant when the user has no atk element rates. It is just assumed to be a null element attack, which means there is no elemental modifier.

Roguedeus commented 10 years ago

I never said that the user had no element rates. I said the user had no note tags. The user has normally assigned element rates. (in this case the PHYSICAL element is assigned to an enemy and the class of the target) But the skill has normal attack and because there are no note tags associated with the element assignment (in the enemy object), its never checked against the targets element rates.

And you are up late. :)

HimeWorks commented 10 years ago

What is the atk element rate supposed to be if you just use the atk element feature?

Roguedeus commented 10 years ago

I always assumed that unless you are dividing it up, you are still using the feature assignment because that's how it worked before you updated the normal attack factoring. I would think that I'm not the only one that would assume that, without it spelled out in big neon letters.

However, if you wanted to support feature element assignments on top of note tags, I wouldn't think it to unreasonable to assume that multiple element features on the same object are split divisions factored into the total with other feature object splits, as the script currently does.

It shouldn't mess anything up, and note tags would always be checked for first. Thus like it functions now, if note tags exist then those are the only thing checked.

HimeWorks commented 10 years ago

Is there any reason why you need the atk element feature instead of just using atk element rate note-tags, which basically does the same thing except

  1. They're not features
  2. They come with rates
Roguedeus commented 10 years ago

No reason besides legacy support.

You are big on not breaking default function so I made an assumption that default feature assignment still worked. Like it did before the update.

If you would prefer not to support it then I big disclaimer indicating that this breaks normal feature assigned elements for non-skills might help avoid the same confusion from others.

HimeWorks commented 10 years ago

The decision was made based on that some things are simply not worth providing backwards compatibility for, especially if it doesn't do things very well in the first place.

A consistent note-tag is more important than being able to piggyback on something that sort of tried to accomplish something.

I guess a disclaimer would be more clear than not mentioning it at all.