[RESOLVED] Learn Skills from using other Skills

Started by Viviatus, August 25, 2008, 04:50:14 am

Previous topic - Next topic

Viviatus

August 25, 2008, 04:50:14 am Last Edit: August 31, 2008, 06:44:27 pm by Blizzard
Pretty simple, the whole thing is. I want a script which enables my characters to learn new skills from previously learnt ones.

Arshes used "Fire" 50 times therefore he learns "Greater Fire"
Gloria used "Fire" 25 times therefore she learns "Greater Fire"

Every actor has his/her own speed at learning new skills, and not every actor can learn every skill. In order to make everyone a little unique.
Some skills actually need more than one skill to be used x times but two or even more

Hilda used "Fire" 80 times, "Ice" 90 times and "Thunder" 5 times therefore she learns "Burst"

Also some skills teach more than one skill

Basil used "Heal" 100 times therefore she learns "Heal Force" and "Remedy"

That should do. As I already said the idea is pretty simple. But since I've never seen such a script before, no wait, there WAS one, back in the rmxp.net times... But it wasn't working.
Also it would be awesome if the actors could learn new skills by using items on them (like it was in rm2k/2k3) simply to prevent the actors from learning powerful skills all too fast.
Or also by equipping certain armour (which can be done using EQUAP Skill in Blizzys addons)

If you (who will be willing to do this for me) have any questions or need more information, I'd be glad to help you out!

Thanks in advance!

Love, Vivvles

Fantasist

