じ☆ve冰风 发表于 2026-7-25 19:05:23

Mobius's Crafting Minigame

Crafting Minigame
by
MobiusXVI​
Release Notes
May 2014 - v. 1.0 Initial Release
Mar 2024 - v. 2.0 Script refactored; updated database; bug fixes
Apr 2024 - v. 2.1 Add additional customization options

Introduction
So a long time ago, I made this script to fulfill a request here. It then sat neglected for many years. After some prompting, I decided to clean it up, fix some bugs, and re-post it here for all to use.

Features
- Create as many recipes as you like
- Create unlimited recipe types
- Players play a minigame to craft items
- Easily call the crafting screen from the menu using my Menu Command Manager

Screenshots
Spoiler
https://forums.rpgmakerweb.com/attachments/1711742859919-png.300202/




Spoiler






How to Use
Instructions are in the script. Download the demo, and open the script editor for more information.

Demo
Download the demo from the attached files.

Script
I recommend you download the demo as this won't work right out of the box, but if you really just want the script and don't mind fiddling with it to get it to work then here you go.
Spoiler: Main Script                Ruby:       
#===============================================================================
# Mobius' Crafting Program
# Authors: Mobius XVI, Deke
# Version: 2.1
# Date: 14 APR 2024
#===============================================================================
#
# Introduction:
#
#   This script allows you to add crafting to your game which includes a
#   crafting minigame where the player tries to match a given recipe for
#   a particular item in order to create it successfully. Failures result
#   in wasted components and a junk item.
#
# Instructions:
#
#   Place this script below all the default scripts but above main.
#
#   Add the database section below the crafting script
#   Create your recipes in the database section; think of it like an
#   extension of the built-in database.
#
#   Each recipe needs the following:
#   - Three ingredients -- These can be any item, weapon, or armor
#   - Three ingredient quantites -- These can be any positive, whole numbers
#       less than 100 but the sum of all three should add up to 100 exactly
#   - A result -- this can be an item, weapon, or armor
#   - A recipe type -- this is an arbitrary tag of your choosing, such as
#       "Cooking", "Baking", "Smelting". This allows recipes to be grouped
#       by their type. This will make it so that you can limit baking recipes
#       to only work when using an oven for example.
#
#   To have the player learn recipes, simply make an event with this script call
#   $game_party.learn_recipe(1) -- Replace 1 with the recipe's ID in the database
#
#   To call the crafting screen, simply make an event with this script call
#   $scene = Scene_Craft.new("Cooking")         -- Will only have cooking recipes
#   $scene = Scene_Craft.new                  -- Will have all recipes
#   $scene = Scene_Craft.new(nil, "menu")       -- Will exit to the menu
#   $scene = Scene_Craft.new("Cooking", "menu") -- Only cooking; exit to menu
#
#   The crafting minigame works by asking the player to add the three ingredients
#   in the correct proportions (with an error tolerance of 10% by default).
#   If the player successfully combines the items in the right portions
#   then they will craft the intended item.
#   If not, they'll receive a junk item of your choosing.
#
#   The minigame itself uses the X, Y, and Z "buttons" to add the ingredients.
#   Check F1 to see what keyboard keys these are mapped to on your computer.
#   By default, they 're mapped to A, S, and D.
#
# Issues/Bugs/Possible Bugs:
#
#   - None
#
#Credits/Thanks:
#    - Deke, author of underlying crafting script
#    - Mobius XVI, author of minigame portion
#    - sutorumie, for the idea/request
#
#License
#
#    This script is available in its entirety for commercial and non-commercial
#    use. View the specific license terms below.
#
#    The portions of this script written by me are licensed under the MIT License:
#
#    The MIT License (MIT)
#
#      Copyright (c) 2024 darmes
#
#       Permission is hereby granted, free of charge, to any person obtaining a copy
#       of this software and associated documentation files (the "Software"), to deal
#       in the Software without restriction, including without limitation the rights
#       to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#       copies of the Software, and to permit persons to whom the Software is
#       furnished to do so, subject to the following conditions:
#
#       The above copyright notice and this permission notice shall be included in all
#       copies or substantial portions of the Software.
#
#       THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#       IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#       FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#       AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#       LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#       OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
#       SOFTWARE.
#
#      Further, if you do decide to use this script in a commercial product,
#      I'd ask that you let me know via a forum post or a PM. Thanks.
#
#==============================================================================
# ** CUSTOMIZATION START
#==============================================================================
module Mobius
module Crafting
    # Set the following value to false if you don' t want the game
    # to check whether your recipes are properly formatted
    # Note that data checking only works in playtest mode
    # and so will not function in any final release
    DATA_CHECK = true
    # Set the item ID for the waste item. This must be an item that exists.
    WASTE_ITEM_ID = 1
    # Do you have a custom menu system? When the crafting scene ends we can
    # return to the menu with a chosen menu index selected.
    MENU_INDEX = 0
    # Set the icons to use for the X, Y, and Z buttons.
    # The icons should be about 24x24, but the script will auto-adjust them
    # if they are slightly larger/smaller. Don't expect miracles though.
    # This is a filename without extension within the icon folder.
    X_BUTTON_ICON = "A"
    Y_BUTTON_ICON = "S"
    Z_BUTTON_ICON = "D"
    # Set the picture to use for the progress bar.
    # Picture should be exactly 204x20 pixels
    # This is a filename without extension within the pictures folder.
    BAR_PIC = "Bar"
    # This lets you set pictures to use for window dressing.
    # Pictures should be about 256x128 pixels
    # You can set a different picture depending on the recipe type.
    # If you'd rather just use a single picture then you can set a
    # default picture below and it will always use that.
    CRAFTING_PICTURES = {
      # Recipe Type => picture filename without extension
      "Smithing" => "Blacksmith",
      # Start with recipe type then make this symbol '=>' then put a
      # picture filename without extension within the pictures folder.
      # End each entry with a comma ',' to separate them from each other.
    }
    # You can set a default picture here.
    CRAFTING_PICTURES.default = "Laboratory"
    # This lets you set sound effects for success and failure
    # These are filenames without extension within the sound effects (SE) folder
    # You can set a different SE depending on the recipe type.
    # If you'd rather just use a single SE then you can set a default
    # SE below and it will always use that.
    CRAFT_SUCCESS_SE = {
      # Recipe Type => SE filename without extension
      "Smithing" => "055-Right01",
      # Start with recipe type then make this symbol '=>' then put a
      # SE filename without extension within the SE folder.
      # End each entry with a comma ',' to separate them from each other.
    }
    # You can set a default success SE here.
    CRAFT_SUCCESS_SE.default = "056-Right02"
    CRAFT_FAILURE_SE = {
      # Recipe Type => SE filename without extension
      "Smithing" => "057-Wrong01",
      # Start with recipe type then make this symbol '=>' then put a
      # SE filename without extension within the SE folder.
      # End each entry with a comma ',' to separate them from each other.
    }
    # You can set a default failure SE here.
    CRAFT_FAILURE_SE.default = "058-Wrong02"
    # Set the error tolerance for recipes, this is a percentage value
    # i.e. the player needs to mix the ingredients according to the
    # recipe within +/- 10%. Given the inherent inaccuracies, I
    # recommend being fairly generous with this.
    ERROR_TOLERANCE = 10
    # Set the color to use for the outlines drawn around the ingredients
    # and the finish/cancel buttons.
    # Black by default. Colors are RGB.
    OUTLINE_COLOR = Color.new(0, 0, 0) # Black
    # Set the colors to use for filling the mixing bar. The colors show
    # how much of each ingredient has been added, and can be set independently.
    X_BUTTON_FILL_COLOR = Color.new(255, 0, 0) # Red
    Y_BUTTON_FILL_COLOR = Color.new(0, 255, 0) # Green
    Z_BUTTON_FILL_COLOR = Color.new(0, 0, 255) # Blue
    # The following words are used for various displays
    # Think of these as additional database words
    HP_RECOVERY_AMOUNT = "HP Heal"
    HP_DAMAGE_AMOUNT = "Damage"
    HP_RECOVERY_PERCENT = "HP Heal %"
    HP_DAMAGE_PERCENT = "Damage %"
    SP_RECOVERY_AMOUNT = "SP Restore"
    SP_DAMAGE_AMOUNT = "SP Drain"
    SP_RECOVERY_PERCENT = "SP Restore %"
    SP_DAMAGE_PERCENT = "SP Drain %"
    HIT_RATE = "Accuracy"
    EVASION = "EVA"
    QUANTITY_OWNED = "Quantity currently owned:"
    HAVE = "Have"
    REQUIRED = "Req %"
    FINISH = "Finish"
    CANCEL = "Cancel"
