Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

Data Structure Generator

Data Structure Generator
Version 1.0


Description:
The idea behind this script was simply: How could I create a data struct class, container class, and make creating these objects easy? The result was a module to generate both Ruby code that creates the Ruby data struct class, creates the container and everything for you. Additionally, a nice little html page that generates the Ruby code for creation of all the objects. More additionally, it organizes your containers for you, auto-increases object id instances, and adds the option to save and load objects into rxdata files!

The Script:
Can you believe all this power in a script under 400 lines for coding, with comments included?
Code:
#==============================================================================

# ** Data Structure Generator

#------------------------------------------------------------------------------

# SephirothSpawn

# Version 1.0

# 2010-01-27

#------------------------------------------------------------------------------

# * Details

#

#   This script was designed to auto-generate data structures and make a nice

#   like html form to allow non-scripters to create these new objects with

#   no scripting required! So any non-scripter can make can not only create

#   data structures, but create an entire database!

#

#   This cannot be easier (and I challenge anyone)!

#

#   The object class is generated auto-matically. The container object is added

#   to a pre-set DSG object ($data_dsg). The generated Ruby code also

#   adds the option to save and load the objects from rxdata files as well.

#

#   The class generated will always be a child class of DSG::Data_Object.

#   Why? The parent class serves 2 functions:

#

#    1) Auto-Set an ID instance variable that increases by 1 after each object

#       is created

#    2) Auto-Sets object directly into the container. You will never need to

#       set the object into $data container.

#

#

#   A HTML page with a form is generated for users to generate Ruby code to

#   create the objects into the data structure. Users fill out this form and

#   Ruby code is generated for you.

#------------------------------------------------------------------------------

# * Generating Ruby Data Struct class and HTML form

#

#   1) Insert a new area into the script list

#   2) Start Data Struct - Add this line

#      DSG.start(class_name, rxdata_name, container_name)

#

#      Replace class_name with a Ruby class name (ie: 'Card')

#      Replace rxdata with data filename (ie: 'Cards.rxdata')

#      Replace container name (ie: 'cards')

#   3) Add required instances (instances that are passed in the initialization)

#

#      For each required instance, add this line:

#

#      DSG.add_req_instance(instance_name, description, default, type)

#

#      Replace instance_name with your instances name (ie: 'suit')

#      Replace description with a simple description for instance (ie: 'Suit')

#      Replace default with default value (ie: 'Hearts')

#      Replace type with Ruby type for values (ie: 'String')

#

#      * Note that the default is only used for HTML form fields

#

#   4) Add optional instances (instances not specified in the initialization)

#

#      For each instance, add this line:

#

#      DSG.add_instance(instance_name, description, default, type)

#

#      * See step 3 for replacement instructions

#      * Note that the default value is specified in object class init.

#

#   5) Finish Data Struct - Add this line of code:

#      DSG.finish(ruby_name, html_name)

#

#      Replace ruby_name with .txt file generated with object class code

#       (ie: 'Card.txt') (Defaults to 'Generate.txt')

#      Replace html_name with .html file form (ie: 'Card.html')

#       (Defaults to 'Generate.html')

#      

#   6) The code generated in the .txt file is your Ruby code to insert into

#      script editor.

#

#   7) Open the .html file and complete the form. For each object, press the

#      Add Item button. When finished, copy and paste code into your first

#      generated code between the lines:

#

#      # Insert Database Modifications Here

#      <Insert HTML Generated Code Here>

#      # Stop Modifications

#==============================================================================

 

#==============================================================================

# ** Data Structure Generator

#==============================================================================

 

