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

State Elements... #285

Closed Roguedeus closed 8 years ago

Roguedeus commented 9 years ago

We have Element Damage... And UsableItem Effects, like add state, etc... But nothing combined.

What if, a stun wasn't just a stun, but an ICE stun? All rates are effected first by element resistance, and then by effect resistance. Thus a target 50% resistance to ICE, and 50% Resistance to Stun, has a 25% total resistance against ICE Stun.

This is just a simple example. But it would make certain effects able to be applied much more liberally, when something Immune to an element, is by extension also immune to all that elements linked effects.

Meanwhile, an EARTH Stun, will work fine... etc.

Elements are purely cosmetic. It would be linked to element ID's and thus can be easily extended.

Now... How would the easiest way to implement it be?

If the effect is a state, the note tag could be on that state... But then, it would require every state of every element to be individually defined. What if the link were made on the UsableItem?

Thus, only one state is needed, and can be applied under different circumstances. Kind of like conditional effects.

example_2 This would mean that each blind effect could be assigned a different element ID dependency. Or the same dependency, under different conditions... etc...

Roguedeus commented 9 years ago

Sounds good.

I would add, how recipes are defined.

Personally, I prefer the option of having recipes independent of the item, but result in the item. Thus you could have 'x' different recipes for the same item.

Roguedeus commented 9 years ago

I am applying casting disjunction to spells, and adding a few new battler parameters to the default process. My first is:

cst = cast (equivalent to hit for non-spells)

It will offset mev the way that I have hit offsetting 'eva' now...

Essentially every action is 100% successful, unless the target counteracts it. This includes critical hits! Every attack is a critical hit, and every target is 100% cev. Thus +10% cri is effectively a -10% cev for the target, and +10% cev is essentially -10% cri for the attacker.

In essence I am making most advanced parameters direct offsets of their opposite. This way, as battle scales, results DONT unless there is a great disparity between battler parameters. Such disparity can be engineered via buff and debuff effects.

...

Disjunction occurs when a spell caster is damaged... Not to be confused with being hit, as armor normally prevents damage.

Disjunction is a progressive state that graduates to a higher state when re-applied, and reverts to a lower state, as it wears off. The higher the state, the more likely a spell will fail when cast.

Spells will have their own 'disjunction' resistance. As will battlers of specific battle classes... Meanwhile, hybrid casters will likely suffer greater disjunction effects. Thus keeping your caster armored, and protected, is SUPER important.

This should prevent most 100% caster, party compositions. ;)

...

This is all part of the removal of default RNG to ability use.

All actions, against a defenseless target, are 100% effective. The challenge is in determining your targets defenses, and counteracting them, in order to maximize the effect of your actions.

Roguedeus commented 8 years ago

I am about 90% done with the core of my Battle_v3 script... Oddly, minus the GUI (which I have not decided on yet) it ended up being a bigger pain than re-writing my armor script. Mainly due to the odd issues I had to rediscover with Theo's Side Battle script.

One great thing is that this fatigue system is SAA-WEET!! Like WOW... I am about giddy with ideas on how to utilize it now that its done and I can see all the applications.

Weapon size, and spell complexity can now have DIRECT impact on the longevity of a battle, via fatigue. Blow all your big hits early and fail yo defeat your targets, you might find yourself at a growing disadvantage due to fatigue... Etc...

I am also day dreaming ways of implementing a subdue mechanic utilizing fatigue! Possibly an Advanced Fatigue plugin that causes issues if you accumulate to much... Etc...

Also making a Battle tactics plugin with 2 new standard actions (like Attack) and a re-design of how counter attacking works.

Not to mention half way done with a new Advanced Elements Script....

I have code flying out my ass these days. It feels good!

HimeWorks commented 8 years ago

Success provides motivation. Good techniques probably helps makes things smoother. On Sep 14, 2015 5:27 PM, "Roguedeus" notifications@github.com wrote:

I am about 90% done with the core of my Battle_v3 script... Oddly, minus the GUI (which I have not decided on yet) it ended up being a bigger pain than re-writing my armor script. Mainly due to the odd issues I had to rediscover with Theo's Side Battle script.