end
end
#==============================================================================
# ** CUSTOMIZATION END
#------------------------------------------------------------------------------
# ** EDIT BELOW THIS LINE AT OWN RISK!!!
#==============================================================================
#==============================================================================
# ** Game_Party
#==============================================================================
class Game_Party
attr_accessor      :known_recipe_ids
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
alias crafting_party_initialize initialize
def initialize
    crafting_party_initialize
    @known_recipe_ids=[]
end
#--------------------------------------------------------------------------
# * Know
#--------------------------------------------------------------------------
def know_recipe?(recipe_id)
    return $game_party.known_recipe_ids.include?(recipe_id)
end
#--------------------------------------------------------------------------
# * Learn Recipe
#--------------------------------------------------------------------------
def learn_recipe(recipe_id)
    # Don't learn recipes that aren't in the database
    return if recipe_id <= 0
    return if recipe_id >= Mobius::Recipes::RECIPES.size
    # Don't learn recipes that we already know
    return if know_recipe?(recipe_id)
    @known_recipe_ids.push(recipe_id)
end
#--------------------------------------------------------------------------
# * Forget Recipe
#--------------------------------------------------------------------------
def forget_recipe(recipe_id)
    @known_recipe_ids.delete(recipe_id)
end
end
#==============================================================================
# ** Game_Recipe
#==============================================================================
class Game_Recipe
include Mobius::Crafting
attr_reader    :ingredients
attr_reader    :quantities
attr_reader    :result
attr_reader    :result_type
attr_reader    :ingredient_types
attr_reader    :recipe_type
attr_reader    :result
attr_reader    :result_type
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize(ingredients, ingredient_types, quantities, result,
    result_type, recipe_type = nil)
    @ingredients = ingredients
    @ingredient_types = ingredient_types
    @quantities = quantities
    @result = result
    @result_type = result_type
    @recipe_type = recipe_type
    data_check if $DEBUG and DATA_CHECK
