Jump to content
Kayzee

Keyword tagged item based fusion/crafting system?

Recommended Posts

So here is something weird I have been working on a bit. I wrote a few scripts that let me create lists of items based on keywords that the items are tagged with in notes. I mostly use it for shops, mostly so shops don't have to be redone if I need to rearange the order of items and I can easily make shops sell perticular kinda of items.

 

Anyway, just for an experment, I make a little thing that let you pick two items, get the list of keywords both items use, and make a list of items that had those keywords, sorted by how many keywords matched. For example if I have a normal staff tagged "staff" and a object tagged "holy" it will try to match any object that has both "holy" and "staff".

 

It's probobly not too remarkable a idea, I just wondered if it would make a good base for a item fusion/crafting system. I could probobly add secondary keywords like materials if I wanted to, and limit results based on item level/rarity or something. It strikes me as a lot more interesting then the recipe systems people seem to like for whatever reason.

 

What do you guys think?

Share this post


Link to post
Share on other sites

Interesting...

It is more like Skyrim alchemy but in slightly different way. Meanwhile in Skyrim, any ingrendient tagged with poison will make poison if two ingrendient are fused and has same tag. Of course if those two ingrendient has both poison and heal will make the result item is inefficient.

 

If you could do that, you can just only define the ingrendient item without thinking what is the result. And the result item would be anything. Even developer will not know all possibility of his crafting system.

 

But I believe it's pretty hard to make.

Edited by TheoAllen

Share this post


Link to post
Share on other sites

Well the system just searches through preset items right now. It's not impossible to make objects that take triats from it's "parent" items, but that involves the whole invintory system being reworked for instance items. And though instance items scripts exist already they are all a pain in the ass and I am not really all that happy with them.

Share this post


Link to post
Share on other sites

Alternatively, you could just create massive numbers of item copies, each with their own prefixes and suffixes and many permutations of these.

This, of course, is easier but way more tedious.

 

It's a very nifty idea, though. You can see it's potential applications in:

  • Inventory Sorting
  • Crafting of Items
  • Selecting desirable traits/upgrades for your items

Share this post


Link to post
Share on other sites

Though if I was going to make tons and tons of items I rather make lots of really strange items rather then suffixes and prefixes. More like a random sand worm tooth and a knife could make a Crysknife. The idea is kind of inspired by Homestuck and it's collection of billions of possible weird fusion objects. Heck if I wanted to I could probably do a direct copy and use bitflags rather then keywords, but that takes a bit more care to set up and isn't as flexible in the long run.

 

Technical aside:

 

If anyone is curious about the code I use, I use this script to set the keywords on items and use this:

 

 

class RPG::BaseItem
  
  def keywords
    @keywords ||= self.get_keywords
  end
  
  def get_keywords
    self.note_field[:keywords] || []
  end

end

# Special array wrapper class for fetching and filtering lists of items.

class ItemList

  # Class Methods
  
  def self.collect_from_array(a, i)
    return self.new(i.collect {|n| a[n]}) if i.is_a?(Enumerable)
    return self.new([a[i]])
  end
  
  def self.item(i)
    return self.collect_from_array($data_items, i)
  end
  
  def self.armor(i)
    return self.collect_from_array($data_armors, i)
  end
  
  def self.weapon(i)
    return self.collect_from_array($data_weapons, i)
  end
  
  def self.all_items
    return self.new(($data_items | $data_armors | $data_weapons).compact)
  end
  
  def self.get_item_fusions(item1, item2)
    k = item1.keywords | item2.keywords
    puts(k)
    a = self.all_items.filter_keyword_fusion(k) | [item1, item2]
    a = self.new(a.shuffle!).sort_by_keyword_match(k)
    return a
  end
  
  # Instance Methods
  
  def initialize(array = [])
     @data = array.to_a
  end
  
  # Dispatch any undefined methods to the @data array
  def method_missing(method, *args, &block)
    if @data.respond_to?(method)
      tmp = @data.send(method, *args, &block)
      # Always return a ItemList instead of an array
      tmp = ItemList.new(tmp) if tmp.is_a?(Array)
      return tmp
    else
      super
    end
  end 
  
  # Type cast methods, to prevent the default of always returning an ItemList

  def to_a
    return @data.to_a
  end
  
  def to_ary
    return @data.to_ary
  end

  # Filters
  
  def filter_keyword(keywords, number = 1)
    @data = @data.compact.select do |i|
      (i.keywords & keywords).count >= number
    end
  end
  
  def filter_keyword_fusion(keywords)
    @data = @data.compact.select do |i|
      (i.keywords & keywords).count >= (i.note_field[:fusion_match] || 2)
    end
  end
  
  def filter_range(sym, min = nil, max = nil)
    @data = @data.compact.select do |i|
      value = i.send(sym)
      (min ? value >= min : true ) && (max ? value <= max : true)
    end
  end
  
  def sort_by_keyword_match(keywords)
    @data = @data.compact.sort do |x,y| 
      (y.keywords & keywords).count <=> (x.keywords & keywords).count
    end
  end
  
