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

Command Manager - Need Help #265

Closed Roguedeus closed 9 years ago

Roguedeus commented 9 years ago

I am attempting to make custom item selection windows, each with their own include? methods, and possibly other things. But first I need to get the thing working.

Here is what I've gotten so far... I added <cmd: custom_item> to your self.init_basic_commands for testing. I'll replace it with a CommandManager.register once this is working.

gl_commandmanager_issue_1

gl_commandmanager_issue_1b

Here is the default Item Command... gl_commandmanager_issue_2

gl_commandmanager_issue_2b

First: The first indexed item in the default item window is select, but its not in the custom window.

Second: The changes made to Window_ItemList.enable? apply to the default window, but not the custom window, even though Window_BattleCustomItem < Window_ItemList

Third: The help window is not showing up... Despite the fact that @help_window is applied to the new window correctly, and isn't getting hidden anywhere but on_enemy_cancel.

I realize that my issue isn't with your script. Its working fine. My issue is with Yanfly's Battle Engine... But he isn't exactly available. ;)

I can't figure out what I am missing, and I am running in circles attempting to.

Code Posted Below...

Roguedeus commented 9 years ago
#RogueDeus  15_0318

#RDeus - Window_BattleCustomItem

$imported = {} if $imported.nil?
$imported["RDeus - Window_BattleCustomItem"] = true
puts("#{$imported.size}~ RDeus - Window_BattleCustomItem") if $TEST

#===============================================================================
# 
#===============================================================================
class Game_Actor < Game_Battler
  #-----------------------------------------------------------------------------
  # new: 
  #-----------------------------------------------------------------------------
  def add_command_custom_item(args)
    cmd = Command_Item.new("C.Item", :custom_item)
    add_command(cmd)
  end
end

#===============================================================================
# 
#===============================================================================
class Window_BattleCustomItem < Window_ItemList
  #--------------------------------------------------------------------------
  # * Object Initialization
  #     info_viewport : Viewport for displaying information
  #--------------------------------------------------------------------------
  def initialize(help_window, info_viewport)
    y = help_window.height
    super(0, y, Graphics.width, info_viewport.rect.y - y)
    self.visible = false
    @help_window = help_window
    @info_viewport = info_viewport
  end
  #--------------------------------------------------------------------------
  # * Include in Item List?
  #--------------------------------------------------------------------------
  def include?(item)
    $game_party.usable?(item)
  end
end