end
#--------------------------------------------------------------------------
# * Data_Check - Checks recipes for errors and reports them to user
#--------------------------------------------------------------------------
def data_check
    unless @ingredients.size == 3
      text = "WARNING:\n The recipe for \'#{name}\' does not have 3 ingredients."
      text << "\nScript may not function as intended."
      print(text)
    end
    unless @ingredient_types.size == 3
      text = "WARNING:\n The recipe for \'#{name}\' does not have 3 ingredient"
      text << " types.\nScript may not function as intended."
      print(text)
    end
    unless @quantities.size == 3
      text = "WARNING:\n The recipe for \'#{name}\' does not have 3 entries for"
      text << " quantities.\nScript may not function as intended."
      print(text)
    end
    unless @quantities.inject {|sum, n| sum + n } == 100
      text = "WARNING:\n The recipe for \'#{name}\' has quantites which do not"
      text << " equal 100%.\nScript may not function as intended."
      print(text)
    end
    unless @result.is_a?(Integer)
      text = "WARNING:\n The recipe for \'#{name}\' has a result which is not"
      text << " a integer.\nScript may not function as intended."
      print(text)
    end
    unless .include?(@result_type)
      text = "WARNING:\n The recipe for \'#{name}\' has a result_type which is"
      text << " not 0, 1, or 2.\nScript may not function as intended."
      print(text)
    end
    unless @recipe_type.is_a?(String)
      text = "WARNING:\n The recipe for \'#{name}\' has a recipe_type which is"
      text << " not a String.\nScript may not function as intended."
      print(text)
    end
end
#--------------------------------------------------------------------------
# * Name - Returns the name of the item the recipe makes
#--------------------------------------------------------------------------
def name
    case @result_type
      when 0
      name = $data_items[@result].name
      when 1
      name = $data_armors[@result].name
      when 2
      name = $data_weapons[@result].name
    end
    return name
end
#--------------------------------------------------------------------------
# * Have - Checks that the party has all the ingredients for this recipe
#--------------------------------------------------------------------------
def have
    have_all = true
    for i in 0...@ingredients.size
      case @ingredient_types
      when 0
          if $game_party.item_number(@ingredients) < 1
            have_all = false
          end
      when 1
          if $game_party.armor_number(@ingredients) < 1
            have_all = false
          end
      when 2
          if $game_party.weapon_number(@ingredients) < 1
            have_all = false
          end
      end
    end
    return have_all
end
#--------------------------------------------------------------------------
# * Decrement - Removes the recipe's ingredients from the party's inventory
#--------------------------------------------------------------------------
def decrement
    for i in 0...@ingredients.size
      case @ingredient_types
      when 0
      $game_party.lose_item(@ingredients, 1)
      when 1
      $game_party.lose_armor(@ingredients, 1)
      when 2
      $game_party.lose_weapon(@ingredients, 1)
      end
    end
end
#--------------------------------------------------------------------------
# * Make - Adds the recipe's result to the party's inventory
#--------------------------------------------------------------------------
def make
    if have
      case @result_type
      when 0
      $game_party.gain_item(@result, 1)
      when 1
      $game_party.gain_armor(@result, 1)
      when 2
      $game_party.gain_weapon(@result, 1)
      end
      decrement
    end
end
#--------------------------------------------------------------------------
# * Make Waste - Adds the waste item to the party's inventory
#--------------------------------------------------------------------------
def make_waste
    if have
      $game_party.gain_item(WASTE_ITEM_ID, 1)
      decrement
    end
end
#--------------------------------------------------------------------------
# * == - Compares two recipes for equality
#--------------------------------------------------------------------------
def == (recipe)
    if recipe.is_a?(Game_Recipe)
      equal = true
      if recipe.ingredients != self.ingredients
      equal = false
      end
      if recipe.ingredient_types != self.ingredient_types
      equal = false
      end
      if recipe.quantities != self.quantities
      equal = false
      end
      if recipe.result != self.result
         equal=false
      end
      if recipe.result_type != self.result_type
      equal = false
      end
      if recipe.recipe_type != self.recipe_type
      equal = false
      end
    else
      equal = false
    end
    return equal
end
end
#==============================================================================
# ** Window_Base
#==============================================================================
class Window_Base
#--------------------------------------------------------------------------
# * Draw Icon - draws icons on "x, y"
#   icon_name : filename of the icon ("String")
#   x         : draw spot x-coordinate
#   y         : draw spot y-coordinate
#--------------------------------------------------------------------------
def draw_icon(icon_name, x, y)
    bitmap = RPG::Cache.icon(icon_name)
    src_rect = Rect.new(0, 0, bitmap.width, bitmap.height)
    self.contents.blt(x, y, bitmap, src_rect)
