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

Light Weight - Battle Order - Script Plugin, Idea #246

Closed Roguedeus closed 9 years ago

Roguedeus commented 9 years ago

I am thinking of writing a plugin for Yanfly's FTB Battle Option that utilizes its AP model but maintains the speed/initiative of the default system, with a twist.

Like FTB, actors will be able to take multiple actions in a turn, dependent on the AP used in the action. But unlike FTB, each potential action point has its own initiative and actions that require multiple action points must WAIT until the total points needed reach their initiative order.

The catch, of course, is that the player will need a visual indicator so they are aware when their potential actions will fall within the order. I figure I could use Yami's - Order Gauge for this somehow.

Thoughts? Critiques? Potential issues?

Roguedeus commented 9 years ago

An alternative to pluging-into FTB, would be some sort of make_speed and make_action_orders change that allowed for the battle order to 'shift' mid-turn, according to each players initiative, chosen actions, and remaining potential actions.

I'm not sure how I could pause and insert multiple actions that way though... I'd have to analyze it.

Essentially I am trying to avoid an Active Battle system, but still allow for a similar dynamic, while maintaining the typical default turn by turn aspect.

HimeWorks commented 9 years ago

I am not familiar with that battle system to provide any useful comments. You'll have to explain how it works.

Roguedeus commented 9 years ago

Each actor has an AP total that is applied to a total party AP cap. An action costs a number of AP (normally 1 AP) and generally each actor is limited to using an equal number of AP they would contribute to the party total, before being capped.

EX: Actor-A with 2 AP and Actor-B with 3 AP, MAY HAVE a party cap of 4 AP. Every turn the party can spend 4 total AP, in any order, via any actor. Thus if Actor-A used both their AP, then Actor-B would be limited to 2 AP, even though they had 3. In reverse, Actor-A would be limited to 1 AP, if Actor-B had used all 3 of theirs first.

The catch for this system, is that initiative is pointless. Speed has no baring on battle at all as every turn is all or nothing for each side. It makes for some very strategic choices and allows for deep skill trees that would be a waste in a timed setting.

HimeWorks commented 9 years ago

Ok, FTB sounds pretty straightforward.

-Turn-based. -You get x amount of AP to work with in one turn -You can use those AP however you wish between actors

Now in your plugin, you want

-AP to recover

That's where I would start. You would determine how you want the AP to recover for the most part. You may be able to implement your new mechanic with that element alone.

Chrono Cross implemented a similar mechanic in the battle system.

Everyone had 7 AP (there was no party cap, each actor just had 7 AP). When your turn comes around (based on speed and whether you had AP available), you have 5 options

I don't remember the AP recovery rates, but I think it was 1 AP per action taken. So for example, if I perform one action, everyone else recovers 1 AP. If I choose to pass, then everyone including myself would recover 1 AP as well.

I could look it up but that's the general idea.

Roguedeus commented 9 years ago

That's similar, but FTB completes the parties turn when all AP have been used, and resets AP at the beginning of every new turn.

I have spent the last few hours deconstructing FTB and finding out how it ticks. I attempted to intercept the way it processes actions as they are made, but I've had an issue with the point in the choice sequence that the action array is touched.

HimeWorks commented 9 years ago

Are the actions executed immediately when you pick them?

Roguedeus commented 9 years ago

Yes.

I am trying to stop that, and instead queue them for NORMAL processing. While keeping the AP mechanic intact.

Roguedeus commented 9 years ago

Once I have that figured out, I am going to shift the actors speed, based on the total queued actions speeds. Thus actors using more AP's will perform theirs after actors using less AP's... Though higher AGI/Speed actors might still act first, even with more AP's used.

Roguedeus commented 9 years ago

I am having a hell of a time trying to work around the damn action_input_index and next_command, and I am so brain fried right now I can't see why.

Looking at it, it seems like it shouldn't be much harder than pushing new actions into the array upon selection, and then allowing only actors with qualifying AP to select. Iterating the selecting actors input index, on the resulting next_command cycle...

But its not working... /boggle

Caveat: It also requires that actors don't initialize their action array with blank actions. Remaining Scene_Battle.next_command is determined by remaining AP's rather than actor.next_command being true.

HimeWorks commented 9 years ago

NO I deleted my comment lol

HimeWorks commented 9 years ago

Ok so basically all next_command should be doing is selecting the next person that can move. If no one can move, it will just keep updating until you find it.

Roguedeus commented 9 years ago

I can get the first actor to select skills for AP, but once their lot is done, it won't move over to the next actor.

I am guessing its something simple. But my head hurts and I need sleep. I'll do my best to jump in first thing tomorrow.

HimeWorks commented 9 years ago

Sounds like there's more to be done after you call next_command

Roguedeus commented 9 years ago