#===============================================================================
# 
#===============================================================================
class Scene_Battle < Scene_Base
  #-----------------------------------------------------------------------------
  alias :rd_cutomitem_create_all_windows :create_all_windows
  def create_all_windows
    rd_cutomitem_create_all_windows
    create_custom_item_window
  end

  #-----------------------------------------------------------------------------
  alias :rd_cutomitem_create_actor_command_window :create_actor_command_window
  def create_actor_command_window
    rd_cutomitem_create_actor_command_window
    @actor_command_window.set_handler(:custom_item,   method(:command_custom_item))
  end

  #-----------------------------------------------------------------------------
  # new: 
  #-----------------------------------------------------------------------------
  def create_custom_item_window
    @custom_item_window = Window_BattleCustomItem.new(@help_window, @info_viewport)
    @custom_item_window.set_handler(:ok,     method(:on_custom_item_ok))
    @custom_item_window.set_handler(:cancel, method(:on_custom_item_cancel))

    if $imported["YEA-BattleEngine"]
      @custom_item_window.height = @item_window.height
      @custom_item_window.width = @item_window.width
      @custom_item_window.y = Graphics.height - @custom_item_window.height
    end
  end

  #--------------------------------------------------------------------------
  # * [Item] Command
  #--------------------------------------------------------------------------
  def command_custom_item
    @custom_item_window.refresh
    @custom_item_window.show.activate

    if $imported["YEA-BattleEngine"]
      @status_window.hide
      @actor_command_window.hide
      @status_aid_window.show
    end
  end

  #-----------------------------------------------------------------------------
  # new: 
  #-----------------------------------------------------------------------------
  def on_custom_item_ok
    # unless $imported["YEA-BattleEngine"]
    #   @item = @custom_item_window.item
    #   BattleManager.actor.input.set_item(@item.id)
    #   if !@item.need_selection?
    #     @custom_item_window.hide
    #     next_command
    #   elsif @item.for_opponent?
    #     select_enemy_selection
    #   else
    #     select_actor_selection
    #   end
    #   $game_party.last_item.object = @item

    # else
      @item = @custom_item_window.item
      $game_temp.battle_aid = @item
      BattleManager.actor.input.set_item(@item.id)
      if @item.for_opponent?
        select_enemy_selection
      elsif @item.for_friend?
        select_actor_selection
      else
        @custom_item_window.hide
        next_command
        $game_temp.battle_aid = nil
      end
      $game_party.last_item.object = @item
    # end
  end

  #-----------------------------------------------------------------------------
  # new: 
  #-----------------------------------------------------------------------------
  def on_custom_item_cancel
    @custom_item_window.hide
    @actor_command_window.activate

    # if $imported["YEA-BattleEngine"]
      @status_window.show
      @actor_command_window.show
      @status_aid_window.hide
    # end
  end

  #-----------------------------------------------------------------------------
  alias :rd_cutomitem_on_actor_ok :on_actor_ok
  def on_actor_ok
    @custom_item_window.hide
    rd_cutomitem_on_actor_ok
  end
  #-----------------------------------------------------------------------------
  alias :rd_cutomitem_on_actor_cancel :on_actor_cancel
  def on_actor_cancel
    rd_cutomitem_on_actor_cancel
    @status_aid_window.refresh
    case @actor_command_window.current_symbol
      when :custom_item
        @custom_item_window.show.activate
    end
  end

  #-----------------------------------------------------------------------------
  alias :rd_cutomitem_on_enemy_ok :on_enemy_ok
  def on_enemy_ok
    @custom_item_window.hide
    rd_cutomitem_on_enemy_ok
  end

  #-----------------------------------------------------------------------------
  alias :rd_cutomitem_on_enemy_cancel :on_enemy_cancel
  def on_enemy_cancel
    rd_cutomitem_on_enemy_cancel
    @status_aid_window.refresh
    case @actor_command_window.current_symbol
      when :custom_item
        @custom_item_window.show.activate
    end

    if @custom_item_window.visible && $imported["YEA-BattleEngine"]
      @help_window.show
    elsif $imported["YEA-BattleEngine"]
      @help_window.hide
    end
  end

  #-----------------------------------------------------------------------------
  alias :rd_cutomitem_select_actor_selection :select_actor_selection
  def select_actor_selection
    rd_cutomitem_select_actor_selection
    @custom_item_window.hide if $imported["YEA-BattleEngine"]
  end

end
Roguedeus commented 9 years ago