One great thing is that this fatigue system is SAA-WEET!! Like WOW... I am about giddy with ideas on how to utilize it now that its done and I can see all the applications.

Weapon size, and spell complexity can now have DIRECT impact on the longevity of a battle, via fatigue. Blow all your big hits early and fail yo defeat your targets, you might find yourself at a growing disadvantage due to fatigue... Etc...

I am also day dreaming ways of implementing a subdue mechanic utilizing fatigue! Possibly an Advanced Fatigue plugin that causes issues if you accumulate to much... Etc...

Also making a Battle tactics plugin with 2 new standard actions (like Attack) and a re-design of how counter attacking works.

Not to mention half way done with a new Advanced Elements Script....

I have code flying out my ass these days. It feels good!

— Reply to this email directly or view it on GitHub https://github.com/Hime-Works/Requests/issues/285#issuecomment-140210559 .

Roguedeus commented 8 years ago

The preview I gave of the new mechanics isn't completely accurate anymore. ;)

I think my small change is actually better. I might be making a blog post in a few days. Once its 100% complete.

Roguedeus commented 8 years ago

I'm thinking of allowing consumption of food and water, out of battle, to quickly remove fatigue. ;) edit: And possibly apply a state that helps remove fatigue for some time after eating. That way staying well fed, allows for better fatigue control!

HimeWorks commented 8 years ago

How long is the fatigue script?

I'm thinking something like this

class Game_Battler
   attr_reader :fatigue

   def initialize
       # alias stuff
       @fatigue = # something
   end
end

And everything else is plugins lol

Roguedeus commented 8 years ago

Yeah. Most of the code is reading fatigue data from objects, plugging fatigue into the actual battle math, and factoring in different rates for different activities.

I want some actors to process fatigue differently when doing activities that complement their normal skills, than when doing things off role... etc... which mirrors reality a lot.

There is also an offset for level difference between battlers. Fighting higher level enemies causes more fatigue. Etc...

    LEVEL_DIFF_ROLLOVER = 0.5
    #The rate applied to BASE fatigue due to level difference between attacker
    #and target. It can be extended as far as you desire.
    LEVEL_DIFF_RATES = {
      0 => 1.0,
      1 => 1.1,
      2 => 1.2,
      3 => 1.5,
      4 => 2.0,
      5 => 2.5,
      6 => 3.0,
    }
Roguedeus commented 8 years ago

The only reason its part of the Battle_v3 core, is its so invasive in the math. I figure I put it where it needs to be while I am completely overwriting the methods... Using the new code as easy access for future plugins.

edit: Fatigue and Parameter offsets are the largest part of replacing the default RNG of the battle system.

edit2: I am thinking I may have to create an entirely new page for the status menu, with nothing but fatigue stats... ;( It will be that important, if you wish to min/max your game. Or you can just ignore it, and eat food, etc, whenever you need to. I doubt many people ever really eyeball the expanded status screen anyway.

This is why I am still unsure how much GUI work I'll need. I will need to do a ton of play testing, and get feedback, to know what info to share and what to hide.

Roguedeus commented 8 years ago

Battle_v3 - Core is completed... With a few things I had not planned to include. Simply because it was prudent.

As it stands, all item effect rates and fatigue are offset via actor and target level difference. Maneuvers are any battle action (offensive or defensive) and their results are offset via current total fatigue and applicable parameter differences.

In order to achieve a good diminishing return, parameter based offsets are sqrt divided by sqrt.

The No.1 issue I had was re-discovering compatibility issues with Theo's scripts.

Roguedeus commented 8 years ago

Lions share of the advanced element script is done... All element related data for an attack is stored in data classes and re-collected every attack for easy access by other scripts. Including Armor_v3 and Battle_v3... Also, to include aptitudes and State Elements (next on the list)

Elements can now be biased by rates or flat damage bonuses. All applicable resists included. Going to add popup support next.

Roguedeus commented 8 years ago

Am I missing something obvious here?

This method errors:

  def attack_elements
    return @has_element.inject([]) {|x,(element, applicants)| x << element if applicants > 0}
  end

this does not:

  def attack_elements
    temp = []
    @has_element.each {|elem,count| temp << elem if count > 0}
    return temp
  end

Am I using inject wrong? I have done similar with arrays... Without errors.

Roguedeus commented 8 years ago

State Elements are done... Now an actions element is considered when applying a state, in exactly the same way that it is considered when applying attack damage.

It utilizes the same basic methods from my Advanced Attack Elements script, and the methods I added for enemy level offsets on effect application.

In essence, there are now up to half a dozen or so 'situational' adjustments made to every action. Removing the need for base success.

Roguedeus commented 8 years ago

I understand WHY developers rely on base success and onerous RNG... It is quick, and a reasonable abstract for exactly what I am coding. BUT it is also lazy, and punishing of player ingenuity. Not to mention, frustrating as all hell...

I like the idea that my CHOICES are the primary determining factor in my success, not a random number generator. ;)