module DSG

  #--------------------------------------------------------------------------

  # * Start

  #--------------------------------------------------------------------------

  def self.start(class_name, rxdata_name, container_name)

    @class_name  = class_name

    @rxdata_name = rxdata_name

    @container_name = container_name

    @required_instances = {}

    @instances = {}

  end

  #--------------------------------------------------------------------------

  # * Add Required Instance

  #--------------------------------------------------------------------------

  def self.add_req_instance(instance_name, description, default, type)

    @required_instances[instance_name] = [description, default, type]

  end

  #--------------------------------------------------------------------------

  # * Add Instance

  #--------------------------------------------------------------------------

  def self.add_instance(instance_name, description, default, type)

    @instances[instance_name] = [description, default, type]

  end

  #--------------------------------------------------------------------------

  # * Finish

  #--------------------------------------------------------------------------

  def self.finish(ruby_name = 'Generate.txt', html_name = 'Generate.html')

    # Generate Data Structure Ruby Code

    self.generate_ruby_data_struct(ruby_name)

    # Generate HTML Sheet Field

    self.generate_html_form(html_name)

  end

  #--------------------------------------------------------------------------

  # * Generate Data Structure Ruby Code

  #--------------------------------------------------------------------------

  def self.generate_ruby_data_struct(ruby_name)

    # Start Line Code

    s  = []

    s << '#=============================================================================='

    s << '# ** Load Rxdata (Set to false when change in database)'

    s << '#=============================================================================='

    s << ''

    s << 'module DSG'

    s << "  Load_#{@container_name.capitalize} = false"

    s << 'end'

    s << ''

    s << '#=============================================================================='

    s << "# ** #{@class_name}"

    s << '#=============================================================================='

    s << ''

    s << "class #{@class_name} < DSG::Data_Object"

    s << '  #--------------------------------------------------------------------------'

    s << '  # * Public Instance Variables'

    s << '  #--------------------------------------------------------------------------'

    (@instances.keys + @required_instances.keys).sort.each do |instance_name|

      s << "  attr_accessor :#{instance_name}"

    end

    s << '  #--------------------------------------------------------------------------'

    s << '  # * Object Initialization'

    s << '  #--------------------------------------------------------------------------'

    s << "  def initialize(#{@required_instances.keys.sort.join(', ')})"

    s << '    super()'

    @required_instances.keys.sort.each do |instance_name|

      s << "    @#{instance_name} = #{instance_name}"

    end

    @instances.keys.sort.each do |instance_name|

      default = @instances[instance_name][1]

      s << "    @#{instance_name} = #{self.object_to_string(default)}"

    end

    s << '  end'

    s << 'end'

    s << ''

    s << '# Creates Container Object'

    s << "$data_dsg.add_container('#{@container_name}')"

    s << "DSG::Containers.module_eval('attr_reader :#{@container_name}')"

    s << "DSG::Data_Object.module_eval(\"@@container_objects[#{@class_name}] = '#{@container_name}'\")"

    s << ''

    s << '#=============================================================================='

    s << '# ** Load or Create Database'

    s << '#=============================================================================='

    s << ''

    s << "if DSG::Load_#{@container_name.capitalize}"

    s << "  $data_dsg.#{@container_name} = load_data('Data/#{@rxdata_name}')"

    s << 'else'

    s << ''

    s << '# Insert Database Modifications Here'

    s << ''

    s << '# Stop Modifications'

    s << ''

    s << '# Save Data File'

    s << "save_data($data_dsg.#{@container_name}, 'Data/#{@rxdata_name}')"

    s << ''

    s << 'end'

    # Write Ruby Code

    file = File.new(ruby_name, 'w+')

    s.each {|l| file.write("#{l}\n")}

    file.close

  end

  #--------------------------------------------------------------------------

  # * Generate HTML Doc Form

  #--------------------------------------------------------------------------

  def self.generate_html_form(html_name)

    # Start Line Code

    s  = []

    s << '<html>'

    s << '<head>'

    s << '<meta http-equiv="Content-Language" content="en-us">'

    s << '<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">'

    s << '<title>Data Structure Generator 1.0</title>'

    s << '<script type="text/javascript">'

    s << ''

    s << '// * Variable Decloration'

    s << "var class_name = '#{@class_name}'"

    @instances.keys.sort.each do |instance_name|

      default = @instances[instance_name][1]

      s << "var #{instance_name}_default = #{self.object_to_string(default)}"

    end

    s << ''

    s << 'function AddItem() {'

    s << ''

    s << '// Get Current Code'

    s << 'text  = document.form1.textarea.value'

    s << ''

    s << '// Add Comment'

    s << "text += '# ' + document.form1.object_coment.value"

    s << 'text += "\n"'

    s << ''

    s << '// Create Object'

    s << "text += class_name + '.new('"

    if @required_instances.size > 0

      s << '// Add Required Items'

      @required_instances.keys.sort.each do |instance_name|

        s << "text += document.form1.#{instance_name}.value"

        if instance_name != @required_instances.keys.sort.last

          s << 'text += ", "'

        end

      end

      s << '// Finish Required Items'

    end

    s << 'text += ")\n"'

    s << ''

    if @instances.size > 0

      s << '// Modify Instances Comparing Defaults'

      @instances.keys.sort.each do |instance_name|

        s << "value = document.form1.#{instance_name}.value"

        s << "if (value != #{instance_name}_default) {"

        s << "  text += 'object.#{instance_name} = ' + value"

        s << '  text += "\n"'

        s << '}'

      end

      s << ''

    end

    s << '// Write Text'

    s << 'document.form1.textarea.value = text'

    s << '}'

    s << '</script>'

    s << '</head>'

    s << ''

    s << '<body text="#FFFFFF">'

    s << ''

    s << '<div align="center">'

    s << '  <form action="" method="POST" name="form1" id="form1">'

    s << '    <table border="1" cellspacing="3" cellpadding="5" bordercolor="#111111" width="600" bgcolor="#808080">'

    s << '      <tr>'

    s << '        <th colspan="3" bgcolor="#333333"><font size="5">Data Structure Generator 1.0</font></th>'

    s << '      </tr>'

    s << '      <tr>'

    s << '        <td colspan="2" bgcolor="505050">Comment Heading (To Seperate Items)</td>'

    s << '        <td bgcolor="505050"><input type="text" name="object_coment" size="20" value="New Item" /></td>'

    s << '      </tr>'

    if @required_instances.size > 0

      s << '      <tr>'

      s << '        <th colspan="3" bgcolor="#444444"><font size="4">Required Settings</font></th>'

      s << '      </tr>'

      s << '      <tr>'

      s << '      <!-- Start Required Instance Fields -->'

      @required_instances.keys.sort.each do |instance_name|

        s << '      <tr>'

        s << "        <td width='30%'>#{@required_instances[instance_name][0]}</td>"

        s << "        <td align='center' width='35%'><input type='text' name='#{instance_name}' size='20' value='#{@required_instances[instance_name][1]}'><br><font size='1'>#{instance_name}</font></td>"

        s << "        <td width='35%'>Default: #{@required_instances[instance_name][1]} (#{@required_instances[instance_name][2]})</td>"

        s << '      </tr>'

      end

      s << '      <!-- End Required Instance Fields -->'

    end

    if @instances.size > 0

      s << '      <tr>'

      s << '        <th colspan="3" bgcolor="#444444"><font size="4">Optional Settings</font></th>'

      s << '      </tr>'

      s << '      <!-- Start Instance Fields -->'

      @instances.keys.sort.each do |instance_name|

        s << '      <tr>'

        s << "        <td width='30%'>#{@instances[instance_name][0]}</td>"

        s << "        <td align='center' width='35%'><input type='text' name='#{instance_name}' size='20' value='#{@instances[instance_name][1]}'><br><font size='1'>#{instance_name}</font></td>"

        s << "        <td width='35%'>Default: #{@instances[instance_name][1]} (#{@instances[instance_name][2]})</td>"

        s << '      </tr>'

      end

      s << '      <!-- End Instance Fields -->'

    end

    s << '      <tr>'

    s << '        <td colspan="3" bgcolor="#222222" align="center" ><input type="button" name="Submit" value="Add Item" onclick="AddItem()" /></td>'

    s << '      </tr>'

    s << '      <tr>'

    s << '        <td colspan="3" bgcolor="#FFFFFF">'

    s << '          <label>'

    s << '            <textarea name="textarea" cols="70" rows="10" wrap="physical" value=""></textarea>'

    s << '          </label>'

    s << '        </td>'

    s << '      </tr>'

    s << '    </table>'

    s << '  </form>'

    s << '</div>'

    s << ''

    s << '</body>'

    s << ''

    s << '</html>'

    # Write HTML Code

    file = File.new(html_name, 'w+')

    s.each {|l| file.write("#{l}\n")}

    file.close

  end

  #--------------------------------------------------------------------------

  # * Object to String

  #--------------------------------------------------------------------------

  def self.object_to_string(object)

    case object

    when Array

      object.collect! {|o| self.object_to_string(o)}

      return "[#{object.join(', ')}]"

    when Hash

      array = []

      default.each do |k, v|

        k_string = self.object_to_string(k)

        v_string = self.object_to_string(v)

        array << "#{k} => #{v}"

      end

      return "{#{array.join(', ')}}"

    when String

      return "'#{object}'"

    else

      return object

    end

  end