I've reached a point where I can't follow the methods clearly enough to figure out why actor selection breaks with my recent changes.

I don't know what I am missing, but whatever it is I have lost my patience and I am beginning to get irritated. So I am going to step away.

If you have time, here is a demo. (uncomment the plugin to see the results) http://www.roguedeus.com/Stuff/New_FTB_Demo.zip

I've commented my plugin as clearly as possible so you know why I did what. (Thats not obvious) If you can figure out what I did wrong, I'd appreciate it. I'll try to look at it again later anyway. After I clear my head for a while. ;)

Note: I had a small realization as I made the demo and this post, so its not as broken as I complained in the first sentence. But I am still stepping away for a while. :)

HimeWorks commented 9 years ago

Alright taking a look.

HimeWorks commented 9 years ago
def next_ftb_member?(last_index)
    actor = BattleManager.actor
    puts("<FTB Plugin> next_ftb_member? #{actor.name}(#{actor.ftb_actions}/#{actor.max_ftb_actions})")
    return true if actor.nil? #OK
    return false if actor.max_ftb_actions >= actor.ftb_actions # <------------
    return false if BattleManager.actor.index >= last_index
    return BattleManager.actor.index != last_index
  end

What does this method do?

Roguedeus commented 9 years ago

That may be something I left in on accident. I changed it from > to >= in order to try and stop the extra selection from occurring. Forgot to comment it.

HimeWorks commented 9 years ago

So what is the problem? Eric selects two skills, natalie then selects one skill, and the actions are carried out.

Roguedeus commented 9 years ago

You need to un-comment the plugin... When active, Eric always selects an extra skill atm. I haven't touched the code since I posted the demo so I am not sure if its as simple as accurately updating action_input_index or more complex. Another set of eyes helps.

HimeWorks commented 9 years ago

The problem is that method. There's something wrong with the highlighted condition.

HimeWorks commented 9 years ago

Or at least I think it is...

HimeWorks commented 9 years ago

I don't think BattleManager's next_command would work here, since it's picking its own actor separate of your logic...

HimeWorks commented 9 years ago

Anyways I used

return false if actor.max_ftb_actions > actor.ftb_actions #OK

And it works fine.

Roguedeus commented 9 years ago

lol no kidding... It seems I tend to pick the moment I am right on the edge of figuring it out to walk away or ask for help.

Thanks BTW. I'll poke at it a bit to see if I can find any holes before adjusting speeds and stuff.

Roguedeus commented 9 years ago

Found something already... In the demo, with your change, act with natalie first, then eric... It completes the turn before both of Erics AP are used. ;)

HimeWorks commented 9 years ago

That's not happening for me. I re-downloaded and applied the one-line change.

Roguedeus commented 9 years ago