HimeWorks commented 8 years ago

For your inject issue, inject works by recursively calling itself and applying the operation to the result eg:

[1,2,3].inject(0) {|r, num| num + r}

Is equivalent to

num1 + (num2 + (num3 + r))

On the first call, r is equal to 0

num1 + r    # 1 + 0  --> 1

On the second call, r is equal to 1

num2 + r    # 2 + 1 --> 3

On the third call r is equal to 3

num3 + r    # 3 + 3 => 6

And now it returns r.

So on every iteration, r gets passed to the next iteration. But what happens if you don't pass anything?

Start with this

[1,2,3].inject(0) {|r, num| p [num, r] ;num + r}

It will print out

[1, 0]
[2, 1]
[3, 3]

But if you decide to return a nil instead...

[1,2,3].inject(0) {|r, num| p [num, r] ;num + r; nil}

Now you get

[1, 0]
[2, nil]

And it crashes. This is also called a fold operation.

Roguedeus commented 8 years ago

Thanks for the explanation!

Roguedeus commented 8 years ago

It just occurred to me that with this State Elements script I can essentially asign non-damage damage elements that are used purely for effect dynamics.

A spell that causes no actual damage, but places an effect, can be assigned arcane damage element and be subject to arcane resistances. Plus the way I coded bias and bonus factors, multi-element effects can require multiple stages of resistance applied.

Consider an 150% arcane effect that also happens to be 150% Fire based. (300% / 2 = 150% rate) A 100% Fire Resist would merely drop the application rate to 75%. (150% / 2) Thus it would need separate arcane resistance to mitigate it further.

It creates a large swath of potential interactions. The target would have to resists both, to prevent the effect.

edit: Also, I could simply create a certain element type that guarantees unhindered rates. Much like a certain hit type guarantees skill effect. (independent of PDR and MDR)

The above example, could then be 100% Certain and 100% Arcane and always have a 50% minimum chance of application.

Roguedeus commented 8 years ago

Just noticed an error in my math... But when I fix it, I am getting a NaN error...

I am looking at the code, and I do not see where I am multiplying an infinite.

Roguedeus commented 8 years ago
  def total_element_rate
    rate = 0.0
    bias = 0
    @game_elements.each {|element, detail|
      next if element.empty?
      rate += ((detail[:rate] / [@has_element[element], 1].max) * defender_element_data.element_defense(element))
      bias += detail[:bias] 
    }
    return rate + bias
  end

Everything has default init values of 1.0 or 0... None are Boolean. Yet I am returning a NaN value.

/boggle

Roguedeus commented 8 years ago

Not sure what I did... Took a break, came back and split it up... Now it works... (It includes a stupid math fix)

  def total_element_rate
    rate = 0.0
    bias = 0
    atk_rate = 0.0
    def_rate = 0.0
    @game_elements.each {|element, detail|
      next if element.empty?
      atk_rate = detail[:rate]
      next if atk_rate == 0.0
      def_rate = defender_element_data.element_defense(element)
      rate += ((atk_rate / [@has_element[element], 1].max) * def_rate)
      bias += detail[:bias] 
    }
    return (rate / attack_elements.length) + bias
  end

I don't see it...

Roguedeus commented 8 years ago

Usable items rescued eval just made me feel like I was losing my mind...

I would laugh if I wasn't so irritated.

Roguedeus commented 8 years ago