I apologize but you'll have to download Yanfly's Core Engine and Battle Engine to be able to see how his @status_aid_window and friends interact, as well as his changes to window switching sequences. (If you don't already have them)

https://yanflychannel.wordpress.com/rmvxa/

Roguedeus commented 9 years ago

Ok... I figured out the reason the Window_ItemList.enable? issue was because I aliased the method, rather than overwrite it... So its not being inherited.

I've gone ahead and added show, hide, methods to the appropriate methods so @help_window is working as its supposed to.

As well as called select_last for the new window.

But I am still unable to see why the help window and select index didn't simply work right out of the box. I didn't overwrite any methods, and aliased everything that touched an Window_SkillList method... /shrug

Roguedeus commented 9 years ago

I figured it all out... Acceptably enough. While I'd like to know where I messed up, I wouldn't ask you to waste your time now that I have a working solution. Unless its obvious.

HimeWorks commented 9 years ago

Maybe it's the "aliasing parent class does not affect child classes that do not call super" problem that you've encountered before.

Roguedeus commented 9 years ago

Wouldn't that only apply to the child classes aliased methods? Such as my new window?

I added this to get the index and help window to show up.

    @custom_item_window.select_last
    @help_window.show

Along with corresponding hide calls for cancels.

While my solution is 100% functional, I really wish there was a less verbose way I could manage this. As it stands, each new possible window requires an entire ~150 line script (minus comments).

Each class role will have at least a Default and a Class specific Item List. Traits will add new filters to the include? methods where applicable.

It feels better if the player has separate Potions and a Scrolls battle commands, rather than Items with all the many potions and scrolls to flip through every single time. (for example)

Roguedeus commented 9 years ago

Next on my list... And hopefully much less bother... State Groups.

I would really like the ability to apply rate changes to a group via tag filtering, rather than have to cut/paste each state individually in the features list.

Roguedeus commented 9 years ago

Here are my ambitions... :)

Many of these effects are already in existing scripts. However, in my previous build, where I attempted to HACK them all to play nicely, it created a noticeable hitch in performance in some battles.

So. I am going to attempt to start from the ground up with a 'State Management System'.

Which should include the following.

=begin

#===============================================================================
# DEVELOPER
#===============================================================================

  #----------------------------------------------
  Remember State User:
    All states applied to a target, remember who their last user was.

  #----------------------------------------------
  Grouped States: 
    Feature object apply state rates and resistances to all grouped states. 

#===============================================================================
# APPLY STATE
#===============================================================================

  #----------------------------------------------
  Environment State: 
    Transitioning into an area, attempts to apply a state to entering battlers.

  #----------------------------------------------
  Skill State:
    Having a skill, applies one or more Traits and or Auras.

      #------------------------------------------
      Trait State: 
        A state without normal duration, that can not be removed, but CAN be
        canceled out via a condition. It returns when the condition is false.

      #------------------------------------------
      Aura State: (Ranged)
        A trait like state that can effect more than self.

  #----------------------------------------------
  Progressive State:
    Can apply new states when state charge reaches a value.
    See: DURING STATE details for more. 

#===============================================================================
# DETERMINE STATE
#===============================================================================

  #----------------------------------------------
  Conditional State ID: 
    Applies one or more states from a list, when condition is true.

  #----------------------------------------------
  Linked State:
    Effects other states when itself is active.

      #------------------------------------------
      Inclusive State:
        Adds other states when itself is added.

      #------------------------------------------
      Qualified State:
        Keeps itself active, while another state is active.

      #------------------------------------------
      Exclusive State:
        Removes itself, when another state is added.

          Note: This is preemptive. Thus if the added state is normally
            resisted by the ACTIVE state, the active state removes itself
            BEFORE that resistance can prevent the added state from being 
            added.

#===============================================================================
# DURING STATE
#===============================================================================

  #----------------------------------------------
  Maintain State:
    This state will only remain active if maintenance is applied on Regen.

      #------------------------------------------
      Deficit Cost:
        This is a (formula value) of a parameter that is no longer available, 
        as long as the state remains. 

          Note: How its removed isn't important.

      #------------------------------------------
      Rent Cost:
        This is a (formula value) of a parameter that is consumed on Regen, 
        as long as the state remains. 

          Note: How its removed isn't important.

      #------------------------------------------
      Unlearn/Relearn Maintenance:
        These are the two skill ID's that swap with each other as long as
        the state is active. One applies, one removes.

  #----------------------------------------------
  Charged State: 
    This state will only remain active as long as charges remain. If this
    state is re-applied, the new applications charges are added.

      #------------------------------------------
      Charge Adjust:
        An action or newly applied state can adjust charges.

          Note: If charges reach zero, charged state is removed.

      #------------------------------------------
      Dispel Charges:
        An action or newly applied state can COMPLETELY remove all charges.

      #------------------------------------------
      Progressive State:
        This state adjusts a (formula value) of charges on Regen. It CAN 
        change into or add a new state when its charge level is at a 
        (formula value).

  #------------------------------------------
  Elemental State:
    Supports Hime - Attack Element Modifiers, for all damage effects.

          Note: Needs, Remember State User.

  #----------------------------------------------
  Advanced DOT State:
    This state applies its effects over time in an advanced formula.
    Primarily, front and rear loaded effects. 

      #------------------------------------------
      Drain State: 
        This state drains a (formula value) of Parameter from the target, and
        gives it to the user. 

          Note: Non-fuel parameters are Debuffs to the target
            and Buffs to the user, as long as the state is active.

          Note: Needs, Remember State User.