end

 

 

Actualy I have two versions of this script. The one above that use it's own class, and this older one that dosn't:

 

 

class RPG::BaseItem
  
  def keywords
    @keywords ||= self.get_keywords
  end
  
  def get_keywords
    self.note_field[:keywords] || []
  end
  
end

class Game_Interpreter
  
  def item(i)
    return i.collect {|n| $data_items[n]} if i.is_a?(Enumerable)
    return $data_items[i]
  end
  
  def armor(i)
    return i.collect {|n| $data_armors[n]} if i.is_a?(Enumerable)
    return $data_armors[i]
  end
  
  def weapon(i)
    return i.collect {|n| $data_weapons[n]} if i.is_a?(Enumerable)
    return $data_weapons[i]
  end
  
  def all_items
    return $data_items | $data_armors | $data_weapons
  end
  
  def get_armors_by_type(type)
    $data_armors.compact.select {|a| a.atype_id == type}
  end
  
  def get_weapons_by_type(type)
    $data_weapons.compact.select {|w| w.wtype_id == type}
  end
  
  def get_armor_by_equip_group(group)
    $data_armors.compact.select {|a| a.equip_groups.include?(group.downcase)}
  end
  
  def get_runes
    $data_armors.compact.select {|a| a.is_rune?}
  end
  
  def get_item_fusions(item1, item2)
    k = item1.keywords | item2.keywords
    puts(k)
    a = filter_keyword_fusion(all_items, k) | [item1, item2]
    a = sort_by_keyword_match(a.shuffle, k)
    return a
  end
  
  def filter_keyword(array, keywords, number = 1)
    array.compact.select do |i|
      (i.keywords & keywords).count >= number
    end
  end
  
  def filter_keyword_fusion(array, keywords)
    array.compact.select do |i|
      (i.keywords & keywords).count >= i.note_field[:fusion_match] || 2
    end
  end
  
  def filter_range(array, sym, min = nil, max = nil)
    array.compact.select do |i|
      value = i.send(sym)
      (min ? value >= min : true ) && (max ? value <= max : true)
    end
  end
  
  def sort_by_keyword_match(array, keywords)
    array.compact.sort do |x,y| 
      (y.keywords & keywords).count <=> (x.keywords & keywords).count
    end
  end
    
end

 

 

The first script may be nicer becuase it works outside of events, but the second may be easier to understand. Also the first script does some stuff in place where the second uses a return value.

 

Either way, I can check it with this tiny script to make a shop from an array:

 

 

class Game_Interpreter
  
  def make_normal_shop(array, price_mult = nil)
    goods = []
    array.each do |item|
      next if item.nil?
      case 
      when item.is_a?(RPG::Item)
        type = 0
      when item.is_a?(RPG::Weapon)
        type = 1
      when item.is_a?(RPG::Armor)
        type = 2
      else
        next
      end
      next unless item.name =~ /[^-\s]/i
      if price_mult
        goods.push([type, item.id, 1, (item.price * price_mult).round])
      else
        goods.push([type, item.id, 0, 0])
      end
    end
    SceneManager.call(Scene_Shop)
    SceneManager.scene.prepare(goods, false)
    Fiber.yield
  end
  
end

 

Share this post


Link to post
Share on other sites
Guest
This topic is now closed to further replies.

  • Recently Browsing   0 members

    No registered users viewing this page.

×