end

 

#==============================================================================

# ** DSG::Data_Object

#==============================================================================

 

class DSG::Data_Object

  #--------------------------------------------------------------------------

  # * Container Objects

  #--------------------------------------------------------------------------

  @@container_objects = {}

  #--------------------------------------------------------------------------

  # * Object ID#s (Auto-Counter)

  #--------------------------------------------------------------------------

  @@ids = {}

  #--------------------------------------------------------------------------

  # * Public Instance Variable

  #--------------------------------------------------------------------------

  attr_accessor :id

  #--------------------------------------------------------------------------

  # * Object Initialization

  #--------------------------------------------------------------------------

  def initialize

    # Create Class Counter (If not defined)

    @@ids[self.class] = 0 unless @@ids.has_key?(self.class)

    # Add class counter

    @@ids[self.class] += 1

    # Set ID

    @id = @@ids[self.class]

    # Add object to container

    eval "$data_dsg.#{@@container_objects[self.class]}[@id] = self"

  end

end

 

#==============================================================================

# ** Data Structure Generator::Containers

#==============================================================================

 

class DSG::Containers

  #--------------------------------------------------------------------------

  # * Add Container

  #--------------------------------------------------------------------------

  def add_container(instance_name)

    eval "@#{instance_name} = {}"

  end