If you can think of anything I am leaving out, please feel free to say so.

Roguedeus commented 9 years ago

Remembered one I missed...

=begin
  #----------------------------------------------
  Living State: 
    This state remains active as long as its duration lasts AND it remains 
    alive. The state has a number of HP that are lost when the target takes 
    damage, and will die if its HP reach zero.

          Note: Can be combined with charged and maintain state for more 
            dynamic durations.

      #------------------------------------------
      Substitutive State:
        A living state that takes damage INSTEAD of the intended target.

          Note: Living states can be element dependent. As in ONLY Fire damage. 
HimeWorks commented 9 years ago

Hmm, I think a good system is one where you would be able to implement new types of states as they come up without having to worry about breaking previous things.

Roguedeus commented 9 years ago

I've learned that my own character flaws prevent me from bouncing back and forth between focuses to quickly, without suffering major productivity issues. Its a curse I've accepted, that comes with working solo.

I wish I could take the 'add it when I need it' approach, but am simply not built that way... I am best when I am focused on one area at a time.

So I've gone the, I will build the things I wanted before, and add new stuff while Im under the hood. So later I can move on to creating content, while doing as little coding as I can. (Flat refusing to do anything but squash bugs).

Since the vast majority of battle resolution (and class diversity) revolves aroundskill effects, use restrictions and state mechanics,(having separate classes perform the same actions in unique ways) those are the systems I am doing my best to iron out now.

Anything else that pops in my head gets put on the ToDo list for later.

Roguedeus commented 9 years ago

The core will be the state user and charges.

Using a Hash of Hashes where state_id keys the user value which is the total charges that user applied. When charges are removed by a user that has not applied their own charges, the charges are randomly removed from available users. If the user is removing charges, their own charges are removed first.

In the future, any option that applies a state linking back to the user will be able to know how many charges that user applied, and if there are any benefits to having applied charges, they are split between charge owners in a ratio equal to the users contribution of charges.

I plan on applying charges to effects based on caster states. More powerful casters apply more charges. Etc. Also, some states effects will scale with the number of charges applied. Etc. The idea is to have a foundation that allows for the most depth I can think of.

HimeWorks commented 9 years ago

How about instead of having a hash of hashes, create a hash of custom data objects that store state information?

class Data_StateInfo
  attr_accessor :charges
  attr_accessor :user
  attr_accessor :etc
end
Roguedeus commented 9 years ago

I don't know why I always forget to consider that. Its a better solution, I think.

Can you think of any issues regarding a situation where a user Battler (Enemy or Actor) applies a non-duration or permanent state to an actor and then dies?

Would the pointer to that battler (user) maintain the data as long as the state user hash pointed to it? Because Game_Troop clears out the @enemies array after the battle ends.

edit: I can't. The only reason why the user is important is IF I want to link back to the user or search for that users charges. If the user is dead, there's no need to link back to them, and I couldn't care less if that user was alive if iterating through the remaining charges.

Roguedeus commented 9 years ago

Now that I think about it... Would the Data_StateInfo objects be easy to search through? Or would I have to iterate through each existing key looking for the StateInfo that applies to the right user?

Roguedeus commented 9 years ago