DOCUMENTING!!... Documenting............ Doc.... u.... ment... ing.....

edit: My scripts are getting to where I need to keep good use notes just to remember what I added already.

Roguedeus commented 8 years ago

In the above issue, is it because I am potentially dividing zero? (Not by zero).

HimeWorks commented 8 years ago

I'm not too sure about NaN. The only thing that comes to mind is when you try floating point division 0.0 / 0 or something. As opposed to 1 / 0.0 which gives you Infinity

Roguedeus commented 8 years ago

Ok. That makes sense then.

Roguedeus commented 8 years ago

On today's list... Complete Battle_v3 - Item Effects Adding standard effects... Something like 'Normal' attack states, only they are effects that are applied to all actions. Including Pre and Post application. Also going to throw in user targeting effects while I am in there.

I don't think I'll need to replace your User Effects script as that can still apply to normal database assigned effects. My addition is note-tag assigned effects. I am considering piggy backing on your Pre and post effects arrays somehow... Or maybe I'll let your script be the PRE-Pre effect and POST-Post effect script... ;)

Roguedeus commented 8 years ago

Yesterday was a roller coaster.

It seemed every time I turned around, some bit of code I had added before the weekend came back to haunt me. Suddenly, all my evals were broken (I had a bad hash key), guard was gobbling up 100% of dealt damage (I was not rounding floats), and assigned element types were almost completely broken (I had mixed up the collection loop)... Heh..

They were all, very simple oversights. Relatively easy to fix once I found out what they were. Of course, since I didn't notice them until two or more days after causing them, it was that much harder to figure out what the culprit code was.

On the bright side, Item Effects are almost done... Almost.... :)

Oh yeah... AND I finely fixed a lingering math issue with element rates.

Roguedeus commented 8 years ago

Whats cool about the common effect script, is that I can attach them to feature objects and have them applied to all actions (with conditions) or just attach them to the usable item and have fully conditional effects. Without worrying about the database assigned effects at all.

  #------------------------------------
  EXAMPLES:
  #------------------------------------

    This note tag will cause a debuff to the attackers DEF any time one of 
    their physical attacks misses its target.

      <common_effects>
        target_user
        effect: code: 32 id: 3 value1: 1.0 value2: 0
        cond: e.physical? && !r.hit?
        select: post
      </common_effects>

edit: You can apply more than one effect in a note tag. But they need separate ones for separate options.

Roguedeus commented 8 years ago

I remember when I first started using RMVXA and looking at the ruby code thinking there was simply no way I could understand it... I poked and poked until I got some of the simplest things to work.

My brain had a hard enough time with static typed compiled languages like C#, trying to make sense of Ruby was brain bending. I had to think in code, rather than trace the types and declarations.

Now... When I look at scripts that used to give me head aches trying to follow, I can only see how I might improve it, or wonder why they did a thing a specific way. Like a lot of Yanfly's stuff.

Then there are still some scripts you've written that I can't follow for some reason... Maybe I just give up to easily. ;)

HimeWorks commented 8 years ago

Personally I would prefer using a tagging systems, and assign effects to each tag. Then you can mix and match tags to achieve various things.

Roguedeus commented 8 years ago

It sounds great... But it would require a lot of thinking on how to include conditions, selections, and targets... All built into the note tag I am using.

HimeWorks commented 8 years ago

How complex are each of those things going to be?

Roguedeus commented 8 years ago

Most feature object assigned common effects will require a selection and a condition. Otherwise doing something like guarding or using an item will trigger it.

Selections are for sorting. Like only grabbing the effects assigned :preempt selection for preemptive effects that are applied before item_apply processes the item. Etc...

Conditions are as complex as you want... The example I gave is rather simple.

Flagging target_user is equivalent to your user effects script. But with the added bonus of built in conditions and not having to count database effects.

Roguedeus commented 8 years ago

I understand that you could conceivably define a tag for every combination... But that would get rather cumbersome. Or at least I can't easily think of an intuitive solution.

Roguedeus commented 8 years ago

293 Hmmm... Might it be possible to use the hash based tag system to allow for sub-tags?