end
#--------------------------------------------------------------------------
# * Draw Icon Centered- draws icons centered on "x, y"
#   icon_name : filename of the icon ("String")
#   x         : draw spot x-coordinate
#   y         : draw spot y-coordinate
#--------------------------------------------------------------------------
def draw_icon_centered(icon_name, x, y)
    bitmap = RPG::Cache.icon(icon_name)
    src_rect = Rect.new(0, 0, bitmap.width, bitmap.height)
    draw_x = x - (bitmap.width / 2)
    draw_y = y - (bitmap.height / 2)
    self.contents.blt(draw_x, draw_y, bitmap, src_rect)
end
#--------------------------------------------------------------------------
# * Draw Pic - draws pic on "x, y"
#   pic_name: filename of the pic ("String")
#   x         : draw spot x-coordinate
#   y         : draw spot y-coordinate
#--------------------------------------------------------------------------
def draw_pic(pic_name, x, y)
    bitmap = RPG::Cache.picture(pic_name)
    src_rect = Rect.new(0, 0, bitmap.width, bitmap.height)
    self.contents.blt(x, y, bitmap, src_rect)
end
end
#==============================================================================
# ** Window_Craft - Displays the list of recipes
#==============================================================================
class Window_Craft < Window_Selectable
attr_reader    :data
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize(recipe_type = nil)
    super(0, 64, 240, 416)
    @recipe_type = recipe_type
    @column_max = 1
    @data = []
    self.index = 0
    get_recipes
    refresh
end
#--------------------------------------------------------------------------
# * Recipe - Returns the recipe currently selected
#--------------------------------------------------------------------------
def recipe
    return @data
end
#--------------------------------------------------------------------------
# * Get Recipes - Gets the party's known recipes
#--------------------------------------------------------------------------
def get_recipes
    for recipe_id in $game_party.known_recipe_ids.sort
      recipe = Mobius::Recipes::RECIPES
      next if !recipe # If we didn't get a recipe, skip
      if @recipe_type # If not nil
      @data.push(recipe) if recipe.recipe_type == @recipe_type
      else
      @data.push(recipe)
      end
    end
end
#--------------------------------------------------------------------------
# * Refresh
#--------------------------------------------------------------------------
def refresh
    # Dispose old contents
    if self.contents != nil
      self.contents.dispose
      self.contents = nil
    end
    # Draw all recipes
    @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
#--------------------------------------------------------------------------
# * Draw Item - Draws a single recipe to the screen
#--------------------------------------------------------------------------
def draw_item(index)
    recipe = @data
    self.contents.font.color = recipe.have ? normal_color : disabled_color
    x = 16
    y = index * 32
    w = self.width - 32
    h = 32
    self.contents.draw_text(x, y, w, h, recipe.name, 0)
end
#--------------------------------------------------------------------------
# * Update Help - Updates the help window with the recipe's description
#--------------------------------------------------------------------------
def update_help
    if self.recipe.is_a?(Game_Recipe)
      case self.recipe.result_type
      when 0
          description = $data_items.description
      when 1
          description = $data_armors.description
      when 2
          description = $data_weapons.description
      end
    else
      description = ""
    end
    @help_window.set_text(description)
end
end
#==============================================================================
# ** Window_CraftResult - Displays the result of the chosen recipe
#==============================================================================
class Window_CraftResult < Window_Base
include Mobius::Crafting
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize
    super(240, 64, 400, 192)
    self.contents = Bitmap.new(width - 32, height - 32)
    @result = nil
    @type = nil
    @item = nil
    @item_count = nil
end
#--------------------------------------------------------------------------
# * Set Result - Sets the recipe result to display
#--------------------------------------------------------------------------
def set_result(recipe)
    @result = recipe.result
    @type = recipe.result_type
    case @type
      when 0
      @item = $data_items[@result]
      @item_count = $game_party.item_number(@result)
      when 1
      @item = $data_armors[@result]
      @item_count = $game_party.armor_number(@result)
      when 2
      @item = $data_weapons[@result]
      @item_count = $game_party.weapon_number(@result)
    end
    refresh
end
#--------------------------------------------------------------------------
# * Refresh
#--------------------------------------------------------------------------
def refresh
    self.contents.clear
    # Draw recipe icon and quantity owned
    self.draw_icon(@item.icon_name, 0, 0)
    self.contents.font.color = normal_color
    self.contents.draw_text(40, 0, 300, 32, QUANTITY_OWNED)
    self.contents.font.color = system_color
    self.contents.draw_text(294, 0, 45, 32,@item_count.to_s)
    # Analyze Item
    case @type
      when 0
      stats = analyze_item
      when 1
      stats = analyze_armor
      when 2
      stats = analyze_weapon
    end
    # Draw item's stats
    for i in 0...stats.size
      x = i % 2 * 184
      y = i / 2 * 32 + 32
      stat_label = stats
      stat_amount = stats.to_s
      self.contents.font.color = normal_color
      self.contents.draw_text(x, y, 100, 32, stat_label)
      self.contents.font.color = system_color
      self.contents.draw_text(x + 110, y, 45, 32, stat_amount)
    end
