Save-Point
What's up, RMers? - Printable Version

+- Save-Point (https://www.save-point.org)
+-- Forum: Games Development (https://www.save-point.org/forum-4.html)
+--- Forum: Development Discussion (https://www.save-point.org/forum-17.html)
+--- Thread: What's up, RMers? (/thread-395.html)



RE: What's up, RMers? - DerVVulfman - 08-06-2024

Keeping sharp, I decided... why not make a reversal of my Missing Resources script. Instead of checking your project to see if any files are missing... check the resources to see if it is even being USED!

Code:
#==============================================================================
# ** Resource Sifter
#------------------------------------------------------------------------------
#    by DerVVulfman
#    version 0.1 (beta - incomplete early stage)
#    08-06-2024 (MM-DD-YYYY)
#    RGSS / RPGMaker XP
#------------------------------------------------------------------------------
#    Place below Scene_Debug and above Main,  and it should search your game's
#    project folder for "Audio" not used in your game. That is to say, it will
#    find if you have any extra/unnecessary audio files.
#
#    Output of resources unused will appear in your game's root folder.
#
#    This is a beta as I am continuing development.
#==============================================================================


module Resource_Sifter
  
  #--------------------------------------------------------------------------
  # * Invariables
  #--------------------------------------------------------------------------  
  GFX = ['@system_l.gameover_name',          '@system_l.title_name',
         '@system_l.battle_transition',      '@system_l.windowskin_name',
        
         '@system_l.title_bgm.name',         '@system_l.battle_bgm.name',
         '@system_l.battle_end_me.name',     '@system_l.gameover_me.name']
        
  SFX = ['@system_l.cursor_se.name',         '@system_l.decision_se.name',
         '@system_l.cancel_se.name',         '@system_l.buzzer_se.name',
         '@system_l.equip_se.name',          '@system_l.shop_se.name',
         '@system_l.save_se.name',           '@system_l.load_se.name',
         '@system_l.battle_start_se.name',   '@system_l.escape_se.name',    
         '@system_l.actor_collapse_se.name', '@system_l.enemy_collapse_se.name']
        
  #--------------------------------------------------------------------------
  # * Main Processing
  #--------------------------------------------------------------------------
  def self.main
    #
    setup                                     # Setup data
    update                                    # Update/Process tests
    output                                    # Output result(s)
    #
  end
  #--------------------------------------------------------------------------
  # * Setup
  #--------------------------------------------------------------------------
  def self.setup
    #
    # Load database data
    @system_l = load_data("Data/System.rxdata")
    @common_l = load_data("Data/CommonEvents.rxdata")
    @map_l    = load_data("Data/MapInfos.rxdata")
    #
    @root_bgm = []
    @root_bgs = []
    @root_me  = []
    @root_se  = []
    #    
    setup_files(@root_bgm,  "Audio/BGM/*")    # Setup test for BGM files
    setup_files(@root_bgs,  "Audio/BGS/*")    # Setup test for BGS files
    setup_files(@root_me,   "Audio/ME/*")     # Setup test for ME files
    setup_files(@root_se,   "Audio/SE/*")     # Setup test for SE files
    #
  end
  #--------------------------------------------------------------------------
  # * Update and perform searches
  #--------------------------------------------------------------------------
  def self.update
    #
    search_system                             # Search system database
    search_common                             # Search common events
    search_map                                # Search map content
    #
  end
  #--------------------------------------------------------------------------
  # * Output Content
  #--------------------------------------------------------------------------
  def self.output
    #
    output_to_file(@root_bgm,   "BGMs.txt")   # Print BGM test results
    output_to_file(@root_bgs,   "BGSs.txt")   # Print BGS test results
    output_to_file(@root_me,    "MEs.txt")    # Print ME test results
    output_to_file(@root_se,    "SEs.txt")    # Print SE test results
    #
  end
  #--------------------------------------------------------------------------
  # * Setup (when acquiring files from a game resource folder)
  #     pathway_list : pathway specific resource array
  #     path_test    : the folder path for a resource
  #--------------------------------------------------------------------------
  def self.setup_files(pathway_list, path_test)
    #
    path_len  = (path_test.length) -1         # Get pathway length (minus *)
    #
    for name in Dir[path_test]                # Cycle through root folder
      next if File.stat(name).directory?      # If NOT a subfolder
      name_len = name.length                  # Get filename's length
      name_len -= path_len                    # Shorten length by path length
      name_len -= 4                           # Shorten length by ext length
      name = name.slice(path_len, name_len)   # Trim the audio name
      pathway_list.push(name)                 # Push into the root path list
    end    
    #
  end
  #--------------------------------------------------------------------------
  # * Perform search of the System Database
  #--------------------------------------------------------------------------
  def self.search_system
    #
    # Testing BGM and ME
    test_bgm(eval(GFX[4]))                    # Test Title Screen BGM
    test_bgm(eval(GFX[5]))                    # Test Battle BGM
    test_me(eval(GFX[6]))                     # Test Battle End ME
    test_me(eval(GFX[7]))                     # Test Gameover ME
    #
    for item in SFX                           # Cycle through all System Sfx
      test_se(eval(item))                     # Test each Sound Effect
    end
    #
  end
  #--------------------------------------------------------------------------
  # * Perform search of the Common Events Database
  #--------------------------------------------------------------------------
  def self.search_common
    #
    for event in @common_l                    # Cycle through all common events
      next if event.nil?                      # Skip invalid event
      list = event.list                       # Get list of commands
      search_event_item(list)                 #`Search every item in the event
    end
    #
  end
  #--------------------------------------------------------------------------
  # * Perform search through every project map
  #--------------------------------------------------------------------------
  def self.search_map
    #
    for map in @map_l                         # Cycle through all maps
      #
      id        = map[0]                      # Get the map ID
      text      = "Data/Map%03d.rxdata"       # Define the Map file structure
      filename  = sprintf(text, id)           # Get the formatted map filename
      map_data  = load_data(filename)         # Get the map data
      #
      search_map_params(map_data)             # Search through map parameters
      search_map_events(map_data)             # Search through map events
      #
    end
    #
  end
  #--------------------------------------------------------------------------
  # * Map Database Search (when getting map param data)
  #     map_data : data acquired from a single map
  #--------------------------------------------------------------------------
  def self.search_map_params(map_data)
    #
    test_bgm(map_data.bgm.name)               # Test the map's auto-run BGM
    test_bgs(map_data.bgs.name)               # Test the map's auto-run BGS
    #    
  end
  #--------------------------------------------------------------------------
  # * Map Database Search (when getting map event data)
  #     map_data : data acquired from a single map
  #--------------------------------------------------------------------------
  def self.search_map_events(map_data)
    #
    events = {}                               # Create the Event hash array
    for i in map_data.events.keys             # Sort through map events
      events[i] = map_data.events[i]          # Fill testable Event hash array
    end      
    #
    for event in events                       # Sort through the Event array
      for page in event[1].pages.reverse      # Sort through Event Pages
        list = page.list                      # Get the list of commands  
        search_event_item(list)               # Execute resource item search
      end
    end
    #
  end
  #--------------------------------------------------------------------------
  # * Map Database Search (when sifting through each event command)
  #--------------------------------------------------------------------------
  def self.search_event_item(list)
    #
    for list_item in list                     # Cycle through all items in list
      item_0 = list_item.parameters[0]        # Get the item
      case list_item.code                     # Branch by item's event code
        #
        when 132 ; test_bgm(item_0.name)      # Change Battle BGM
        when 133 ; test_me(item_0.name)       # Change Battle End ME
        when 209 ; self.search_mr(list_item)  # Set Move Route
        when 241 ; test_bgm(item_0.name)      # Play BGM
        when 245 ; test_bgs(item_0.name)      # Play BGS
        when 249 ; test_me(item_0.name)       # Play ME
        when 250 ; test_se(item_0.name)       # Play SE
        #
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Map Database Search (when sifting through the move route command)
  #--------------------------------------------------------------------------
  def self.search_mr(list_item)
    #
    move_l = list_item.parameters[1].list     # Get the move route command list
    for move_item in move_l                   # Cycle through
      item_0 = move_item.parameters[0]        # Get the item
      case move_item.code                     # Branch by item's event code
        when 44 ; test_se(item_0.name)        # Play SE
      end
    end
    #
  end
  #--------------------------------------------------------------------------
  # * Test BGM Audio resource and remove from list
  #     filename     : name of the resource to test
  #--------------------------------------------------------------------------
  def self.test_bgm(filename)
    #
    return if filename.nil?                   # Exit if no file
    return if filename. == ''                 # Exit if empty filename
    test_resource(@root_bgm, filename)        # Perform resource test
    #
  end
  #--------------------------------------------------------------------------
  # * Test BGS Audio resource and remove from list
  #     filename     : name of the resource to test
  #--------------------------------------------------------------------------
  def self.test_bgs(filename)
    #
    return if filename.nil?                   # Exit if no file
    return if filename. == ''                 # Exit if empty filename
    test_resource(@root_bgs, filename)        # Perform resource test
    #
  end
  #--------------------------------------------------------------------------
  # * Test ME Audio resource and remove from list
  #     filename     : name of the resource to test
  #--------------------------------------------------------------------------
  def self.test_me(filename)
    #
    return if filename.nil?                   # Exit if no file
    return if filename. == ''                 # Exit if empty filename
    test_resource(@root_me, filename)         # Perform resource test
    #
  end
  #--------------------------------------------------------------------------
  # * Test SE Audio resource and remove from list
  #     filename     : name of the resource to test
  #--------------------------------------------------------------------------
  def self.test_se(filename)
    #
    return if filename.nil?                   # Exit if no file
    return if filename. == ''                 # Exit if empty filename
    test_resource(@root_se, filename)         # Perform resource test
    #
  end
  #--------------------------------------------------------------------------
  # * Test resource and remove from list
  #     pathway_list : pathway specific resource array
  #     filename     : name of the resource to test
  #--------------------------------------------------------------------------
  def self.test_resource(pathway_list, filename)
    #
    unless pathway_list.include?(filename)    # If file is not found
      return pathway_list                     # Exit method with pathway list
    end
    #
    jeff = pathway_list.index(filename)       # Get index position
    pathway_list.delete_at(jeff)              # Delete at index position
    return pathway_list                       # Exit with updated pathway list
    #
  end  
  #--------------------------------------------------------------------------
  # * Output Content to a file
  #     pathway_list : pathway specific resource array
  #     filename     : name of the output file
  #--------------------------------------------------------------------------
  def self.output_to_file(pathway_list, filename="Unnecessary")
    #
    return if pathway_list.nil?               # Exit if no list
    return if pathway_list == []              # Exit if empty list
    #
    f = File.new(filename,  "w+")             # Create and open the file
    for resource_name in pathway_list         # Cycle through all resources
      f.puts resource_name                    # Print resource name to file
    end
    f.close                                   # Close the file
    #
  end  
end

Resource_Sifter.main

This is an extreme BETA edition, only focusing on the Audio folder. So indeed, I do plan to continue this... which is why it is not in the Scripts Database. its not done!


RE: What's up, RMers? - DerVVulfman - 08-08-2024

(08-05-2024, 07:49 PM)DerVVulfman Wrote: Indeed, this right here can let me open up any script from the database and read it as text IN the game.  And I have included a method that 'compresses' any code into the necessary form which can be properly saved.

Yet another followup, and again... with the script database as days before. I now present THIS:
Code:
module Script_list
  
  #--------------------------------------------------------------------------
  # * Main Processing
  #--------------------------------------------------------------------------
  def self.main
    #
    # Define hashes for scripts
    @scripts_name = {}                        # Holds script names
    @scripts_list = {}                        # Holds scripts as an array
    #
    # Access the script database
    script_setup
    #
  end
  #--------------------------------------------------------------------------
  # * Search project script database
  #--------------------------------------------------------------------------
  def self.script_setup
    #
    path_test = "Data/Scripts.rxdata"         # Define scripts database file
    scripts_db = load_data(path_test)         # Load file into memory
    list_size = scripts_db.size               # Acquire total number of scripts
    #
    # Cycle through them all
    for i in 0...list_size                    # Cycle through all scripts
      script_setup_hash(i+1, scripts_db[i])   # Put into hash array entries
    end
    #
    p @scripts_name[i+1]                      # Show the last script's name
    p @scripts_list[i+1]                      # Show the script decoded as array
    p @scripts_list[i+1][3]                   # Show line 4 of the last script
    #
  end
  #--------------------------------------------------------------------------
  # * Put each script into its own hash entry
  #     key_id : key in hash for the script (starts from 1, not 0 as an index)
  #     script : script contents
  #--------------------------------------------------------------------------
  def self.script_setup_hash(key_id, script)
    #
    txt_id      = script[0]                   # Unknown variable for script...
    txt_name    = script[1]                   # Script name in editor
    txt_decode  = tool_inflate(script[2])     # Script converted to text
    #
    txt_sep     = "\r\n"                      # Define separator between lines
    txt_array   = txt_decode.split(txt_sep)   # Split script into array
    #
    @scripts_name[key_id] = txt_name          # Push the name of the script
    @scripts_list[key_id] = txt_array         # Push the usable script array
    #
  end
  #--------------------------------------------------------------------------
  # * ZLib Tool Inflate
  #     data_text : text to be inflated/expanded from encryption
  #--------------------------------------------------------------------------
  def self.tool_inflate(data_text)
    #
    zlib_tool   = Zlib::Inflate.new()           # Create the ZLib Inflate tool
    new_text    = zlib_tool.inflate(data_text)  # Decrypt/expand with the tool
    zlib_tool.finish                            # Show work done
    zlib_tool.close                             # Close the tool
    return new_text                             # Return the expanded text
    #
  end
  #--------------------------------------------------------------------------
  # * ZLib Tool Deflate
  #     data_text : text to be compressed/encrypted
  #     level     : a defined compression level (see comments)
  #--------------------------------------------------------------------------
  def self.deflate(data_text, level = Zlib::BEST_COMPRESSION)
    #
    # Zlib::DEFAULT_COMPRESSION  (-1)    
    # Zlib::NO_COMPRESSION         0
    # Zlib::BEST_SPEED             1
    # Zlib::BEST_COMPRESSION       9
    #
    zlib_tool = Zlib::Deflate.new(level)        # Create the ZLib Inflate tool
    new_text  = zlib_tool.deflate(data_text, Zlib::FINISH)  # Encrypt until done
    zlib_tool.close                             # Close the tool
    return new_text                             # Return the expanded text
    #
  end
end


# This executes at start
Script_list.main

This will store the 'name' of a script from the editor in one hash array, and the script itself in a similar matching hash array. Separate, but they both use the same matching key (numeric value). As defined in the script, the first script would be key #1, the second being Key #2... rather than by index positions that begin from 0 on up.

Ergo, key 7 would get both the name "Game_Picture" and separately the script for Game_Picture as an array. No clumsy illegal characters, and broken into their individual lines.

How can this be adapted for use to read VX or VXAce scripts? Just change the line that reads path_test = "Data/Scripts.rxdata" on line 21. I'm not fond of RPGMaker VX, but I do have it and did test this to confirm.

USEFULNESS?

Well, this COULD be used to generate a script Exporter of sorts, whether to output to a file or series thereof.

Or more comically... to make a FREAKIN SCRIPT TEXT EDITOR. But ya better be pretty dang good at making it possible to edit text that way.


RE: What's up, RMers? - kyonides - 08-09-2024

Unnecessary if you know that such data is being stored in $RGSS_SCRIPTS already. Tongue sticking out


RE: What's up, RMers? - DerVVulfman - 08-09-2024

(08-09-2024, 12:20 AM)kyonides Wrote: Unnecessary if you know that such data is being stored in $RGSS_SCRIPTS already. Tongue sticking out
That is not really true.

Whether you load the scripts database with:
 script_list = load_data("Data/Scripts.rxdata")
or
 script_list = $RGSS_SCRIPTS
the result is the same...

[Image: attachment.php?aid=2127]
A test result with the actual script content under ZLIB compression.
Generated with $RGSS_SCRIPTS (the same result with my code).

My above code acquires the script database, breaks the database down into a hash array so you can grab an individual script by a  key value, expands and basically decrypts each script in the database itself into something legible, and breaks each script down into individual lines to make it even more useful and essentially legible.

The only thing argued is that I used the classic load_data command used already within the system rather than $RGSS_SCRIPTS which you have just suggested. Other than that, nothing else would have changed.

BUT... on an incidental note, the $RGSS_SCRIPTS variable appears nowhere in the default scripts, no where in the help manual, would surely not be in Ruby-Docs, and does not appear in any Google search.  Considering the Game.Ini file has the line Scripts=Data\Scripts.rxdata, I assume it is here where $RGSS_SCRIPTS comes into play. Only one who would have been working on their own replacement engine would have known that.


RE: What's up, RMers? - kyonides - 08-09-2024

Nah, just print all of the global variables by calling:

Code:
print global_variables.join("\n")



RE: What's up, RMers? - DerVVulfman - 08-09-2024

Oh, dang.  Part of the Kernel module. Indeed, global_variables would be a way to find all the globals... as long as at the bottom of the script list. Heh. Put it at the top and it skips everything below it. Winking

Well, I have made a breakthrough when it comes to sifting through scripts.  I can now retrieve the custom cache methods from RPG::Cache from any custom script! 

Indeed, if a script appeared ...

module RPG::Cache
  def self.menus(filename, hue=0)

or

module RPG
  module Cache
    def self.cursors(filename)

... it can retrieve both the names menus and cursors. Which means it does recognize the RPG::Cache module, whether formatted in one line or broken up in twain.

This is stage 1 in being able to actively search scripts for resources.  Stage 2 will require me to delve into each method (def) and get the pathway suggested by each method.  Given my proclivity to have the path be 'variable', this could be ... entertaining.


RE: What's up, RMers? - DerVVulfman - 08-16-2024

I can now define the RPG::Cache.(method_name) to the actual subfolders... mainly assumed to be in the project's Graphics folder.

[Image: attachment.php?aid=2128]
...defining the actual Folder Pathways to custom RPG::Cache methods!

The right image is a 'sample' RPG::Cache code that I am using to work out the mechanics of the sifter cache folder scripts. The first is a 'direct' assignment of a folder name, the second and third are versions that assumes the game developer (like YOU) can change the name in a script.

And the left image shows that I can create the relationship between the calling methods created within RPG::Cache and the actual folder paths. And in it, the 'path' word is merely a placeholder for what is meant to be the name of the subfolder... assuming certain conditions of course.

THIS part will be a FUN one to figure out. But once done, I very well may be able to acquire resources used by custom scripts for my resource sifter!


RE: What's up, RMers? - kyonides - 08-18-2024

Now working on the KGuildFame XP demo loosely based on the Guild scene available in the Generations of War games. Happy with a sweat I guess I should have picked the term Titles instead of Fame there, but I'm lazy Laughing so I'll be keeping the current name for good. Tongue sticking out

[Image: attachment.php?aid=2129]



RE: What's up, RMers? - Remi-chan - 08-18-2024

t update last week due to a number of factors, most of which was recovering from a surgery alongside just a general lack of productivity in regards to such!

But that has changed, i am getting close to my full recovery and dismorning I implemented some things. For see, someone has been busy during my sojourn of silence.

Pjcr
To say my gambit to motivate him to send me files went well is putting it lightly. Ten files per fortnight at minimum now, he should be done with the vast majority of the games files before it becomes a concern.
[Image: qPhDk7Y.png]
I didn't even need over half of these yet! The white skeleton and ghosty gal aren't even until Disk 3, but there was a funny mishap and it just saves me time in the future.

So the winning strat was to make the resolution of the files small enough that they could be uploaded before something happened to stop them. We started at 25% size but decided 50% was fine after some experiments.

So here's a single screenshot of just one of these in-game.
[Image: 0lCgBgY.png]
Why don't I show the rest?

1. Lots of clutter
2. I have videos for that!

Svana and the Sylph

It's pretty rare for someone to so easily bypass Fyori's general stiff nature, but Svana broke through by being a philosophical rizzler, truly brazen!

Mothers Reunited

In this episode of "Mum powers are bullsh**!" we get to see a casual conversation between Svoli and her mother Svana.

Finally, these snakes have been hanging for some love, so lets get to our final video!

The High Courtesan

A redo of an older video, now with all the updated portraits.

For the record, I am still largely on break. I plan to return to gamedev full time in early to mid-september.

Until then, implementing some files just saves me a bit of work later and gives me some youtube content.

Speaking of my youtube, we recently hit 250 subscribers, at last. It's been slow-going but I'ma sticking with it. My twitter is also in the 2300s.


RE: What's up, RMers? - DerVVulfman - 08-23-2024

My endeavor to scour through the actual Script database in RPGMaker XP... WHILE running in RPGMaker XP... ran into an odd snag.

If someone wished to set up custom sub-folders within their project's Graphics folder, they would likely add their folder paths to the RPG::Cache module.  And in doing so, they would undoubtedly create a new method* to retrieve the graphics from that folder.  So my work to scour through the RPG::Cache module requires that my code find any viable methods.  Viable methods would provide folder-paths such as:
Graphics\KyoQuestIcons or Graphics\RemiAnims.

If ya like the name of the subfolder, kyonides... go for it. 

This way, I could find the self.kyo_q_icon method and acquire its matching Graphics\KyoQuestIcons sub-folder.

But, what if a method discovered was not one that retrieved a viable bitmap for use?  What if it was an already existing method that someone wished to tamper with, or one that was added that performed a new or unique function? Therein was the problem... It would find a method, but had no folder-paths to use.

Until I decided to create an array to test against:

Quote:module Script_list
 
  #--------------------------------------------------------------------------
  # * Invariables
  #--------------------------------------------------------------------------
  SL_IGNORE = "Cache_list.main"
 
  # Names of all default cache method names
  DEFAULTS  = ['animation', 'autotile', 'battleback', 'battler', 'character',
              'fog', 'gameover', 'icon', 'panorama', 'picture', 'tileset',
              'title', 'windowskin', 'tile']

             
  # Methods within RPG::Cache that are not to be checked           
  FORBID    = ["def self.clear", "def self.load_bitmap", "def self.test_1",
             
"def self.test_2" ]

             
  # Search test phrases
  PHRASE    = [ "module RPG::Cache", "module RPG", "module Cache", "def self.",
               
"self.load_bitmap", "begin", "if ", "end" ]

               
  PATH_BROKE = "INVALID"
               
  #--------------------------------------------------------------------------
  # * Main Processing
  #--------------------------------------------------------------------------
  def self.main
    #

This code has a FORBID constant array, an array that listing methods that exist within the RPG::Cache module that are not to be examined.  While the first two are part of the RPG::Cache's innate system, the other two are custom methods I wrote that allow for testing of cached graphics existence.  So indeed, this can be expanded.

And now, it will skip any method that is within the RPG::Cache module that shouldn't be tested... and not give me any stupid errors.

But the next phase is going to be the headache phase...  

In the event that the folder-path (ex: Graphics\Goofball) isn't fixed, but is instead something the game developer can configure, I have to search for the SOURCE of the path... the configuration page, section, variable.

For this, I have to determine what kind of search I am even doing?   If the variable used to define the folder path begins with "$", that is a global variable and can be ANYWHERE in the entire run of the script database.  If it has '::' within the name, it is likely a constant within some separate configuration module... and may be in pairs.  And I have to test for both instance and local variable usage too.

Much to do.