Something like; an item has the sword tag and an optional note stating that tag has sub-tags. Which may be a set of constants that can qualify a tag in various ways. Rather than have a different sword tag for each type of sword, you have sword sub-tags that IF WANTED can be queried.

A battlers tag pool, will keep track of all existing tag - details such that if a script has specific use dependent information, every time it sees a tag, it can 'cache' that data in the tag for easy retrieval. The most obvious being the number of said tags in the pool in relation to other tags. Thus a battler trait might be that they gain a bonus any time they have more of tag-x than tag-y... Or maybe they gain a bonus for every tag-x... etc... But the data can be anything dependent on that tag, as once the tag has a 0 count that information is ignored. Like a self regulating switch system.

Roguedeus commented 8 years ago

The tag + sub-tag process might be used as the system you suggest. Using tag combos to dictate features, effects, etc...

HimeWorks commented 8 years ago

Yes. So for example, you have a sword and a axe tag.

You tag all your swords with sword and all your axes with axe. They would then inherit properties that are assigned to each tag.

Note that any sub-tag can be converted into a tag by using a common delimiter. However, sub-tags may be preferred because you can then re-use them.

Would there be some reason why sharp-sword and sharp-axe is not as versatile as having a separate sharp sub-tag?

Roguedeus commented 8 years ago

Only in that I amusing symbols to reduce memory and increase speed. And rather than having:

#20 tags
:sword_sharp, :sword_dull,  :sword_magic, :sword_fire,  :sword_ice, 
:axe_sharp,   :axe_dull,    :axe_magic,   :axe_fire,    :axe_ice, 
:dagger_sharp,:dagger_dull, :dagger_magic,:dagger_fire, :dagger_ice, 
:spear_sharp, :spear_dull,  :spear_magic, :spear_fire,  :spear_ice, 
#9 Tags
:sword, :axe, :dagger, :spear
:sharp, :dull, :magic, :fire, :ice

The variety could multiply into hundreds really quickly, without separating them.

Roguedeus commented 8 years ago

Oh... I might have missunderstood a little.

The battler has a tag pool... All tags from all sources, are pooled. If you have a battler with sword and sharp tags, there is no way to know they are related. Unless you saved it as sword + sharp as a tag + subtag.

HimeWorks commented 8 years ago

Yes I was referring to having lots of combinations of every possible tag + sub-tag. That's one of the main drawbacks.

Roguedeus commented 8 years ago

With tags done, I just completed the core of the Advanced Damage script... Remaining:

Advanced Damage implements Damage Core (Core param damage methods for damage formulas), Damage Delivery (Factoring in the means of attack to accuracy vs target mobility) and Damage Nature (How the damage is finely applied to the target, upon arrival)

However, in order to make Casting and Aptitudes work completely, I'll have to finish the core of my State Mechanic... It both reply on some form of alternative state tracking.

Roguedeus commented 8 years ago

I put in 7 hours (from 6am to 1pm) today and I am about to do more... And I am actually NOT fatigued in any way.

This is a first. There was once a time when any more than 3 hours of coding made my head hurt.

It is an odd feeling. Doing something that once frustrated me more than not, and actually enjoying it.

/boggle

Roguedeus commented 8 years ago
  #-----------------------------------------------------------------------------
  # new: 15_0924 last: XXXXX
  #-----------------------------------------------------------------------------
  def add_parameter(param_sym, value)
    param_id = RDeus::RDeus::PARAM_TABLE[param_sym]
    if param_id < 10
      add_param(param_id, value)
    elsif param_id < 20
      add_xparam(param_id - 10, value)
    elsif param_id < 30
      add_sparam(param_id - 20, value)
    else
      add_cparam(param_id - 30, value)
    end
  end
Roguedeus commented 8 years ago

I am trying to build a dynamic custom parameter script... Just because. ;)

Roguedeus commented 8 years ago