end
#--------------------------------------------------------------------------
# * Analyze Item - Gets the item's relevant stats
#--------------------------------------------------------------------------
def analyze_item
    stats = []
    # For each stat, include it for display only if it's non-zero
    if @item.recover_hp > 0
      stats.push()
    end
    if @item.recover_hp < 0
      stats.push()
    end
    if @item.recover_hp_rate > 0
      stats.push()
    end
    if @item.recover_hp_rate < 0
      stats.push()
    end
    if @item.recover_sp > 0
      stats.push()
    end
    if @item.recover_sp < 0
      stats.push()
    end
    if @item.recover_sp_rate > 0
      stats.push()
    end
    if @item.recover_sp_rate < 0
      stats.push()
    end
    if @item.parameter_type > 0 and @item.parameter_points != 0
      case @item.parameter_type
      when 1# Max HP
      stats.push(["Max " + $data_system.words.hp, @item.parameter_points])
      when 2# Max SP
      stats.push(["Max " + $data_system.words.sp, @item.parameter_points])
      when 3# Strength
      stats.push([$data_system.words.str, @item.parameter_points])
      when 4# Dexterity
      stats.push([$data_system.words.dex, @item.parameter_points])
      when 5# Agility
      stats.push([$data_system.words.agi, @item.parameter_points])
      when 6# Intelligence
      stats.push([$data_system.words.int, @item.parameter_points])
      end
    end
    if @item.hit < 0
      stats.push()
    end
    return stats
end
#--------------------------------------------------------------------------
# * Analyze Armor - Gets the armor's relevant stats
#--------------------------------------------------------------------------
def analyze_armor
    stats = []
    # For each stat, include it for display only if it's non-zero
    if @item.pdef != 0
      stats.push([$data_system.words.pdef, @item.pdef])
    end
    if @item.mdef != 0
      stats.push([$data_system.words.mdef, @item.mdef])
    end
    if @item.eva != 0
      stats.push()
    end
    if @item.str_plus != 0
      stats.push([$data_system.words.str, @item.str_plus])
    end
    if @item.dex_plus != 0
      stats.push([$data_system.words.dex, @item.dex_plus])
    end
    if @item.agi_plus != 0
      stats.push([$data_system.words.agi, @item.agi_plus])
    end
    if @item.int_plus != 0
      stats.push([$data_system.words.int, @item.int_plus])
    end
    return stats
end
#--------------------------------------------------------------------------
# * Analyze Weapon - Gets the weapon's relevant stats
#--------------------------------------------------------------------------
def analyze_weapon
    stats = []
    # For each stat, include it for display only if it's non-zero
    if @item.atk != 0
      stats.push([$data_system.words.atk, @item.atk])
    end
    if @item.pdef != 0
      stats.push([$data_system.words.pdef, @item.pdef])
    end
    if @item.mdef != 0
      stats.push([$data_system.words.mdef, @item.mdef])
    end
    if @item.str_plus != 0
      stats.push([$data_system.words.str, @item.str_plus])
    end
    if @item.dex_plus != 0
      stats.push([$data_system.words.dex, @item.dex_plus])
    end
    if @item.agi_plus != 0
      stats.push([$data_system.words.agi, @item.agi_plus])
    end
    if @item.int_plus != 0
      stats.push([$data_system.words.int, @item.int_plus])
    end
    return stats
end
end
#==============================================================================
# ** Window_CraftIngredients - Displays the ingredients of the chosen recipe
#==============================================================================
class Window_CraftIngredients < Window_Base
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize
    super(240, 256, 400, 224)
    self.contents = Bitmap.new(width - 32, height - 32)
    @ingredients = []
    @counts = []
    @percentages = []
end
#--------------------------------------------------------------------------
# * Set Ingredients - Sets the recipe ingredients to display
#--------------------------------------------------------------------------
def set_ingredients(recipe)
    @ingredients = []
    @counts = []
    @percentages = recipe.quantities
    for i in 0...recipe.ingredients.size
      ingredient = recipe.ingredients
      case recipe.ingredient_types
      when 0
      item = $data_items
      count = $game_party.item_number(ingredient)
      when 1
      item = $data_armors
      count = $game_party.armor_number(ingredient)
      when 2
      item = $data_weapons
      count = $game_party.weapon_number(ingredient)
      end
      @ingredients.push(item)
      @counts.push(count)
    end
    refresh
end
#--------------------------------------------------------------------------
# * Refresh
#--------------------------------------------------------------------------
def refresh
    self.contents.clear
    for i in 0...@ingredients.size
      ingredient = @ingredients
      count = @counts
      y = i * 32
      self.draw_icon(ingredient.icon_name, 0, y)
      self.contents.font.color = count >= 1 ? normal_color : disabled_color
      self.contents.draw_text(30, y, 280, 28, ingredient.name)
      self.contents.font.color = normal_color
      self.contents.draw_text(300, y, 45, 28, @percentages.to_s + "%")
      self.contents.font.color = system_color
      self.contents.draw_text(245, y, 45, 28, count.to_s )
    end
end
end
#==============================================================================
# ** Window_CraftIngredientsLabel - Displays the labels above the ingredients
#==============================================================================
class Window_CraftIngredientsLabel < Window_Base
include Mobius::Crafting
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize
    super(440, 224, 196, 48)
    # Weird hack so that this window can have smaller borders than normal
    @bitmap = Bitmap.new(width - 16, height - 16)
    @sprite = RPG::Sprite.new(self.viewport)
    @sprite.bitmap = @bitmap
    @sprite.visible = true
    @sprite.x = self.x + 16
    @sprite.y = self.y + 8
    @sprite.z = self.z + 1
    refresh