end

 

$data_dsg = DSG::Containers.new

Example:
Take for instance, something like my Triple Triad script. The following would create a class for these with the basic 4 powers and element for the cards. It would save them in a container $data_dsg.cards.

Code:
DSG.start('Card', 'Cards.rxdata', 'cards')

stats = {

  'np' => 'North Power',

  'ep' => 'East Power',

  'sp' => 'South Power',

  'wp' => 'West Power',

}

stats.each do |stat, string|

  DSG.add_req_instance("#{stat}", "#{string}", 1, 'Integer')

end

DSG.add_instance('element', 'Element', 0, 'Integer')

DSG.finish

Running this code would end up generating this Ruby code for you:
Code:
#==============================================================================

# ** Load Rxdata (Set to false when change in database)

#==============================================================================

 

module DSG

  Load_Cards = false

end

 

#==============================================================================

# ** Card

#==============================================================================

 

class Card < DSG::Data_Object

  #--------------------------------------------------------------------------

  # * Public Instance Variables

  #--------------------------------------------------------------------------

  attr_accessor :element

  attr_accessor :ep

  attr_accessor :np

  attr_accessor :sp

  attr_accessor :wp

  #--------------------------------------------------------------------------

  # * Object Initialization

  #--------------------------------------------------------------------------

  def initialize(ep, np, sp, wp)

    super()

    @ep = ep

    @np = np

    @sp = sp

    @wp = wp

    @element = 0

  end

end

 

# Creates Container Object

$data_dsg.add_container('cards')

DSG::Containers.module_eval('attr_reader :cards')

DSG::Data_Object.module_eval("@@container_objects[Card] = 'cards'")

 

#==============================================================================

# ** Load or Create Database

#==============================================================================

 

if DSG::Load_Cards

  $data_dsg.cards = load_data('Data/Cards.rxdata')

else

 

# Insert Database Modifications Here

 

# Stop Modifications

 

# Save Data File

save_data($data_dsg.cards, 'Data/Cards.rxdata')

 

end

How neat? It would also generate a html page for you. Fill out the form, create your objects, copy and page and the code is done.

All objects could simply by accessed via:
Code:
$data_dsg.cards[card_id]




Author Notes:
I am all up for suggestions to make this easier, or questions on how to use this. Other than that...

Enjoy!
 
Hm, a very versatile script... should shave some time off one or the other project. Great concept there! :)

As you're asking for suggestions... as far as I can tell, this just creates the methods for you to copy&paste them into the project, right? I think it'd be both easier on the scripter, as well as less line-consuming (while being more resource-draining) if you'd make a script simulating those classes. In other words, a script that injects 'virtual scripts' (wonky term for a virtual media really XD ) into the project, yet only needs the big code once.
So, instead of writing a script that outputs a script to paste into the project, you'd have a script that runs all the predefined cases (one would be the cards example) right in the game itself - no need to copypaste anything, just define the arguments.

Don't get me wrong here though... I really like this script, and my addition would only be for comforting reasons - probably not worth the effort ^^"
 
Hmm.. I thought about this as well. It would have to generate the code everytime game is loaded, and eval what could be a potential chunk of data. I could work with Zeriab, and work on injecting the script into the Scripts.rxdata file, deleting the original generator code. But in the end, that's not a lot more than copy and paste itself, except the script would do be doing it for you. Overall, I just didn't want to have to eval everything.

