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

Enemy Param "BONUS" Formulas #55

Closed Roguedeus closed 10 years ago

Roguedeus commented 10 years ago

I was wondering...

Completely removing the option of using Enemy Param Formulas in exchange for Parameter Tables has put me in a rather odd position where changes I had planned for battle member counts, are now impossible that I have decided to use Parameter Tables for the ease of enemy level scaling.

I am able to still use Enemy Param Formulas for all the sparam and xparam needs, thanks to your addition, as they are not YET table based (though it would be really cool if they where).

So I was wondering if it where possible to add the option of assigning the Enemy Param Formulas as BONUSES rather than flat assignments when combined with your Parameter Tables?

It will allow a near unlimited range of conditional customization.

HimeWorks commented 10 years ago

Probably better to modify this script with that functionality http://himeworks.wordpress.com/2013/12/09/parameter-bonuses/

However, note that param formulas accept the "original" value as part of the formula itself. For example

val + 20

Would simply increase the original value by 20. I don't know whether the original value, in this case, is the appropriate value based on its level.

Roguedeus commented 10 years ago

Thats what I was afraid of... I was hoping to be able to do something like this.

Example: I am allowing the player to have up to 5 battle members, with difficulty scaling pretty heavily for each member above 3. Being able to, for example, multiply an enemies MHP by 150% when the $game_party.battle_members.count > 3 would be sweet. Otherwise I am left guessing what an adequate flat bonus would be.

HimeWorks commented 10 years ago
val * 1.5 if p.battle_members.count > 3

Doesn't work?

Roguedeus commented 10 years ago

I was referring to the Parameter Tables assigning the value on scene load.

Example:

Enemy Lv-1 : HMP = 100 ... Enemy Lv-10 : HMP = 550

If I have the Lv-10 enemy default stat in tool set set to 100. And the bonus as val * 1.5 then wouldn't the bonus be 150? When I would actually need it to be something closer to 250?

Unless I am mistaking your comment. I have not used Parameter Bonuses, as that was Actors only.

HimeWorks commented 10 years ago

It looks like enemy param formulas won't be able to accomplish it, since it only uses the enemy's params and doesn't consider the level. It might even crash.

Adapting the parameter bonuses for enemies is a better way.

HimeWorks commented 10 years ago

Parameter bonuses can be applied to enemies now.

Roguedeus commented 10 years ago

Thanks for tackling this so quickly. Though, I am not sure I fully understand how this is supposed to work.

I assume a is Actor and Enemy? Also, I don't see a way to access the current param and then multiply it by 150%.

This causes an infinite loop.

   <param bonus: mhp>
     a.mhp * 1.5 if $game_party.battle_members.count > 3
   </param bonus>
Roguedeus commented 10 years ago

Other then that snag, it works great.

I attempted to stare and compare your script, to see if I could hack in a way to accomplish what I need, but you are using syntax thats foreign to my relatively ruby-noobie eyes. :p

I am still digesting the various ways method parameters can be used and default method types.

When I see you do stuff like this:

  def value(a, p=$game_party, t=$game_troop, s=$game_switches, v=$game_variables)
    eval(@formula)
  end

and

    def eval_param_formula(formula, val, a, p=$game_party, t=$game_troop, v=$game_variables, s=$game_switches) 
      eval(formula)
    end

I am left with big ? in my head...

There is still sooo much about Ruby I have no clue of.

HimeWorks commented 10 years ago

Yes, a now refers to the current battler.

I've updated it to support the base parameter of the current parameter type. For example, if you write

<param bonus: atk>
  val * 1.5
</param bonus>

That will give you a 50% bonus to the current atk. The reason why accessing a.atk causes an infinite loop is because that includes the bonus. That can be changed though.

Roguedeus commented 10 years ago

Checking it out now! :)

HimeWorks commented 10 years ago

I've updated the script to support "recursive" references. It is not really recursive though

<param bonus: atk>
  a.atk * 1.5
</param bonus>

This will now increase the pre-bonus atk by 50%.

The trick is simple: use a temp flag

return 0 if @check_param_bonus
@check_param_bonus = true
val = param_bonus_object.param_bonuses.inject(0) {|r, obj|
   obj.param_id == param_id ? r += obj.value(self, param_base(param_id)) : r
}
@check_param_bonus = false
return val