end
#--------------------------------------------------------------------------
# * Refresh
#--------------------------------------------------------------------------
def refresh
    @bitmap.clear
    @bitmap.draw_text(4, 0, 82, 32, HAVE)
    @bitmap.draw_text(86, 0, 82, 32, REQUIRED)
end
#--------------------------------------------------------------------------
# * Dispose
#--------------------------------------------------------------------------
def dispose
    @sprite.bitmap = nil
    @sprite.dispose
    @bitmap.dispose
    super
end
end
#==============================================================================
# ** Window_CraftCombine - Displays the crafting minigame
#==============================================================================
class Window_CraftCombine < Window_Selectable
include Mobius::Crafting
TEMP_COLOR = Color.new(0, 0, 0) # Black
CLEAR_COLOR = Color.new(0, 0, 0, 0) # Clear
#---------------------------------------------------------------------------
# Object Initialization
#---------------------------------------------------------------------------
def initialize(recipe)
    super(176, 78, 256 + 32, 288 + 32)
    self.contents = Bitmap.new(width - 32, height - 32)
    @recipe = recipe
    @combined_percentages =
    self.z = 1000
    @item_max = 2
    @column_max = 2
    @index = 0
    refresh
end
#--------------------------------------------------------------------------
# * Update
#--------------------------------------------------------------------------
def update
    super
    # Check if percentage bar is maxed out
    unless @combined_percentages.inject {|sum, n| sum += n } >= 100
      # If it isn't, allow additional adding
      if Input.press?(Input::X)
      @combined_percentages += 1
      elsif Input.press?(Input::Y)
      @combined_percentages += 1
      elsif Input.press?(Input::Z)
      @combined_percentages += 1
      end
      draw_bar
    # If it is full, don't allow adding
    else
      if Input.trigger?(Input::X) or
      Input.trigger?(Input::Y) or
      Input.trigger?(Input::Z)
      $game_system.se_play($data_system.buzzer_se)
      end
    end
end
#---------------------------------------------------------------------------
# Refresh
#---------------------------------------------------------------------------
def refresh
    # Dispose old contents
    self.contents.clear
    # Draws the window dressing
    draw_picture
    # Draw the ingredient icons
    draw_ingredient(0)
    draw_ingredient(1)
    draw_ingredient(2)
    # Draw the ingredient button icons
    draw_ingredient_button(0)
    draw_ingredient_button(1)
    draw_ingredient_button(2)
    # Draw the progress bar
    draw_bar
    # Draw the finish / cancel buttons
    draw_action_buttons
end
#--------------------------------------------------------------------------
# * Draw picture
#--------------------------------------------------------------------------
def draw_picture
    picture_name = CRAFTING_PICTURES[@recipe.recipe_type]
    if picture_name == ""
      self.contents.fill_rect(0, 0, 256, 128, TEMP_COLOR)
    else
      draw_pic(picture_name, 0, 0)
    end
end
#--------------------------------------------------------------------------
# * Draw Ingredient
#--------------------------------------------------------------------------
def draw_ingredient(index)
    x = 22 + 80 * index
    y, w, h = 132, 52, 52
    self.contents.fill_rect(x, y, w, h, OUTLINE_COLOR)
    self.contents.fill_rect(x + 2, y + 2, w - 4, h - 4, CLEAR_COLOR)
    ingredient = get_ingredient(index)
    draw_icon_centered(ingredient.icon_name, x + w/2, y + h/2)
end
#--------------------------------------------------------------------------
# * Draw Ingredient Button
#--------------------------------------------------------------------------
def draw_ingredient_button(index)
    button =
    x = 22 + 80 * index
    y, w, h = 184, 52, 32
    if button == ""
      self.contents.fill_rect(x, y, 18, 18, TEMP_COLOR)
    else
      draw_icon_centered(button, x + w/2, y + h/2)
    end
end
#--------------------------------------------------------------------------
# * Draw Bar
#--------------------------------------------------------------------------
def draw_bar
    x, y, h = 28, 218, 16
    # Draw inner bar background fill first
    self.contents.fill_rect(x, y, 200, 16, Color.new(255,255,255))
    # Draw inner bar ingredient fills
    x_percent = (@combined_percentages * 2)
    y_percent = (@combined_percentages * 2)
    z_percent = (@combined_percentages * 2)
    self.contents.fill_rect(x, y, x_percent, h, X_BUTTON_FILL_COLOR)
    self.contents.fill_rect(x + x_percent, y, y_percent, h, Y_BUTTON_FILL_COLOR)
    self.contents.fill_rect(x + x_percent + y_percent, y, z_percent, h, Z_BUTTON_FILL_COLOR)
    # Draw the outer bar outline with tick marks over the top
    draw_pic(BAR_PIC, x - 2, y - 2)
end
#--------------------------------------------------------------------------
# * Draw Action Button
#--------------------------------------------------------------------------
def draw_action_buttons
    x1, x2 = 36, 138
    y, w, h = 254, 82, 22
    # Finish Button
    self.contents.fill_rect(x1 - 2, y - 2, w + 4, h + 4, OUTLINE_COLOR)
    self.contents.fill_rect(x1, y, w, h, CLEAR_COLOR)
    self.contents.draw_text(x1, y, w, h, FINISH, 1)
    # Cancel Button
    self.contents.fill_rect(x2 - 2, y - 2, w + 4, h + 4, OUTLINE_COLOR)
    self.contents.fill_rect(x2, y, w, h, CLEAR_COLOR)
    self.contents.draw_text(x2, y, w, h, CANCEL, 1)