This is my base method.

  def add_state_charges(state_id, user = nil, charges = 1)
    return unless state?(state_id)
    user = RDeus::StateUser::BATTLERX unless user
    @result.last_state_user = user
    @last_state_user = user
    #Necessary because marshal dumps can't be performed on HASH with defaults.
    @state_users[state_id] ||= {}
    @state_users[state_id][user] ||= 0
    @state_users[state_id][user] += charges
  end

edit: this method is only ever called AFTER add_state is called. So if the state doesn't exist, no charges are added.

Its the reverse for remove_state. In order to easily track user, the majority of the activity occurs in the item_effect_xxxxx methods. As they all have (user, item, effect) parameters.

Roguedeus commented 9 years ago

Is it just me or is this method broken?

  def item_effect_add_state_normal(user, item, effect)
    chance = effect.value1
    chance *= state_rate(effect.data_id) if opposite?(user) #<-----
    chance *= luk_effect_rate(user)      if opposite?(user)
    if rand < chance
      add_state(effect.data_id)
      @result.success = true
    end
  end

Say you had a spell that effected every battler in the scene... Well, now your party has ZERO defense against any effects.

I realize that would require a custom targeting script, but there are a lot of those. And the one I am most familiar with makes no consideration for this.

HimeWorks commented 9 years ago

Consider moving these into a separate issue.

For the most part, yes, the default engine does not have a scope such as "all battlers" which means anything they do will probably assume that.

Roguedeus commented 9 years ago

Its just idle chat. No help needed.

I forget that you must be asked questions that appear unimportant but the people asking expect real answers. No worries. If I had a serious question I'd make a separate topic. ;)

Roguedeus commented 9 years ago

On that note. I am considering using the Help Window, in Scene_Battle, as a replacement state display for actors. Its already used for enemies, and yet that screen space is unused when actors are selected. Why?!? All that space would make a great place to display umpteen states all at once.

edit: now that I think about it, that space could be used to display a TON of useful info. Rather than settle for cramming it all in that small space for each actor. Especially when you've got a party bigger than 4 members.

HimeWorks commented 9 years ago

Want to use this instead? Haven't really decided whether I want to go a forum direction, but when I release some games I might need one.

http://forums.himeworks.com/index.php

Roguedeus commented 9 years ago

Sure thing. I have a hosted forum myself that I never used... I once had an ambition that FAR surpassed my capabilities, and I got a little ahead of myself. By well more than a few years...

I would post to rpgmakerweb and such, but to make a long story short, I simply don't have the patience. I spent my 20's playing the forum game, and when I look back on it all I cringe.

Roguedeus commented 9 years ago

Well... I just had to change a MAJOR issue with the way TSBS handles usable items and my scripts...

For some odd reason Theo decided that it would be best to marshal dump and load every single item used in battle... /boggle

Trouble was, this meant that saving an items instance variables for the sake of speed was rendered pointless.

I had thought this was only happening when his sequence messed with damage, but it was EVERY SINGLE ITEM.

I just hope this is the last time I have to gut that script for compatibility.

edit: On the bright side, I can go back to using lambda's in usable item evals.

edit: 10 hours straight today... The inside of my skull feels fuzzy. :p

HimeWorks commented 9 years ago

I think at one point I thought it would be a good idea to do a deep clone of database objects as well before I operate on them, but then I didn't really see any point.

I'd rather create a wrapper for an item instead if I wanted to change some properties during execution.

Roguedeus commented 9 years ago

Ok, I just ran into something I am completely confused about...

  #-----------------------------------------------------------------------------
  # new: 
  #-----------------------------------------------------------------------------
  def eval_max_user_charges(state_id, a, b, p=$game_party, t=$game_troop, v=$game_variables, s=$game_switches)
    return eval(RDeus::StateCharge::BASE_USER_MAX_CHARGES_FORM)
  end

this is the formula...

    BASE_USER_MAX_CHARGES_FORM = "($data_states[state_id].charges * (2 + (a.level * 0.1).to_i) )"