However, in the end this is for scripter a to create script b so user c can copy and paste the script into their project and use the html page to generate their database (as you would with the database editor). If any other person planned on using script b, they would end up coping something from scripter a, and might as well not be something that needs to be eval'd more than it has to be.


I also planned on originally not making a source code for the original Ruby data struct. Instead that would just appear in the html page, and you could copy and paste the entire code in one block. I didn't do that because A) I'm lazy and didn't think of it until I was basically done with this and B) I wanted to keep the data structure code separate from all the objects being created. The hope was to make it easier for non-scripters to modify objects without having to know ruby. I'll end up throwing this in to the 1.1 (Generator (Complete).html. I really only worked on this for about 2 hours (and most of that was working on the html page layout and javascript).

My next plan for this system is actually to generate the html page to look more like the XP/VX editor itself. On the left you would have the list of each item list (By ID) and on the right the data fields. However I am not super proficient in html/javascript. I believe I could just use a javacode to add a new value to a selectable list for the left field, have a javacode so when the item from the list is clicked it made a div visible and hid the others and when the user was done, like in the editor, just click ok and a complete code was generated. I might just do this in the next week to work on my javascript coding. To be honest, this was probably the second time I have even worked with it. lol
 
Interesting... every time I talk to you about some script, you tell me you plan to get into html/javascript a bit more ^^ Well, personally, I've gotten some experience with html and php/mysql because of both job-oriented and private interest, so if you need any help regarding stuff like that that isn't javascript, give me a call, I'd be happy to help ya out.

Other than that, I guess we were thinking kind of along the same lines... now of course, you can put it a little bit ( :wink: ) better, but yeah... it'd be a fancy feature, but nothing that's necessarily needed, or worth the effort... like I said ^^

Good luck with the further process!
 

Gust

Member

Man... you are a god =O
XD Very interesting idea.
Just the "creating database" system look weird to me... you have to get out of the maker, open an html page, editing, copying and then go back to the maker. If you could make a Scene to edit the data from inside the game, it would feel more natural, I think. The problem is how to do a nice interface with RGSS limited resources (that being a pain for myself lately u.u).

Anyway, very good work. I like your "scripts for scripters", that are not plug'n'play systems, but useful tools for programmers.
 
I avoided using any Scene objects in XP/VX. Simply because they are really so ugly and actually generating the code for this to be done in Ruby will take longer. So ugly and harder to code made me just go with html/javascript.

Even if I did make a scene for this, and it did generate your rxdata file, you'd still have to inject a call line into your editor and in the end, what's one line or x lines? :smile:
 

Gust

Member

SephirothSpawn":35b393y7 said:
I avoided using any Scene objects in XP/VX. Simply because they are really so ugly and actually generating the code for this to be done in Ruby will take longer. So ugly and harder to code made me just go with html/javascript.

Even if I did make a scene for this, and it did generate your rxdata file, you'd still have to inject a call line into your editor and in the end, what's one line or x lines? :smile:

Yeah, I agree with you on that, the Scenes are ugly, and making any kind of interface resembling a "form" using rgss is a pain. But yet, having to jump from one program to the other and back again, takes away some of the magic XD
Anyway, it still free us from some work, it's just that I'm lazy, I guess lol
 
hum maybe you could create a small ruby program with a GUI, that handles the DB side.
it would adapt its apperance ( i mean the field type ) with the attributes of the rxdata file you load :) That'd be easier i think for you, as it'd be ruby only, though it means getting used to work with a GUI library ( i'd suggest Wx::Ruby )
then once finished you provided a simple .exe with OCRA ( a exe generator for ruby ) :) and VOiLA!

Small suggestion: -> be great to use something better than a Global variable to access the data...
 
I've actually started working a little with Wx::Ruby (and sugar? can't remember the name, but that was a while ago with Trick) and plan to actually make this for that. The final plan is to have a base exe that is a new editor itself. It checks folders for what new data items there are and creates tabs and looks just like the original xp editor. I'm also planning on creating my own load_data/save_data methods for saving the objects and loading them in XP.

I also have already re-wrote this to be used in conjunction with additions to the class Data that works as we have talked about in the MACL thread (loading instances into the module and all $data objects being accessed via Data.object). I plan to update this script when I complete this along with javascript mod mentioned above so you can edit all objects easier.
 
This script is very interesting.
Save time at work to create scripts,I think I will not use it, but it's still a great script.
Wonderful job
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top