Something even I've been after ;) I think it's time I give it a try. If you have that script, could you post it so I can peep and probably get a head start?
Do you like ambient/electronic music? Then you should promote a talented artist! Help out here. (I'm serious. Just listen to his work at least!)


The best of freeware reviews: Gizmo's Freeware Reviews




Berans

I can imagine the hardest part of this script will be making it relatively easy to set up. I've actually got some nice ideas on how to set up something like that though :P
I might let you know if you ask nicely lol, but maybe I'll give a similar system a pop, I'm after making useful customizations for my own game, and this kind of thing inspires me

Sally

wow this is a good idea, i would use it. :D

Viviatus

Quote from: Fantasist on August 25, 2008, 07:37:03 am
Something even I've been after ;) I think it's time I give it a try. If you have that script, could you post it so I can peep and probably get a head start?


Sorry, to say, but I don't have that script anymore, it wasn't working anyways and was designed for an ABS. If I remember correctly the setup for this script was stored in an .rxdata I could be wrong, though.
I'd appreciate ANYONE who'd give it a try. So please, try to work out something.

Fantasist

August 25, 2008, 11:17:24 am #5 Last Edit: August 25, 2008, 12:53:29 pm by Fantasist
I'm trying this, but I can't promise anything yet... I figured out what needs to be modded, but I still need to plan how I do it, since you've asked for a lot of variety (different rates for different actors for example).

EDIT:
I managed this except one thing: using multiple skills different times to learn one/more skill(s), or:
QuoteHilda used "Fire" 80 times, "Ice" 90 times and "Thunder" 5 times therefore she learns "Burst"


I've managed:
- Actor-specific setup
- Learning multiple skills by using one skill some x times.

QuoteI can imagine the hardest part of this script will be making it relatively easy to set up.

phew, you're absolutely right. I can make this work perfectly for my projects but others might get confused...

@Viviatus: One more thing. Do you want the skills to be learnt on-the-fly or after the battle is complete?
And do you want missed skills to count?
Do you like ambient/electronic music? Then you should promote a talented artist! Help out here. (I'm serious. Just listen to his work at least!)


The best of freeware reviews: Gizmo's Freeware Reviews




Viviatus

Learnd after battle would be fine. would be cool if there was a damage pop-up telling "+ Greater Fire" or anything, cause I handle my levelups and stuff via damage pop-ups. Just using the skill should do, meaning missed skills should count as well.

Thanks in advance!

Btw: I made this request like 4 times on rmxp.org many people would've wanted this system but no one ever cared, thanks for this quick try!

Fantasist

August 25, 2008, 02:05:02 pm #7 Last Edit: August 25, 2008, 02:27:54 pm by Fantasist
Don't worry about it :)

Okay, so I'm trying to get the Hilda example to work, but it would need VERY confusing config. The damage popups are a piece of cake, consider it done (two lines of code :D). So the skills should be learned after battle and missed skills also count. Hang on for 5 mins, I'll post the script..

EDIT: It's taking longer than I thought. I should be sleeping now... do you need it urgently? I'll complete this in a couple of days.

PS: I'm not familiar with the battle system, so there's always a chance tha the sstem won't behave as we want it to. I'll test it as best as I can anyway, but one can never be sure  (Stupid bugs >> :evil:)
Do you like ambient/electronic music? Then you should promote a talented artist! Help out here. (I'm serious. Just listen to his work at least!)


The best of freeware reviews: Gizmo's Freeware Reviews




Viviatus

Nah, take your time, as long as I can be sure it's being done I'll be fine!

Fantasist

QuoteThe damage popups are a piece of cake, consider it done (two lines of code :D).

It was more than 2 lines -_-'

Anyway, here's the first draft, which works:

#==============================================================================
# ** module FTS
#==============================================================================

module FTS
 
  module_function
 
  def learn_new_skills?(actor_id, skill_id)
    case actor_id
    #================================
    # Config Begin
    #================================
    #     when A then [B, C]
    # A - Cast skill ID (eg: 57)
    # B - Cast A how many times? (eg: 6)
    # C - ID(s) of skill(s) to learn. It can be a number or
    #       an array of numbers (eg: 13, 21, 9, [2, 3], [1,3,7,35,64])
    #================================
    when 1 # when actor_id
      a = case skill_id
      # when skill with ID 57 is used 1 time, actor learns skills 6 and 7
      when 57 then [1, [6, 7]]
        # when skill with ID 6 is used 1 time, actor learns skill 13
      when 6 then [1, 13]
      else false
      end
    #----------------------------------------------------------------
    # Actor Seperator
    #----------------------------------------------------------------
    when 7 # when actor_id
      a = case skill_id
      when 1 then [1, [8, 9]]
      else false
      end
    #================================
    # Config End
    #================================
    end
    a[1] = [a[1]] if a && !a[1].is_a?(Array)
    return a
  end
 
end

#==============================================================================
# ** class Game_Actor
#==============================================================================

class Game_Actor
 
  attr_accessor :skill_learns, :skill_uses
 
  alias fts_skill_learning_game_actor_setup setup
  def setup(actor_id)
    fts_skill_learning_game_actor_setup(actor_id)
    skill_list = load_data('Data/Skills.rxdata')
    @skill_uses = Array.new(skill_list.size + 1, 0)
    @skill_uses[0], @skill_learns, skill_list = id, [], nil
  end
 
  def skill_use_count(skill_id)
    return @skill_uses[skill_id]
  end
 
end

#==============================================================================
# ** class Scene_Battle
#==============================================================================

class Scene_Battle
 
  alias fts_skill_learning_scene_battle_skill_action_result make_skill_action_result
  def make_skill_action_result
    battler = @active_battler
    @skill = $data_skills[battler.current_action.skill_id]
    if battler.is_a?(Game_Actor) && battler.skill_can_use?(@skill.id)
      battler.skill_learns = [] if battler.skill_learns == nil
      battler.skill_uses[@skill.id] += 1
      s = FTS::learn_new_skills?(battler.id, @skill.id)
      if s && battler.skill_use_count(@skill.id) == s[0]
        s[1].each {|i| battler.skill_learns.push(i)}
      end
    end
    fts_skill_learning_scene_battle_skill_action_result
  end
 
  alias fts_skill_learning_scene_battle_init5 start_phase5
  def start_phase5
    fts_skill_learning_scene_battle_init5
    @phase5_wait_count, @new_skills = 0, []
    $game_party.actors.each_with_index {|actor, i| actor.skill_learns.each {|id|
    @new_skills.push([i, id])}}
    @learn_skill_count = @new_skills.size
  end
 
  alias fts_skill_learning_scene_battle_upd5 update_phase5
  def update_phase5
    if @learn_skill_count > 0
      if @learn_skill_count == @new_skills.size
        a = @new_skills.shift
        actor = $game_party.actors[a[0]]
        actor.learn_skill(a[1])
        actor.damage = "+ #{$data_skills[a[1]].name}!"
        actor.damage_pop = true
      end
      @learn_skill_count -= 1
      @phase5_wait_count = 1 if @new_skills.size == 0
    end
    fts_skill_learning_scene_battle_upd5
  end
 
end


I hope you can understand that easily, because if you REALLY want that Hilda example, it'll be very confusing.

And also, here's a scene where you can check the details of learning. It's not much, it's a draft:


#==============================================================================
# ** Window_Skill
#==============================================================================

class Window_SkillList < Window_Selectable
 
  def initialize(actor)
    super(0, 64, 240, 416)
    @actor = actor
    @column_max = 1
    refresh
    self.index = 0
  end
 
  def skill
    return @data[self.index]
    p @data[self.index]
  end
 
  def refresh
    if self.contents != nil
      self.contents.dispose
      self.contents = nil
    end
    @data = []
    for i in 0...@actor.skills.size
      skill = $data_skills[@actor.skills[i]]
      if skill != nil
        @data.push(skill)
      end
    end
    @item_max = @data.size
    if @item_max > 0
      self.contents = Bitmap.new(width - 32, row_max * 32)
      for i in 0...@item_max
        draw_item(i)
      end
    end
  end
 
  def draw_item(index)
    skill = @data[index]
    self.contents.font.color = normal_color
    x = 4
    y = index * 32
    rect = Rect.new(x, y, self.width / @column_max - 32, 32)
    self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
    bitmap = RPG::Cache.icon(skill.icon_name)
    opacity = self.contents.font.color == normal_color ? 255 : 128
    self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
    self.contents.draw_text(x + 28, y, 204, 32, skill.name, 0)
    #self.contents.draw_text(x + 232, y, 48, 32, skill.sp_cost.to_s, 2)
  end
 
  def update_help
    @help_window.set_text(self.skill == nil ? "" : self.skill.description)
  end
 
end
class Window_SkillInfo < Window_Base
 
  def initialize(actor)
    @actor = actor
    super(240, 64, 400, 416)
    self.contents = Bitmap.new(width - 32, height - 32)
    self.contents.font.size = 28
    contents.draw_text(0, 0, contents.width, 64, @actor.name, 1)
    self.contents.font.size = 22
    refresh
  end
 
  def refresh(skill=nil)
    return unless skill
    self.contents.fill_rect(0, 64, contents.width, contents.height - 64, Color.new(0, 0, 0, 0))
    contents.draw_text(4, 64, contents.width - 8, 32, "Times used: #{@actor.skill_use_count(skill.id)}")
    a = FTS::learn_new_skills?(@actor.id, skill.id)
    return unless a
    contents.draw_text(4, 96, contents.width - 8, 32, "When used #{a[0]} time(s), learns:")
    list = a[1]
    list.size.times {|i| skill_id = list[i]
    rect = Rect.new(4, 128 + i * 32 + 4, 24, 24)
    contents.fill_rect(rect, Color.new(0, 0, 0, 0))
    bitmap = RPG::Cache.icon($data_skills[skill_id].icon_name)
    contents.blt(rect.x, rect.y, bitmap, Rect.new(0, 0, 24, 24))
    contents.draw_text(32, 128 + i * 32, contents.width - 36, 32, "#{$data_skills[skill_id].name}")}
  end
 
end
class Scene_SkillLearnInfo
 
  def initialize(actor_index=0)
    @actor_index = actor_index
    $data_skills = load_data('Data/Skills.rxdata') unless $data_skills
  end
 
  def main
    @actor = $game_party.actors[@actor_index]
    @help_win = Window_Help.new
    @list_win = Window_SkillList.new(@actor)
    @list_win.help_window = @help_win
    @info_win = Window_SkillInfo.new(@actor)
    @info_win.refresh(@list_win.skill)
    Graphics.transition
    loop do
      Graphics.update
      Input.update
      update
      break if $scene != self
    end
    Graphics.freeze
    @help_win.dispose
    @list_win.dispose
    @info_win.dispose
  end
 
  def update
    @help_win.update
    @list_win.update
    @info_win.update
    if Input.trigger?(Input::B)
      $scene = Scene_Map.new
    elsif Input.trigger?(Input::L)
      new_index = (@actor_index - 1) % $game_party.actors.size
      $scene = Scene_SkillLearnInfo.new(new_index)
    elsif Input.trigger?(Input::R)
      new_index = (@actor_index + 1) % $game_party.actors.size
      $scene = Scene_SkillLearnInfo.new(new_index)
    elsif Input.repeat?(Input::UP) || Input.repeat?(Input::DOWN)
      @info_win.refresh(@list_win.skill)
    end
  end
 
end


For calling the scene, you can use this call script command:

$scene = Scene_SkillLearnInfo.new
Do you like ambient/electronic music? Then you should promote a talented artist! Help out here. (I'm serious. Just listen to his work at least!)


The best of freeware reviews: Gizmo's Freeware Reviews




Viviatus

Yay! Works perfectly! Thanks alot! And Actually I understand every single line, so that the config isn't confusing at all! And you even added a progress window *fuiiii <- whistle* great job. Only two thing's that're buggin me:

1.  Apparently you didn't add the function from one of the examples so far "using several skills to learn one", I hope that's yet to come?

2. If one skill can teach two skill but at different times being used, the later taught one won't be learnd at all. Saying "Healing" can teach "Greater Healing" at 50 uses but also "Remedy" at 25. Greater Healing won't be taught at all. Not THAT bad, but still kinda wanted by me :-p

Once I'm getting on your nerves tell me! :D


And thank you very much, for what you did so far! Great job, really!

Fantasist

QuoteAnd you even added a progress window

You mean the scene where you check your skill info? I'll come up with a better one eventually :D

Quote1.  Apparently you didn't add the function from one of the examples so far "using several skills to learn one", I hope that's yet to come?

Of course, I just need some more time.

Quote2. If one skill can teach two skill but at different times being used, the later taught one won't be learnd at all. Saying "Healing" can teach "Greater Healing" at 50 uses but also "Remedy" at 25. Greater Healing won't be taught at all. Not THAT bad, but still kinda wanted by me :-p

...ouch -_- I'll try my best, I think it can be done, but the config method will change, probably drastically. There's only one requirement from your end: don't get confused with too many [s[dfb[gfggg], [hgn],gn]. I don't think you will, but anyway, I'll work on this and we'll see how it turns out.

QuoteOnce I'm getting on your nerves tell me!


And thank you very much, for what you did so far! Great job, really!

You know what? This is experience to me, so you're paying me off too. I plan on making custom scripts for my game (and use Blizz's scripts meanwhile) and for that, I need to understand how all the default systems work, and this is definitely helping me :)
Do you like ambient/electronic music? Then you should promote a talented artist! Help out here. (I'm serious. Just listen to his work at least!)


The best of freeware reviews: Gizmo's Freeware Reviews




Viviatus

Alright, then I shall stop worrying... and, well you, know I have more things I'd like to get realised... ^^

Berans

August 26, 2008, 08:37:46 pm #13 Last Edit: August 27, 2008, 04:47:08 am by Berans
My idea for a config that is possible to use in this system would be by using hashes, something like this:
SKILLS_TO_LEARN = {1 => [[3,50],[10,60]], 2=>[[12,10]]}
So that the key in the hash is the learnable skill's ID, and the array following it is filled with sub-arrays that each indicate required_skill_id,uses.
I'm not sure how practical it really is to use, but I think that's how I would set it up :P
just throwing it out there

Fantasist

Excerpt from Blizz's Tute: ShowHide
Never use hash for configurations! Don't let users set up data and/or configurations with hash! Those would be very bad mistakes and only show people who have understandings of this method that you don't. The problem is that hashs just are not efficient enough in comparison to arrays if you don't use large amounts of data which you rarely will in case RGSS Ruby.
There is a clever alternative to hash, which works much faster and is even much easier to overview. This alternative is excellent for configurations by the user and mapping data similar to how hash actually works. The actual trick is that the data mapping IS spread, but the actual data in the RAM is stored as one block. In the praxis it has proven to be way easier to use by users. This alternative is a simple conversion method. As argument the method gets a key which it will transform to the corresponding value. The only disadvantage of such a method compared to hash is that it's constant data. You cannot change the mapping during the game at all. But every non-constant configuration is stored in special classes anyways (see 7.2. for more information) so this method is as good as 100%-ly in advantage compared to hash configurations.
Example: Let's say you want equipment parts to trigger some special skills. An example would be a method like using the template when EQUIP_ID then return SKILL_ID:

def trigger_skill(id)
  case id
  when 1 then return 12
  when 4 then return 31
  when 7 then return 9
  when 12 then return 4
  when 81 then return 27
  end
  return 0
end


An equivalent with hash:

TRIGGER_SKILL = {}
TRIGGER_SKILL[1] = 12
TRIGGER_SKILL[4] = 31
TRIGGER_SKILL[7] = 9
TRIGGER_SKILL[12] = 4
TRIGGER_SKILL[81] = 27


Or the other way hash can be defined:

TRIGGER_SKILL = {1 => 12, 4 => 31, 7 => 9, 12 => 4, 81 => 27}


The last example is awful. It's quite hard to overview, especially for somebody who is not a programmer. The second example could pass, but I would say using ANSI syntax like in the first example should be easiest to understand. You can almost read it like "when id is 1 then skill is 12, when id is 4 then skill is 31..." The second example isn't as easy to read. Using a conversion method will make codes easier to overview and especially configurations the user has to set up. It also uses only the RAM required for code execution, which DOES take more space than the hash would, but it's stored as a data block and that way it will be way quicker in the CPU cache. Also keep in mind that memory storing and loading operations are 10 to 15 times slower than arithmetical-logical operations. Accessing one hash piece requires some time, another one far away takes the same amount of time again, etc. while accessing one and the same method a couple of times is much faster. That in both cases the parameter carry-overs needed for method execution (yes, getting a hash element by using a key is a method) took also time to be stored on the system stack as well as the return address to the current command was not taken into account as it happens in both cases and doesn't give any of those methods an advantage.


So I'll only use hash if the user has a VERY extensive and large list of skills to be learnt. The same thing can be done with data mapping through methods, but the problem is the depth of the arrays needed. Let's just assume we're using hashes for this, because it won't make any difference to the problem I have.

Let's start with your example:
SKILLS_TO_LEARN = {1 => [[3,50],[10,60]], 2=>[[12,10]]}


So when skill ID is 1:
  if number of uses is 3, then learn skill 50
  if number of uses is 10 then learn skill 60

Now you have an array of the possible number of times of use. But then, the skills to be learnt can be multiple, like this:
when 1 is used 10 times, learn 2 and 3.
So for that, I should have an array of learnable skills instead of just the required ID.

All of this makes config a bit confusing and coding it would be difficult too.
Do you like ambient/electronic music? Then you should promote a talented artist! Help out here. (I'm serious. Just listen to his work at least!)


The best of freeware reviews: Gizmo's Freeware Reviews




Berans

Actually, my idea was a little different from how you explained it
What I meant was
the learnable skills in that hash are 1 and 2
to learn skill 1,  skill 3 needs to be used 50 times AND skill 10 needs to be used 60 times
to learn skill 2, skill 12 needs to be used 10 times.
Wouldn't that also allow for multiple skills being unlocked by a single skill?
i.e. if skills 1 and 2 both require skill 3 to be used 50 times

SKILLS_TO_LEARN = {1 => [[3,50]], 2 => [[3,50]]}
as soon as skill 3 has been used 50 times, the condition for both those skills has been met, et voila.

Fantasist

But then, I would have to check the whole hash to determine which skills were used how many times. I need the key of the hash to be the skill used, not the result. I've worked on it and it finally works with all the features Viviatus wanted:
- Actor specific.
- Learn one/more skill(s) from using a skill a certain number of times:
QuoteArshes used "Fire" 50 times therefore he learns "Greater Fire"

- Learn skills from the same skill used different number of times:
Quote2. If one skill can teach two skill but at different times being used, the later taught one won't be learnd at all. Saying "Healing" can teach "Greater Healing" at 50 uses but also "Remedy" at 25. Greater Healing won't be taught at all. Not THAT bad, but still kinda wanted by me :-p

- Learn skill(s) by using different skills different times:
Quote1.  Apparently you didn't add the function from one of the examples so far "using several skills to learn one", I hope that's yet to come?


I just need to give the script some finishing touches and proper instructions. btw, for the last feature, a different function is used for multiple skill requirements, where the key is an actor and ALL the conditions are checked everytime a skill is cast. I'm not happy about it...
Do you like ambient/electronic music? Then you should promote a talented artist! Help out here. (I'm serious. Just listen to his work at least!)


The best of freeware reviews: Gizmo's Freeware Reviews




Viviatus

When you're not happy about it how can I be? You don't need to rush it. I'm not planning to release a Demo of my game and I only finished half of the game itself, so plenty of work for me left...

Fantasist

August 27, 2008, 11:04:04 am #18 Last Edit: August 27, 2008, 11:06:36 am by Fantasist
QuoteALL the conditions are checked everytime a skill is cast.

The only other alternative I can think of is executing that code some other time instead of when the skill is being cast (during phase5 of battle scene, aka the scene where battle ends). Of course, it works perfectly even now and there's not even a sign of lag. It's just a coder's obsession regarding the code being efficient. I wonder what Bliz would've done... I'll have to ask him later. I'll go through the script once again and come up with a presentable version.

Oh, and I've made the config a little bit easier to set up :D
Do you like ambient/electronic music? Then you should promote a talented artist! Help out here. (I'm serious. Just listen to his work at least!)


The best of freeware reviews: Gizmo's Freeware Reviews




Viviatus

You're gorgeous! But, sure go ahead and ask Blizzy, greet him btw. I used to really drive him mad with requests :D