And its giving me a wrong number of arguments (0 for 2) Error.

It must be something simple that I simply can't see for some reason...

HELP!? :p

Roguedeus commented 9 years ago

nevermind. .charges(arguments here)

Roguedeus commented 9 years ago

Just to make sure I am not inadvertently breaking or over complicating this... Can you tell me if this works the way it appears to?

  #-----------------------------------------------------------------------------
  # new: <state_constitution: id> formula </state_constitution>
  #   The number of charges of state_id that are adjusted on Regen.
  #-----------------------------------------------------------------------------
  def state_constitution(state_id, a, b, p=$game_party, t=$game_troop, v=$game_variables, s=$game_switches)
    if @state_constitution.nil?
      @state_constitution = {}
      @state_constitution_forms = {}
      data = self.note.scan(/<state[-_ ]?constitution:\s*(.*)\s*>(.*)<\/state[-_ ]?constitution>/im)
      unless data.empty?
        data.each {|charges|
              id = charges[0].to_i
              @state_constitution_forms[id] = charges[1]
              @state_constitution[id] = eval( "lambda {|a,b,p,t,v,s,id| #{@state_constitution_forms[id]} } ")
        }
      end
    end
    id = state_id
    return @state_constitution[state_id].call(a,b,p,t,v,s,id) if @state_constitution[state_id]
    return 0
  end

I figured I'd try to allow for each possible state ID to have its own lambda eval ready for recall as fast as possible.

Roguedeus commented 9 years ago

I noticed... I forgot the ? in the regex captures. Attempting to use multiple note-tags caused issues as a result.

data = self.note.scan(/<state[-_ ]?constitution:\s*(.*?)\s*>(.*?)<\/state[-_ ]?constitution>/im)
HimeWorks commented 9 years ago

Oh ya, you should always use lazy matching when you're using .* or .+ if you don't actually intend to match as much as possible.

Roguedeus commented 9 years ago

Also forgot to make sure \r\n didn't carry over if there was a word wrap in the note-tag...

.collect! {|x|x.collect!{|y|y.gsub(/\r\n/, '')} }
data = self.note.scan(/<state[-_ ]?constitution:\s*(.*?)\s*>(.*?)<\/state[-_ ]?constitution>/im).collect!{|x|x.collect!{|y|y.gsub(/\r\n/,'')}}

edit1: There a faster way to do this? Hmmm, the \r\n only seem to be captured on the first match, for some reason...

edit2: nope that was just my stupidity. Its captured every time. My first solution appears the most reasonable. And using destructive replacement is usually faster than constructing an entirely new array.

Roguedeus commented 9 years ago

I am flummoxed...

As the state_constitution method above is written, how could the last condition check return a true:TrueClass (NoMethodError) ???

This is the end of an identical method, using a different instance variable.

    puts("\n   apply_charges(#{state_id}, #{a.name||"Nil"}, #{b.name||"Nil"})\n")
    id = state_id
    return @apply_charges[state_id].call(a,b,p,t,v,s,id) if @apply_charges[state_id]
    return 0

As you can see, I am passing the integer 20 for state_id and both a & b are battlers. gl_stateengine_itemapplybytags_issue_1

Where am I attempting to verify the trueness of an already true object?

Roguedeus commented 9 years ago

Ok... I really need to stop asking questions before I exhaust all my brain attempting to figure it out.

That error was caused by me using the same instance variable in more than one assignment. Duh. I didnt see it at first, because I tend to group variables with the same basic prefixes, so highlighting one tends to highlight all in some way.

I may have to revisit that practice.

HimeWorks commented 9 years ago

Also forgot to make sure \r\n didn't carry over if there was a word wrap in the note-tag...

Word-wrap in the note-box do not have new line characters added automatically. Or at least, I didn't see them when I word-wrap.

Its captured every time. My first solution appears the most reasonable. And using destructive replacement is usually faster than constructing an entirely new array.