end
#--------------------------------------------------------------------------
# * Update Cursor Rectangle
#--------------------------------------------------------------------------
def update_cursor_rect
    x1, x2 = 36, 138
    y, w, h = 254, 82, 22
    # If cursor position is less than 0
    if @index < 0
      self.cursor_rect.empty
      return
    end
    if @index == 0
      self.cursor_rect.set(x1, y, w, h)
    elsif @index == 1
      self.cursor_rect.set(x2, y, w, h)
    end
end
#--------------------------------------------------------------------------
# * Judge Craft - Determines if crafting was successful
#--------------------------------------------------------------------------
def judge_craft
    craft = true
    correct_percentages = @recipe.quantities
    for i in 0...correct_percentages.size
      min = @combined_percentages - ERROR_TOLERANCE
      max = @combined_percentages + ERROR_TOLERANCE
      range = min..max
      unless range.include?(correct_percentages)
      craft = false
      break
      end
    end
    # If successful
    if craft
      # Play succes SE
      success_name = CRAFT_SUCCESS_SE[@recipe.recipe_type]
      Audio.se_play("Audio/SE/" + success_name)
      # Make result
      @recipe.make
    else
      # Play failure SE
      failure_name = CRAFT_FAILURE_SE[@recipe.recipe_type]
      Audio.se_play("Audio/SE/" + failure_name)
      # Make waste
      @recipe.make_waste
    end
end
#--------------------------------------------------------------------------
# * Get ingredient - returns item/weapon/armor
#   id : 0, 1, or 2
#--------------------------------------------------------------------------
def get_ingredient(id)
    item_id = @recipe.ingredients
    item_type = @recipe.ingredient_types
    case item_type
    when 0 # item
      item = $data_items
    when 1 # weapon
      item = $data_weapons
    when 2 # armor
      item = $data_armors
    end
    return item
end
end
#==============================================================================
# ** Scene_Craft
#==============================================================================
class Scene_Craft
#--------------------------------------------------------------------------
# * Object Initialization
#--------------------------------------------------------------------------
def initialize(recipe_type = nil, return_scene = nil)
    @craft_index = -1
    @return_scene = return_scene
    @recipe_type = recipe_type
end
#--------------------------------------------------------------------------
# * Create Windows
#--------------------------------------------------------------------------
def create_windows
    @craft_window = Window_Craft.new(@recipe_type)
    @help_window = Window_Help.new
    @craft_window.help_window = @help_window
    @result_window = Window_CraftResult.new
    @ingredients_window = Window_CraftIngredients.new
    @label_window = Window_CraftIngredientsLabel.new
    # If there's at least one known recipe
    if @craft_window.data.size > 0
      @craft_index = 0
      @craft_window.index = 0
      @result_window.set_result(@craft_window.recipe)
      @ingredients_window.set_ingredients(@craft_window.recipe)
    end
end
#--------------------------------------------------------------------------
# * Dispose Windows
#--------------------------------------------------------------------------
def dispose_windows
    @craft_window.dispose
    @help_window.dispose
    @result_window.dispose
    @ingredients_window.dispose
    @label_window.dispose
end
#--------------------------------------------------------------------------
# * Main
#--------------------------------------------------------------------------
def main
    create_windows
    Graphics.transition
    loop do
      Graphics.update
      Input.update
      update
      if $scene != self
      break
      end
    end
    Graphics.freeze
    dispose_windows
end
#--------------------------------------------------------------------------
# * Update
#--------------------------------------------------------------------------
def update
    @craft_window.update
    # If there's a selectable recipe and we've changed selection
    if @craft_index >= 0 and @craft_index != @craft_window.index
      @craft_index = @craft_window.index
      set_recipe_in_windows
    end
    # If we're selecting a recipe
    if @craft_window.active
      update_craft
      return
    end
    # If we're in the crafting mini-game
    if @craft_combine_window
      update_craft_combine
      return
    end
end
#--------------------------------------------------------------------------
# * Set Recipe in Windows
#--------------------------------------------------------------------------
def set_recipe_in_windows
    @result_window.set_result(@craft_window.recipe)
    @ingredients_window.set_ingredients(@craft_window.recipe)
end
#--------------------------------------------------------------------------
# * Update Craft - Updates the selection of a recipe
#--------------------------------------------------------------------------
def update_craft
    if Input.trigger?(Input::B)
      $game_system.se_play($data_system.cancel_se)
      if @return_scene == "menu"
      $scene = Scene_Menu.new(Mobius::Crafting::MENU_INDEX)
      else
      $scene = Scene_Map.new
      end
      return
    end
    if Input.trigger?(Input::C) and @craft_window.data.size > 0
      @recipe = @craft_window.recipe
      if @recipe.have
      $game_system.se_play($data_system.decision_se)
      @craft_window.active = false
      @craft_combine_window = Window_CraftCombine.new(@recipe)
      else
      $game_system.se_play($data_system.buzzer_se)
      return
      end
    end
