[SOLVED]Yanfly Chain Lightning modification

Discussion in 'Javascript/Plugin Support' started by woootbm, Jul 3, 2019.

  1. woootbm

    woootbm Super Sand Legend Veteran

    Messages:
    166
    Likes Received:
    92
    Location:
    'Murica
    First Language:
    English
    Primarily Uses:
    RMMV
    Hey again all!

    So I'm trying to create a chain lightning spell. Yanfly actually made a whole "Tips and Tricks" video about this using his Target Core and Damage Core plugins:

    Code:
    <Damage Formula>
    // Set the chain multiplier to a value if it doesn't exist.
    this._chainmultiplier = this._chainmultiplier || 1.0;
    // This is the damage formula.
    value = user.mat + 1000;
    // Apply the chain multiplier to the damage formula.
    value *= this._chainmultiplier;
    // Reduce the chain multiplier per target.
    this._chainmultiplier -= 0.2;
    </Damage Formula>
    
    <Custom Target Eval>
    // Adds the selected target to the target list.
    targets.push(target);
    // Grab the group of alive foes as candidates.
    var members = foes.aliveMembers();
    // Remove the target from the group of candidates.
    members.splice(members.indexOf(target), 1);
    // This is the number of extra targets to select with Chain Lightning.
    var extraTargets = 3;
    // Loop the extra targets.
    while (extraTargets--) {
      // Grab a random foe from the alive foes.
      var member = members[Math.floor(Math.random() * members.length)];
      // Check to see if the member exists.
      if (member) {
       // Add the member to the group of targets.
       targets.push(member);
       // Remove the member as a viable candidate.
       members.splice(members.indexOf(member), 1);
      }
    }
    </Custom Target Eval>
    
    I wanted to change this a little bit. I didn't want the less-damage-each-hit part, but that was easy to fix. I just removed the damage formula at the beginning. The next part was that I wanted it to be able to repeat targets if there are less targets available than hits. I made the following brute force version (I also lowered the targets by one, but that's easy enough to change):


    Code:
    <Custom Target Eval>
    targets.push(target);
    var members = foes.aliveMembers();
    members.splice(members.indexOf(target), 1);
    var extraTargets = 2;
    while (extraTargets--) {
    var member = members[Math.floor(Math.random() * members.length)];
    if (member) {
    targets.push(member);
    }
    }
    </Custom Target Eval>
    
    What happens with this one is that it works normally if there are enough targets. But if there aren't, it simply hits the last target for the remaining hits. Funny enough, if there's only one target it only hits once. So my bumbling about in the code almost worked.

    So, to sum this all up: say I have only two targets. How do I get this to bounce back and forth between the two for all of the hits?
     
    #1
  2. Astfgl66

    Astfgl66 Veteran Veteran

    Messages:
    649
    Likes Received:
    494
    First Language:
    French
    I find it best to go through this one line at a time, in order to understand what's going on.
    Let's run through your code 1 line at a time with 3 enemies [a,b,c]
    Initial target is a
    I'll put an explanation in comments as for the state of the program besides each important line

    Code:
    <Custom Target Eval>
    targets.push(target); //target is now [a]
    var members = foes.aliveMembers(); //members is now [a,b,c]
    members.splice(members.indexOf(target), 1); //members is now [b,c]
    var extraTargets = 2;
    while (extraTargets--) { //will run two times
    var member = members[Math.floor(Math.random() * members.length)]; //picks a random element in members and add it to the target array
    if (member) {
    targets.push(member); //lets say first loop adds b, second loop can add b or c again lets say b
    }
    }
    //targets is now [a,b,b], possibilities are [a,b,c] [a,c,b] [a,c,c]
    </Custom Target Eval>
    
    This but with 2 enemies a,b:

    Code:
    <Custom Target Eval>
    targets.push(target); // a is the initial target = [a]
    var members = foes.aliveMembers(); //members is now [a,b]
    members.splice(members.indexOf(target), 1); //removes a from the members array, members is now [b]
    var extraTargets = 2;
    while (extraTargets--) { //still runs two times
    var member = members[Math.floor(Math.random() * members.length)]; //picks a random element from members
    if (member) {
    targets.push(member); //members only contains b, so only it will be picked  and added to the array
    }
    }
    //targets is [a,b,b] this is the only solution
    </Custom Target Eval>
    

    This, but with only one enemy a:
    Code:
    <Custom Target Eval>
    targets.push(target); // a is the initial target = [a]
    var members = foes.aliveMembers(); //members is now [a]
    members.splice(members.indexOf(target), 1); //removes a from the members array, members is now []
    var extraTargets = 2;
    while (extraTargets--) {//still runs two times
    var member = members[Math.floor(Math.random() * members.length)]; //picks a random element from members and adds it to the array,
    if (member) {
    targets.push(member); //but members is empty
    }
    }
    //targets is [a]
    </Custom Target Eval>
    
    Do you see where you went wrong?

    Hint:
    You shouldn't remove the initial target if you want the spell to bounce back to it.
     
    #2
  3. woootbm

    woootbm Super Sand Legend Veteran

    Messages:
    166
    Likes Received:
    92
    Location:
    'Murica
    First Language:
    English
    Primarily Uses:
    RMMV
    Ah! I see. Somewhat :hswt: So I removed the first members.splice part. This made it so it always gets all of the jumps with less targets. But this created 2 problems (which I'm pretty sure are actually the same problem):

    1. When there is more than 1 target, it can target the same enemy twice in a row, removing the illusion of it "bouncing" between targets.
    2. When there is 1 target, it just hits that target every single time.

    So I need to somehow tell it to not hit the "previous" target, but to not completely remove that target from the list of possibilities forever. Just for a single bounce.
     
    #3
  4. Astfgl66

    Astfgl66 Veteran Veteran

    Messages:
    649
    Likes Received:
    494
    First Language:
    French
    I see. You're absolutely right that this is the same problem in both cases.

    How would you do that then?
    What's the plain english, not code, step by step logic that would work?

    Writing code is basically translating a logic program into something the computer understands.
    You will never be able to make things work if you don't get the logic right.

    Hint:
    You want to move the members definition inside the loop so it does the calculation everytime instead of only before the loop.
    Then you remove the last target from the members array before picking one.

    proposed logic

    Code:
    get first target, store it in array
    loop for two times
      get a list of all targets
      remove last member of target array from list
      get a random element from remaining possible targets
      add it to the target array
    end loop
    
     
    #4
  5. woootbm

    woootbm Super Sand Legend Veteran

    Messages:
    166
    Likes Received:
    92
    Location:
    'Murica
    First Language:
    English
    Primarily Uses:
    RMMV
    I've been trying to get this to work with your advice, and so far it hasn't. Testing it against a fight with 2 enemies, I'm always either getting bounces of a, b or it hits all three but combos of a, b, b or a, a, b are still possible, rather than the desired a, b, a happening 100% (and with more enemies a, b, a would be possible as well, but so would a, b, c etc).

    What I've been doing is simply re-arranging the code I have, though. Since my understanding of Java is so low that modifying any lines either results in a script error or something stupid (like it only hits one target 3 times, or it only hits it once). I feel like some part of this needs to be modified somewhere.

    Specifically, I'm thinking the "var member = members[Math.floor(Math.random() * members.length)];" part is what needs to be changed. Like it needs something to tell it "but not the last target". The "members.splice(members.indexOf(target), 1);" part might also be the culprit, because it seems like it removes a target from the list forever, rather than being a one-time deal.

    For reference, how I perceived your proposed logic was as such:

    Code:
    <Custom Target Eval>
    targets.push(target);
    var extraTargets = 2;
    while (extraTargets--) {
    var members = foes.aliveMembers();
    members.splice(members.indexOf(target), 1);
    var member = members[Math.floor(Math.random() * members.length)];
    if (member) {
    targets.push(member);
    }
    }
    </Custom Target Eval>
    
     
    #5
  6. Astfgl66

    Astfgl66 Veteran Veteran

    Messages:
    649
    Likes Received:
    494
    First Language:
    French
    You've got it mostly right :), again this can be solved by just reading the code out loud.
    Let's do it: three enemies, a b, c initial target a

    Code:
    <Custom Target Eval>
    targets.push(target); //targets is now [a]
    var extraTargets = 2;
    while (extraTargets--) { //first time //second time
    var members = foes.aliveMembers(); //members is [a,b,c] //second loop: members is now [a,b,c]
    members.splice(members.indexOf(target), 1); //removes initial target from members array, members is [b,c] //removes initial target again, members is [b,c]
    var member = members[Math.floor(Math.random() * members.length)]; //gets random member from members array //gets random member from members array again
    if (member) {
    targets.push(member); //let's say b, targets is now [a,b] //let's say b, targets is now [a,b,b]
    }
    }
    </Custom Target Eval>
    
    Do you see where you went wrong?

    Hint
    The target variable contains the initial target, not the last decided target.
    You want to remove the last element of targets from the members array instead inside the loop.
     
    #6
  7. woootbm

    woootbm Super Sand Legend Veteran

    Messages:
    166
    Likes Received:
    92
    Location:
    'Murica
    First Language:
    English
    Primarily Uses:
    RMMV
    Haha, well I didn't really understand how to tell it "last target" instead of first target because I didn't quite get the whole "members.indexOf(target), 1". Eventually I tried "members.splice(members.indexOf(member), 1);" because I figured that "1" would be the first "member" that it was creating by the random roll. That didn't work. But, for some reason, using the command a second time during the "if" statement did. Here's what I have:

    Code:
    <Custom Target Eval>
    targets.push(target);
    var extraTargets = 2;
    while (extraTargets--) {
    var members = foes.aliveMembers();
    members.splice(members.indexOf(member), 1);
    var member = members[Math.floor(Math.random() * members.length)];
    if (member) {
    targets.push(member);
    members.splice(members.indexOf(member), 1);
    }
    }
    </Custom Target Eval>
    
    I've tested this multiple times with troops of sizes 1, 2, and 8. And so far it seems to work? I find the whole thing kind of baffling. I would think I need that splice command only once, in either one of those positions. But neither of those work. Only when I do both. What are your thoughts on this?

    I hope further testing down the road doesn't result in script errors :hswt2:
     
    #7
  8. Astfgl66

    Astfgl66 Veteran Veteran

    Messages:
    649
    Likes Received:
    494
    First Language:
    French
    I don't really get why this works. I'd do the tried and true technique of reading it out loud, but I kinda don't have the time right now.

    Here's my proposed solution:

    targets is an array.
    Arrays have a length property, basically the number of elements in the list,; 1 for 1 element, 2 for 2, so on.
    Arrays are numbered from 0, so to get the last element of an array you just have to do:
    array[array.length - 1]

    Here it'd be:
    Code:
    members.splice(members.indexOf(targets[targets.length-1]), 1)
    
    I'd advise modifying your code to that, since having code you don't understand is kinda bad down the road if errors arise.

    Googling "how to get last member of an array in javascript" would have probably saved you a lot of time and trial and error.
    Same about the splice function, googling how it works and trying it out in the console would probably save you a lot of future headaches. Array manipulation is essential stuff which you'll run into every time you want to create a numbered list. Which is pretty much all the time.

    You get no kudos for a working answer if you cannot tell me how it works. :kaojoy:
    Still I'm glad you've got this sorted out, and I'll be even happier if you learned something about how the code works.
    Happy making!
     
    Last edited: Jul 6, 2019
    #8
    woootbm likes this.
  9. woootbm

    woootbm Super Sand Legend Veteran

    Messages:
    166
    Likes Received:
    92
    Location:
    'Murica
    First Language:
    English
    Primarily Uses:
    RMMV
    Ah, this new code works as well.

    I do appreciate learning, but I'm not sure I properly described my ineptitude with Javascript. Like, you say read it out loud but- to me- I'm reading a lot of stuff I just don't understand. Compare it to an actual language like, say, Spanish. I know the basics and some easy words, so I might be able to pronounce a sentence written in Spanish. But considering I don't know like 85% of the words that make up the language, actually understanding what the hell I'm saying is another thing entirely. I'm pretty sure I know less about Java than Spanish, heh.

    Like take that latest code. That length - 1 part is surprising for sure. I read that as just changing it from "out of three" to "out of two", but I guess that ",1" is what tells it to remove the last thing done. I guess I thought that meant the first target overall, rather than some temporary assignment of an index made by the other calls. Hm!

    I did try to Google the answer, but I usually search for things specifically related to MV. Searching for just regular Javascript is the thing I've learned here then. My searches turned up things that were... not helpful.

    Anyway, thanks again! :thumbsup-right:
     
    #9
  10. Astfgl66

    Astfgl66 Veteran Veteran

    Messages:
    649
    Likes Received:
    494
    First Language:
    French
    Unless you're doing cutting edge programming your problems have already been solved by someone smarter than you. That works for everyone.
    The hard part about programming often isn't really the language, it's the logic. If you can write a logic program that will work you can then google how to write every single thing of that program in javascript and it will work. And you already know how to write logic programs, because that's what eventing is all about, praise RPGMaker for easing us into that stuff.

    Here's a summary of how I did such things when I started programming three years ago:

    get first target, store it in array
    loop for two times
    get a list of all targets
    remove last member of target array from list
    get a random element from remaining possible targets
    add it to the target array
    end loop

    What is an array ? Oh it's a numbered list of things, it starts counting from 0.
    How do I store things in it? Oh I just have to push things in it, then it gets into the next position
    How do I loop two times ? Google says using for loops is usually the norm, and provides the syntax
    How to remove an element from an array ? Google says splice.
    How to get last element from array? Google says length - 1, because length is the number of elements (so 2 for 2 elements) but the numbering starts from 0 (so the last element is array[1] in a 2 element array), generalizing in a n element array the last element is array[n-1].

    Having answered my fair share of plugin requests and related questions, I can tell you that if you come to the js forums having the base logic down and only needing the rpg maker part, you'll have a near instantaneous answer.

    Have a nice day!
     
    #10

Share This Page