This was a pain to figure out how to do. But oddly enough, I enjoyed it!

  #-----------------------------------------------------------------------------
  # new: 15_0924 last: XXXXX
  # <cparam> </cparam>
  #-----------------------------------------------------------------------------
   def custom_parameter(cparam_id)
    unless @custom_param == nil
      return @custom_param[cparam_id] || 0.0
    end
    @custom_param = Array.new(RDeus::AdvParams::CUSTOM_PARAMS.length) {0.0}
    self.note =~ /<cparam>(.*?)<\/cparam>/im
    if $1
      $1.strip.split(/[\r\n]/).each {|cparam|
      next if cparam.empty?
        RDeus::AdvParams::CUSTOM_PARAMS.each {|param, id|
          cparam =~ /#{param}:\s*(\d+)/i
          if $1
            @custom_param[id] = ($1.to_i * 0.01) if $1
            break
          end
        }
      }
    end
    return @custom_param[cparam_id] || 0.0
  end
end

At the moment I only need new xparams...

Roguedeus commented 8 years ago

There is already built in permanent parameter increases for the base 8, but none for the other parameters... So I just added that.

Usable item effect...

  #-----------------------------------------------------------------------------
  # new: 15_0925 last: XXXXX
  # <add_parameter: param_sym, value>
  #-----------------------------------------------------------------------------
  def effect_add_parameters
    return @add_parameters unless @add_parameters == nil
    @add_parameters = {}
    regex = /<add_parameter:\s*(.*)\s*,\s*(.*)\s*>/i
    self.note.scan(regex).each {|params|
      next if params.empty?
      @add_parameters[params[0].strip.to_sym] = (params[1].to_i * 0.01)
    }
    return @add_parameters
  end

This uses the add_parameter method above.

  #-----------------------------------------------------------------------------
  # new: 15_0925 last: XXXXX
  #-----------------------------------------------------------------------------
  def process_effect_paramaters(user, item)
    #ADD PARAMETERS...
    if item.has_add_paramater?
      item.effect_add_parameters.each {|param_sym, value|
        add_parameter(param_sym, value)
      }
    end
  end

edit: Just noticed a small improvement... Moved the value math to the add_parameter method, rather than the items effect.

Roguedeus commented 8 years ago

This makes it so that I don't need to even use the default add param special effect. And it includes all possible parameters. Including custom ones.

Roguedeus commented 8 years ago

changed the buff and debuff mechanic so that they do not overwrite each other, but instead offset each other... If you have 2 buffs and get 1 debuff added, it merely reduces your buffs to 1.

Also added purge buff and purge debuff actions, to make up for the absence.

  #-----------------------------------------------------------------------------
  # overwrite: 15_0924 last: XXXXX
  #-----------------------------------------------------------------------------
  def add_buff(param_id, turns)
    return unless alive?
    puts("    add_buff(#{param_id}, #{turns})")
    if param_id < 10 
      @buffs[param_id] += 1 unless buff_max?(param_id)
      if @buffs[param_id] > 0
        overwrite_buff_turns(param_id, turns)
        @result.added_buffs.push(param_id).uniq!
      end
    elsif param_id < 20
      @xbuffs[get_param_id(param_id)] += 1 unless buff_max?(param_id)
      if @xbuffs[get_param_id(param_id)] > 0
        overwrite_buff_turns(param_id, turns)
        @result.added_xbuffs.push(param_id).uniq!
      end
    elsif param_id < 30
      @sbuffs[get_param_id(param_id)] += 1 unless buff_max?(param_id)
      if @sbuffs[get_param_id(param_id)] > 0
        overwrite_buff_turns(param_id, turns)
        @result.added_sbuffs.push(param_id).uniq!
      end
    else
      @cbuffs[get_param_id(param_id)] += 1 unless buff_max?(param_id)
      if @cbuffs[get_param_id(param_id)] > 0
        overwrite_buff_turns(param_id, turns)
        @result.added_cbuffs.push(param_id).uniq!
      end
    end

    refresh
  end

Did my best to use the same methods as the default system.

Roguedeus commented 8 years ago

Aside from some new icons for the x and s params, it appears to be working great.

It is in addition to the normal buff/debuff effects. Not that I would use the default one for anything now, but I did it for the sake of compatibility.

NOW, I can move into the Battle Tactics script.

Roguedeus commented 8 years ago

I just realized that for some time now I have been including lambda calls inside note tag methods... presumably to reduce the number of methods I needed to write. But now, I either need to re-write them or not pre-load the note tag... :(