Well I'll be damned. Looks like I've got a problem then. :(

For the record, I changed all references of next_command to the local scope version. It doesn't seem to break anything. (Not sure why Yanfly used the other one)

Looks like I've got more fun to look forward to in the morning...

Roguedeus commented 9 years ago

I hate troubleshooting return fall-throughs.

Roguedeus commented 9 years ago

Demo issues.

You can't cancel a selection and re-select before the turn ends. This must be caused by the fact that normally an FTB skill is performed the moment its selected, so there is no reason to cancel it. Canceling it would require the last skill pushed into the action array to b deleted, and the AP refunded. I am just not sure where to do it without breaking something.

The demo wont allow you to change actors after Eric selects his first skill... I'm not sure whats causing the inability to switch actors between action selections. It may be why on my full build, acting with the second party member first (natalie in the demo) causes the first action selected by the remaining actor (index 0) to complete selection to early. I am guessing its comparing the AP of first to act index actor rather than the current one.

I can't guess why it doesn't happen in the demo the same way. Some other one of Yanfly's battle plugins may be touching the scenes variables. I am hoping if the demo code works correctly, then whatever is breaking my full build will fix itself.

Roguedeus commented 9 years ago

I am thinking, would it be easier to simply scrap the default FTB code (leaving in the windows and AP variables) and re-purpose it, rather than try to utilize the Scene_Battle and BattleManager stuff?

edit: Nope that was a mistake... :p

Roguedeus commented 9 years ago

I don't understand why I am having such a difficult time with this. I am about to start pulling my hair out.

I have written well over a few dozen scripts on my own, ranging from re-arranging the UI to adding effects over time states, and some things I surprised myself I figured out without assistance.

But what seems SHOULD BE a straight forward battle mechanic tweek, has consumed almost three days of my time, in both attempting to do it and untangling my head of the resulting frustration.

HimeWorks commented 9 years ago

For refunding an action, you could simply write additional logic that looks at the currently selected actor's list of actions and removes the last one. This should not break anything.

For the other issue, it is not obvious why that's happening either.

HimeWorks commented 9 years ago

I'd just put it aside until later. A lot of scripts I have were put on hold cause I ran into an issue and wasn't sure how to solve it.

Roguedeus commented 9 years ago

I would, if it wasn't a core battle mechanic. I have nearly all my desired tweaks done, with a few flavor ones still not done yet, but I would rather not get far into battle balance without a reliable core mechanic.

Of course, I could simply cut it. But I am stubborn. ;(

HimeWorks commented 9 years ago

You'll have to go back to the drawing board then and figure out exactly how the "next actor" is determined.

next_command is a good start. You might as well just explain to me how it works in detail, with all the steps and the condition checks, since I only glanced at it.

Roguedeus commented 9 years ago

Thats what I spent a good lot of time today doing. I managed to figure out a few new things in the process, but even after tracing the entire logic flow, I get hung up. I am sure if I keep at it long enough I am bound to eventually stumble onto a solution, if nothing else. :p

Its just that my brain doesn't crunch code easily. Its a very deliberate activity for me and after 3+ hours, I get VERY fatigued. Doubly so when I can't follow it easily. Its a curse.

HimeWorks commented 9 years ago

Then write it out. And then translate it into normal language.

Eventually you should be at a point where you have all of the relevant methods down and a brief explanation where you don't have to actually look at it anymore.

That's how debugging usually works.

If the engine doesn't allow you to skip to the next actor, then you'll have to start from the point where you press "skip actor" and then write down all the methods that are being called.

Roguedeus commented 9 years ago

I agree. I have started commenting every single step in the code. A few of my most recent relizations were from that.

I think I am obsessing a bit to much on my battle mechanic though... I have changed pretty much every part of how HP and MP function, HIT, Damage, Variance, Crit, and how Params factor in... Even EVA and MEV... The last untouched part is action speed... Heh.

Very different, but similar enough that its not to shocking.

HimeWorks commented 9 years ago

Anyways so it looks like if I press the "right" key, it skips to the next actor on the right

def create_actor_command_window
    scene_battle_create_actor_command_window_abe
    @actor_command_window.set_handler(:dir4, method(:prior_command))
    @actor_command_window.set_handler(:dir6, method(:next_command)) #<----
  end

Easy enough: it calls next_command in Ace Battle Engine.

Then you go and look for that definition and see that FTB does something

alias scene_battle_next_command_ftb next_command
  def next_command
    if ftb_action?
      perform_ftb_action
    else
      scene_battle_next_command_ftb
    end
  end

Checking if it's an FTB action? Not sure how, let's look

def ftb_action?
    return false unless BattleManager.btype?(:ftb)
    return false if BattleManager.actor.nil?
    return false if BattleManager.actor.current_action.nil?
    action = BattleManager.actor.current_action.item
    return !action.nil?
  end

From what I'm seeing, it's an FTB action if there is an action and you're using FTB battle mode. Ok, so if you have an action, then it's FTB. If you don't have an action, then it calls the old next_command which basically just jumps to the next actor.

OK, so this is the key point: if you skip an actor, it will just go to the next actor. If you skip that actor as well, the action phase will begin and your actors will do nothing.

This also explains something else: if you have selected an action, then it will perform the FTB action. Because you want to queue up all the actions first, you decided to change it

def next_command
    if ftb_action?
      #perform_ftb_action
      queue_ftb_action
    else
      scene_battle_next_command_ftb
    end
  end

Again, if your actor has an action, then it will call queue_ftb_action. If you tried to skip the actor, and it already selected one action, then it will call queue_ftb_action.

We can now conclude that there's something in your logic that must be forcing the game to always choose Eric if Eric selected an action.

HimeWorks commented 9 years ago

So we look at queue_ftb_action

def queue_ftb_action
    puts("<FTB Plugin> queue_ftb_action")
    hide_ftb_action_windows
    @subject = BattleManager.actor
    item = @subject.input.item

    if item
      puts("\n<FTB Plugin> Queuing #{@subject.name} FTB Action #{item.name}.")
      puts("<FTB Plugin> Total Queued: #{@subject.actions.length}\n ")

      consume_ftb_action(item)
      @subject.action_input_index += 1
    end

    @subject = nil
    show_ftb_action_windows
    end_ftb_action #Moved here for visibility. (from: show_ftb_action_windows)
  end

There's nothing too interesting here. It's just making sure you update your AP. end_ftb_action is where we go.

def end_ftb_action
    puts("<FTB Plugin> end_ftb_action")
    if $game_party.inputable?
      select_next_member
    else
      status_redraw_target(BattleManager.actor)
      BattleManager.next_command
      turn_start
    end
  end

Eric has selected one action. You still have two AP available. Therefore, party is still inputable, and therefore it will select the next member.

def select_next_member

    status_redraw_target(BattleManager.actor)
    last_index = $game_party.battle_members.size - 1

    puts("<FTB Plugin> select_next_member #{BattleManager.actor.name} index(#{BattleManager.actor.index}, last(#{last_index}))")

    for member in $game_party.battle_members.reverse
      break if member.inputable?
      last_index -= 1
    end
    # next_command if next_ftb_member?(last_index)
    BattleManager.next_command if next_ftb_member?(last_index)

    return if BattleManager.actor.nil?
    if BattleManager.actor.index >= last_index && !BattleManager.actor.inputable?
      p 'prior'
      prior_command
    else
      start_actor_command_selection
      status_redraw_target(BattleManager.actor)
    end
  end

You have overwritten the old select_next_member, so we don't need to worry about anything else. And this...is not obvious what's going on.

HimeWorks commented 9 years ago

Note that I put in a "prior" output there to test if it occurs. It doesn't happen if I just skip everyone's turn. It doesn't happen if Eric selects one action and tries to skip his turn. It never happens.

If Eric is always being asked to select his action, then that means the BattleManager has set it to Eric. And it is always set to Eric, even if you try to skip his turn after selecting one action.

You'll have to go to BattleManager.next_command and see what it's doing. Perhaps it is being overwritten by other scripts as well.

Roguedeus commented 9 years ago

I think select_next_member and whats happening in the BattleManager via indexes is whats throwing me off big time. I think I am missing something regarding the windows actor selection and the resulting BattleManage.actor assignment... But I can't be sure.

In my build I have removed ftb_action? need for an action, as it will always return true now that actions aren't removed on selection like they would be normally in FTB. I also moved the index iteration outside of queue_ftb_action because @subject.input.item was sometimes nil, due to the index not updating BEFORE its checked...

Etc...

Roguedeus commented 9 years ago

Thanks for helping me logic through it. ;)

HimeWorks commented 9 years ago

Battlemanager is straightforward

   def self.next_command
    begin
      if !actor || !actor.next_command
        @actor_index += 1
        return false if @actor_index >= $game_party.members.size
      end
    end until actor.inputable?
    return true
  end

First, @actor_index is the index of the actor that will select a command. actor simply returns that actor

def self.actor
    @actor_index >= 0 ? $game_party.members[@actor_index] : nil
  end

First, if there is no actor, it will check the next member. If the actor cannot move, it will check the next member.

If no one can move anymore, then it will return false and the battle scene should realize that no more actors can input anymore and it's time to move on.

Here is the default method in Scene_Battle

def next_command
    if BattleManager.next_command
      start_actor_command_selection
    else
      turn_start
    end
  end

Taking what I said before: If no more actors can move, then start the turn. Done.

So now the problem. Look carefully

   def self.next_command
    begin
      if !actor || !actor.next_command
        @actor_index += 1   #<-------
        return false if @actor_index >= $game_party.members.size
      end
    end until actor.inputable? #<-------
    return true
  end

Index only changes if your actor can't move. If your actor can still move, then it will return true.

Can Eric move? YES. YES HE CAN.

He still has available AP!!

HimeWorks commented 9 years ago

This is why eric is always selected.

Roguedeus commented 9 years ago

Give me a minute to get my code up and digest through what you've posted. ;)

THANKS!

HimeWorks commented 9 years ago

Here is your problem:

(1) You want to skip Eric's turn. Eric has already selected one action. We know from yanfly's code that skipping an actor's turn involves checking if the actor has any actions. If there are actions, then it will process FTB action. But you've changed it to queue it up and get the next actor. But battle manager will always select Eric because he can still move. Back to (1). We're in a loop.

Roguedeus commented 9 years ago

I think I follow all of that. But I have a few questions... Give me a sec.

HimeWorks commented 9 years ago

I should start charging fees for script consulting lol kind of like corporations that hire people to come in and solve their problems.

Roguedeus commented 9 years ago

First:

BattleManager.actor_index is only changed when self.clear_actor, self.next_command and self.prior_command are called. So my change to Scene_Battle.next_command,

  def next_command
    puts("<FTB Plugin> Scene_Battle.next_command")
    if ftb_action?
      BattleManager.actor.action_input_index += 1
      queue_ftb_action
    else
      scene_battle_next_command_ftb
    end
  end

means self.next_command is only ever called in end_ftb_action... And since thats only called if the party is out of AP, it helps lock the actor on selection.

Now that I look, the default engine only ever actually calls BattleManager.next_command in Scene_Battle.next_command to that's pretty necessary.

However, before I changed it, FTB allowed for actor selection change freely between actions and I can't see how it managed to do that, given the same circumstance.

Being that BattleManager.actor_index was still decided independent of the player selecting them manually.