Yes, that is generally the case since you don't need to allocate new memory and copy things over and stuff like that.

I didnt see it at first, because I tend to group variables with the same basic prefixes, so highlighting one tends to highlight all in some way.

This is probably why Hungarian Notation exists to make it clear what kind of value is stored in a variable.

Of course, it's not as useful if you're duck-typing as a single variable could then hold any number of different types at any given time.

Roguedeus commented 9 years ago

I refereed to word wrap for simplicity. I often forget they have very specific meanings. Since I use external texts for object notes, I use a lot of multi spaced formatting to help keep things clear. So there are bound to be many \r\n inserted randomly inside regex matches.

Way back in early 2000's when I first stared messing with code, it was for a C like scripting language. I was instructed to use things like iAttackBonus and sItemName variables for that exact reason.

I stopped doing it when I started using IDE's. (aka: Got lazy)

And with Ruby, I often found them pointless, as variables often hold multiple types of data. So instead I started grouping related variables for easy searching through my code. But in some cases I forget to double check my new variables with a quick existing variable search. :)

Roguedeus commented 9 years ago

I just realized that the .inject(0) method has issues when you attempt to mess with the total in conditionals more complex than this.

    return feature_objects.inject(0) {|sum,obj| 
        sum += obj.state_constitution(state_id, user, self) if obj.qualifies?
    }
Roguedeus commented 9 years ago

I am beginning to notice how so much of so many of my scripts use the same basic methods, with small changes... It makes me wish I was better at planning my code as to better avoid it. Rather than having half a dozen nearly identical rather verbose methods, have just one verbose method, that acts like half a dozen methods with half a dozen 3 line wrapper methods.

I remember reading someone say that it needs to work first... Then refine it.

HimeWorks commented 9 years ago

Yes. If it doesn't work, it doesn't matter how optimized it looks. Taking a bunch of similar methods and finding a way to cut it down is part of the refactoring process.