I'm going to remove the val argument though because...I don't see a point to it.

Roguedeus commented 10 years ago

It may be better to keep the val argument for consistency. I use multiple of your param scripts and having some of them use val, and others use more natural references may confuse me and others. :p

HimeWorks commented 10 years ago

It is a relatively new concept that I introduced in my formula related scripts, and it is rather inconsistent. For example, enemy gold formula uses gold for the base value, enemy exp formula uses exp, and parameters use val for some reason.

Of course, you can't really compare them because they are separate things. for parameters, I think I introduced val only for the purpose of accessing "base" value (that is, excluding equips and other bonuses), but there may be a better way to handle that.

I would rather introduce a bunch of aliases like base_atk, base_def, and so on rather than use an ugly thing like val

Roguedeus commented 10 years ago

Ah. Do you plan to add the recursive block to your other param scripts? Like Enemy Param Formulas?

Roguedeus commented 10 years ago

I think I may have run into an issue... Making sure.

HimeWorks commented 10 years ago

It looks more complicated to add that kind of thing to enemy param formulas.

Roguedeus commented 10 years ago

It may not be an issue, but if for some reason the formula used for the bonus, doesn't return a bonus value, it errors.

This caused a crash error when the party was 3 or less.

  <param bonus: mhp> 
    a.mhp * 0.5  if $game_party.battle_members.count > 3
  </param bonus>

This fixed it.

  <param bonus: mhp> 
    if $game_party.battle_members.count > 3
      a.mhp * 0.5 
    else
      0.0
    end
  </param bonus>
HimeWorks commented 10 years ago

Right, that is how I intended it to be done because I don't specify a default value.

Roguedeus commented 10 years ago

This seems to fix it.

Nope
Roguedeus commented 10 years ago

I am trying to wrap my head around the way eval works... And the way you use it so effortlessly.

HimeWorks commented 10 years ago

That's what I mean to return a default value.

eval takes a string and evaluates it as regular ruby code. For example, when you say

a.atk * 2

If you think of it as regular ruby, a is a variable, atk is a method access, and the rest is just normal math. When you say eval, all of the variables in the scope of the call are available, so when I expose a to the method, I can pass whatever object I want as an argument and let the eval use it.

Roguedeus commented 10 years ago

I really don't understand why its so difficult for me to grasp logic loops and recursive math sometimes but I am having one of those moments while looking at your method here:

return 0 if @check_param_bonus
@check_param_bonus = true
val = param_bonus_object.param_bonuses.inject(0) {|r, obj|
   obj.param_id == param_id ? r += obj.value(self, param_base(param_id)) : r
}
@check_param_bonus = false
return val

Can you explain how this method ever ends with @check_param_bonus as true?

Roguedeus commented 10 years ago

Unless, it shouldn't unless its calling itself?

/facepalm

Now thats something I haven't seen before!

You know, I have tried using similar to those kinds of things in the past, with Java, and had issue with the variable not updating fast enough to prevent the repeat the first few times... Maybe its in Ruby's slow nature that it evaluates every variable before its use that allows this to work so well???

HimeWorks commented 10 years ago

It's just Ruby's syntactic sugar.

do_something if some_condition

is equivalent to

if some_condition
   do_something
end

nil resolves to boolean false, so the first line doesn't do anything unless you actually set it to something that doesn't resolve to false.

It has basically nothing to do with whether the language is fast or not. If the code is executed in sequential order (which it does), then it will work as expected.

If you are working with a multi-threaded application, then you may run into cases where one thread checks it before the value is properly modified.

Roguedeus commented 10 years ago

Its the idea of a class variable (in this case an instance variable) being toggled in time to be caught by the loop that made me remember it. It was so long ago I am likely messing up the circumstance. :)

As for your explanation regarding eval...

How would eval know its referencing itself with a?

  def value(a, p=$game_party, t=$game_troop, s=$game_switches, v=$game_variables)
    eval(@formula)
  end

I see thats what its doing here:

class Game_Enemy < Game_Battler

  def param_bonus_object
    self.enemy
  end
end

But I am failing to grasp how a is anything more than a blank parameter?

Roguedeus commented 10 years ago

Nevermind... I got it.

obj.value(self)

Roguedeus commented 10 years ago

I am extremely slow sometimes... But once I get something, I tend to surprise people. :p