end
#---------------------------------------------------------------------------
# * Update Craft Combine - Updates the crafting minigame
#---------------------------------------------------------------------------
def update_craft_combine
    @craft_combine_window.update
    # If cancel button is pressed
    if Input.trigger?(Input::B)
      # Play cancel SE
      $game_system.se_play($data_system.cancel_se)
      @craft_window.active = true
      @craft_combine_window.dispose
    end
    # If confirm button is pressed
    if Input.trigger?(Input::C)
      # If finish is chosen
      if @craft_combine_window.index == 0
      @craft_combine_window.judge_craft
      @craft_window.active = true
      @craft_combine_window.dispose
      # Refresh item counts
      @craft_window.refresh
      set_recipe_in_windows
      # If cancel is chosen
      elsif @craft_combine_window.index == 1
      $game_system.se_play($data_system.cancel_se)
      @craft_window.active = true
      @craft_combine_window.dispose
      end
    end
end
end

Spoiler: Database Section                Ruby:       
# This is the database section for storing your recipes
# Follow the instructions below for how to create recipes
module Mobius
module Recipes
    #===== Don't touch this block =====
    RECIPES =
    def Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
      RECIPES.push(
      Game_Recipe.new(
          ingredients,
          ingredient_types,
          quantities,
          result,
          result_type,
          recipe_type
      )
      )
    end
    #===== You can edit below this line =====

    # This is how you create a new recipe
    # Specify the ingredient IDs (Item/Armor/Weapon) from the database
    ingredients =
    # Specify what types the ingredients are (0 = Item, 1 = Armor, 2 = Weapon)
    ingredient_types =
    # Specify how much of each ingredient
    # 20% of ingredient #1, 30% of ingredient #2, 50% of ingredient #3
    quantities =
    # Specify what the recipe makes (Item/Armor/Weapon) from the database
    result = 1
    # Specify what type the result is (0 = Item, 1 = Armor, 2 = Weapon)
    result_type = 0
    # Specify the recipe type.
    # The tag must be in quotes, and capitalization does matter.
    recipe_type = "Brewing"
    # Add the recipe to the database by doing this
    # (the first recipe added will be recipe with ID = 1)
    # This will be a potion in the default project
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be a full potion in the default project
    # Because this is the second recipe added it will have ID = 2
    ingredients =
    ingredient_types =
    quantities =
    result = 3
    result_type = 0
    recipe_type = "Brewing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be a perfume in the default project
    ingredients =
    ingredient_types =
    quantities =
    result = 4
    result_type = 0
    recipe_type = "Brewing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be a full perfume in the default project
    ingredients =
    ingredient_types =
    quantities =
    result = 6
    result_type = 0
    recipe_type = "Brewing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be an elixir in the default project
    ingredients =
    ingredient_types =
    quantities =
    result = 7
    result_type = 0
    recipe_type = "Brewing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be a full elixir in the default project
    ingredients =
    ingredient_types =
    quantities =
    result = 8
    result_type = 0
    recipe_type = "Brewing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be a seed of life in the default project
    ingredients =
    ingredient_types =
    quantities =
    result = 17
    result_type = 0
    recipe_type = "Brewing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be a seed of strength in the default project
    ingredients =
    ingredient_types =
    quantities =
    result = 19
    result_type = 0
    recipe_type = "Brewing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be a bronze sword in the default project
    ingredients =
    ingredient_types =
    quantities =
    result = 1
    result_type = 2
    recipe_type = "Smithing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be a mythril sword in the default project
    ingredients =
    ingredient_types =
    quantities =
    result = 4
    result_type = 2
    recipe_type = "Smithing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be a bronze shield in the default project
    ingredients =
    ingredient_types =
    quantities =
    result = 1
    result_type = 1
    recipe_type = "Smithing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
    # This will be a mythril shield in the default project
    ingredients =
    ingredient_types =
    quantities =
    result = 4
    result_type = 1
    recipe_type = "Smithing"
    Recipes.add_receipe_to_database(
      ingredients,
      ingredient_types,
      quantities,
      result,
      result_type,
      recipe_type
    )
end
end



FAQ
Q. Can I disable the minigame?
A. Not with this version. I'd like to make an easy way to do that. You can always grab Deke's original version though if that's what you want.

Q. I used the old version you made; will this work with that?
A. No, I made enough changes "under the hood" that if you had been using the 1.0 version you can't simply drop this in to replace it.

Credit and Thanks
- Deke, author of underlying crafting script
- Mobius XVI, author of minigame portion
- sutorumie, for the idea/request

Author's Notes
This script is available in its entirety for commercial and non-commercial use. View the full license terms in the script header.


本贴来自国际rpgmaker官方论坛作者:MobiusXVI处,因国际论坛即将永久关站,为了存档多年珍贵资料,署名转载到本论坛存档,由于官方帖子为英文原帖,需要中文翻译请点击论坛顶部切换语言为中文就可以将帖子翻译成中文浏览,方便大家随时查看,原文地址:https://forums.rpgmakerweb.com/threads/mobiuss-crafting-minigame.167296/
页: [1]
查看完整版本: Mobius&#039;s Crafting Minigame