It is possible to realize it before you begin coding (either by figuring out how you're going to code it on paper), and it might not even be necessary to refactor it (maybe the extra method call results in bad unacceptable performance for you)

Roguedeus commented 9 years ago

I am about |...| close to completing this damned State Charges foundation... Spent the climax of the last 5 hours about ready to tear my own hair out over what ended up a cut/paste typo... Must calm the self, and eat some Captain Crunch. ;p

gl_stateengine_itemapplycharges_issue_5b

Roguedeus commented 9 years ago

Too much Captain Crunch....

HimeWorks commented 9 years ago

Crunchy.

Sometimes it might be better to delete it and re-implement with the new discoveries.

Roguedeus commented 9 years ago

I am considering the creation of an intelligent equips script. Like a party actor that not part of the party, but 'acts' in game, as if it were.

Not sure how they would be damaged, or if they would at all. Maybe if at all, they go dormant for a while, and the player has no idea how long. The equip still functions like normal, it just stops 'helping' in battle, and can't be talked to out of battle.

Hmm...

HimeWorks commented 9 years ago

So you want your equips to be treated like actors?

Roguedeus commented 9 years ago

Something like that. I am thinking I'll write it so that any battler object can have an associated sub-actor array, where any actors in that array will get returned when certain game events occur. Such as determining the action order in battle, and perhaps a few others.

I am thinking something along the lines of the traditional talking sword, or whatever.

Roguedeus commented 9 years ago

I think I am done with my basic charged states foundation... From the looks of it, I can use this as the building block, in some way, for all the other state mechanics I mentioned before. Including a few new ones.

This only took me all week. And about... hmmm.. 40 Hours of strait coding. I am thinking a good 6 hours of it was figuring out where to put what, and making sure I considered all the effected default methods. Etc... About 20 Hours of error searching and bug squashing... And about 14 hours of inspired coding, learning new things along the way.

I am sooo inadequate, and prone to impatience caused errors.

Edit: I know this, because I screen cap my desktop every second or so, in a time lapse video recorder. As a sort of work journal. Been doing it for about two years now. I have video of me fumbling around with some of the most mundane things (knowing what I do today). It comes out to roughly 1 minute of video per hour of work. But it varies by as much as 10% sometimes, due to desktop activity and encoding lag.

HimeWorks commented 9 years ago

I think actually getting it done is better than not. I don't think I've seen a framework for this kind of thing either.

Roguedeus commented 9 years ago

Essentially, minus permanent and trait states (plugins yet to be done) every state has at least 1 charge when applied. If the usable item applying the state doesn't dictate its own charges at application for the state, the state itself has a default charges application. This allows for ONE state to have multiple duration possibilities on application. Rather than requiring copies of the state when different durations are needed.

By default, base charges removed every turn on remove_states_auto (along with a check for non-duration states with zero charges) is ONE charge. This can be changed in a global var. The state is default removed when it has zero charges. But this will be overridden with more options in plugins.

On application, the target checks its state charge resistances (feature object collected) against the number of charges being applied, and if the result is 0 or less, the state is not added. I call this 'soft resistance'. Default resistance still works exactly how its supposed to. As do state rates.

Every turn, on regenerate_all the battlers state constitutions are checked against any current states. If qualified, each constitution will update their applicable states charges. This can be both positive and negative. So a WEAKNESS to a state can cause its charges to increase, preventing normal duration from removing it, and forcing an action to remove it. Meanwhile, any positive constitutions are removed... Thus having a 3 State Constitution vs. states with the 'infernal' tag, will remove 3 charges from every infernal state on the battler, on regen. When added with default auto remove, its effectively AUTO + CONSTITUTION per turn.

There are more smaller details, such as charge adjusts on application, soft resist upon new state application, user item charge conditions (like requiring a critical hit to remove X charges), max user state charges, max state charges, etc...etc...

edit: I attempted to keep as many things as possible formula limited, as to reduce the number of note-tags needed. And to allow for a wide variety of possible applications for scripters. This is why I made collections of lambda evals... I hope that as the plugins increase the load on feature object totals won't cause any hitching... HOPE.

Roguedeus commented 9 years ago

Just wrote the Permanent State plugin.

Permanent states persist even without charges. They can only be removed via a dispel effect, which can occur only when command events remove states, or when a state is added that has a default remove feature for the permanent state.

Hardened Permanent States, aren't removable until all their charges are gone. Then a dispel effect can remove them.

I just need to change Yanfly's default turns remaining indicator into a charges remaining indicator, and have it appear in the simple status window as well as the battle status window.

Roguedeus commented 9 years ago

To code or play Pillars of Eternity?

That is the question...

edit: Coding it is... I've been more driven (and productive) lately than usual. It feels good.

Roguedeus commented 9 years ago

Just added support for shield based charge loss...

Effectively, if a state is set to remove itself on damage, and has 5 charges, it needs to be damaged 5 times to be removed. BUT what if that state has a 0% PDR? No damage occurs when a physical attack lands, so no charges are lost. Well, now they can be.

Also added Recovery shield support. If a state makes the next 3 heals 50% less powerful, the state will fire its on damage remove if the hp_damage result is negative. Of course, it works in reverse as well... So the next 3 heals might be 50% stronger. Etc...

edit: Was only able to work about an hour so far... Weekend got in the way.

Roguedeus commented 9 years ago

For the Group State plugin I am using a state_rate overwrite method to substitute a placeholder state_id, for any state flagged with it, as one of their 'grouped' states. This allows feature objects to apply a state rate to a database entry, and have it apply to any state that shares it as a group. If a state has more than one group state id, then all applicable feature object rates effect it...

Thus a suit of armor can have a single state rate 50% Tier 1 Magic DOT's and have it apply to any state that is tagged with the <group_state: id> of the Tier 1 Magic DOT's state placeholder in the database.