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.

RGSS Table, Color & Tone Classes

Raku

Member

trebor777":zfmnf2xj said:
Actually if we know how works the table we should be able to write directly

t = Table.new(nx, ny, nz)

for all the cases... as ny and nz, should be set to 0 by the unpack if the loaded tabled only used one or two dimensions.

Secondly you have a small bug that may happen if you try to dump a table which has just been created with only 1 or 2 dim
I think it's related to this; @data.pack('S#{size}') crashes "too few arguments".

I'll later tonight try to fix and improve that anyway.


EDIT^^
Al right Here is the fixed version:

Not Having a 2nd dimension is the same as having a 2nd dimension which only contains 1 item. (can be applied for a 3rd Dimension)
Therefore instead of increasing the dim size if the y or z are greater than 0, we do it if those are greater than 1
The default value for y and z also becomes 1.

Code:
 

class Table

  def initialize(x, y = 1, z = 1)

    y = [y.abs,1].max # Just in case the user types 0, or a negative value

    z = [z.abs,1].max

&nbsp; &nbsp; @dim = 1 + (y <=> 1) + (z <=> 1) &nbsp; # <=> returns 1 if greater, 0 if the same, -1 if smaller

&nbsp; &nbsp; @xsize, @ysize, @zsize = x, y, z # We don't need to set the 1 to 1 ;)

&nbsp; &nbsp; @data = Array.new(@xsize * @ysize * @zsize, 0)

&nbsp; end

&nbsp; def [](x, y = 0, z = 0)

&nbsp; &nbsp; @data[x + y * @xsize + z * @xsize * @ysize]

&nbsp; end

&nbsp; def []=(*args)

&nbsp; &nbsp; x = args[0]

&nbsp; &nbsp; y = args.size > 2 ? args[1] : 0

&nbsp; &nbsp; z = args.size > 3 ? args[2] : 0

&nbsp; &nbsp; v = args.pop

&nbsp; &nbsp; @data[x + y * @xsize + z * @xsize * @ysize] = v

&nbsp; end

&nbsp; def _dump(d = 0)

&nbsp; &nbsp; [@dim, @xsize, @ysize, @zsize, @xsize * @ysize * @zsize].pack('LLLLL') << @data.pack("S#{@xsize * @ysize * @zsize}")

&nbsp; end

&nbsp; def self._load(s)

&nbsp; &nbsp; size, nx, ny, nz, items = *s[0, 20].unpack('LLLLL')

&nbsp; &nbsp; t = Table.new(nx, ny, nz)

&nbsp; &nbsp; t.data = s[20, items * 2].unpack("S#{items}")

&nbsp; &nbsp; t

&nbsp; end

&nbsp; attr_accessor(:xsize, :ysize, :zsize, :data)

end

&nbsp;
trebor777, just a few comments from my end...

1) I did some "field research" on how the RMXP version of Table behaves for various inputs. Here are the results. The comments show what is packed into the dumped string:

Ruby:
                              # =>  [dim, x, y, z, size, data (# of bytes)]

t = Table.new(2,4,2).dump    # =>  [  3, 2, 4, 2,   16,                32]

t = Table.new(1,1,1).dump    # =>  [  3, 1, 1, 1,    1,                 2]

t = Table.new(1,1,-1).dump   # =>  [  3, 1, 1, 0,    0,                 0]

t = Table.new(1,1).dump      # =>  [  2, 1, 1, 1,    1,                 2]

t = Table.new(1).dump        # =>  [  1, 1, 1, 1,    1,                 2]

t = Table.new(2,3,-4).dump   # =>  [  3, 2, 3, 0,    0,                 0]

t = Table.new(2,-4,2).dump   # =>  [  3, 2, 0, 2,    0,                 0]

It seems like RMXP doesn't crash when negative arguments are passed, but it also dumps a zero-sized array (which seems to be a bug). I recommend we copy RMXP's behavior, but NOT its bugs. ;)

2) I agree with your point that we should handle 0 and negative values in the initialize method, but should it really assume a dimension -x is x? I think 0 is more intuitive to the user. For example, a Table.new(1,-4,3) would create a 1 x 1 x 3 table instead of a 1 x 4 x 3 table. I think this is intuitive because usually when users give a negative array value, it is because some variable has accidentally gone negative, not because the dimension sign was reversed. What do you think?

If we want to replicate the dump behavior of RMXP exactly, here's what I propose, using a little from everyone. This one:
Ruby:
class Table

  def initialize(x, y = 0, z = 0)

    @dim = 1 + (y > 0 ? 1 : 0) + (z > 0 ? 1 : 0)

    @xsize, @ysize, @zsize = x, [y, 1].max, [z, 1].max

    @data = Array.new(x * y * z, 0)

  end

  def [](x, y = 0, z = 0)

    @data[x + y * @xsize + z * @xsize * @ysize]

  end

  def []=(*args)

    x = args[0]

    y = args.size > 2 ? args[1] : 0

    z = args.size > 3 ? args[2] : 0

    v = args.pop

    @data[x + y * @xsize + z * @xsize * @ysize] = v

  end

  def _dump(d = 0)

    [@dim, @xsize, @ysize, @zsize, @xsize * @ysize * @zsize].pack('LLLLL') <<

    @data.pack("S#{@xsize * @ysize * @zsize}")

  end

  def self._load(s)

    size, nx, ny, nz, items = *s[0, 20].unpack('LLLLL')

    t = Table.new(*[nx, ny, nz][0,size])           # The * breaks apart an array into an argument list

    t.data = s[20, items * 2].unpack("S#{items}")

    t

  end

  attr_accessor(:xsize, :ysize, :zsize, :data)

end

I've tested it and I believe the dumped string matches the RMXP version 100% (except it doesn't have the negative dimension bug). Also, I settled on hanmac's the Table.new(*[...][...]) because, during some test over dumping many tables I didn't see a notable difference in speed. It's more succinct and with a comment it should be apparent what the syntax does.
 
hum I think you're right with the whole negative thing, that they should becomes 0values, but as i said: 0 means 1 ^^ so they should becomes ones instead :biggrin:

So as ysize = 1, and zsize = 1 if y or z = 0, why not put y and z by default to 1 when they're not passed as arguments, or
when y or z are wrong values.
Also the dimension calculation just need to be adapted to work with ones instead of zeros(as in my version).
( and I quite like the use of <=> instead of ?: )

In the end in the load method, ny and nz will always have a value of 1 if we only have 1 dimension. (or nz will be 1 but not ny if 2 dimensions)( either with your's or my method ).

Therefore we can call simply Table.new(nx,ny,nz) all the time and don't have to worry about the number of argument needed.
 

Raku

Member

trebor777":2cntigak said:
hum I think you're right with the whole negative thing, that they should becomes 0values, but as i said: 0 means 1 ^^ so they should becomes ones instead :biggrin:

So as ysize = 1, and zsize = 1 if y or z = 0, why not put y and z by default to 1 when they're not passed as arguments, or
when y or z are wrong values.
Also the dimension calculation just need to be adapted to work with ones instead of zeros(as in my version).
( and I quite like the use of <=> instead of ?: )

In the end in the load method, ny and nz will always have a value of 1 if we only have 1 dimension. (or nz will be 1 but not ny if 2 dimensions)( either with your's or my method ).

Therefore we can call simply Table.new(nx,ny,nz) all the time and don't have to worry about the number of argument needed.
Oh, sorry I don't think I explained well. The reason for making the y and z default parameters 0 is because we need to differentiate between the following two cases:

Ruby:
t = Table.new(1)._dump       # @dim, @x, @y, @z = 1, 1, 1, 1

 

t = Table.new(1,1,1)._dump   # @dim, @x, @y, @z = 3, 1, 1, 1
For some crazy reason, the RMXP Table class dumps 1 for the dimension in the first case and 3 for the dimension in the second case. The only thing I can think is that it is something about the underlying C/C++ implementation (since arrays aren't mutable there). Anyhow, If we make the defaults 1, we won't be able to differentiate between the above two cases and so the dumps won't be the same (indeed the code with defaults of 1 dumps 1 in both cases).

By the way, I completely agree with your style (defaulting to 1). :biggrin: The only issue is the compatibility with RMXP. :sad:
 
aaah well my bad then :p
Bad design from Enterbrain again.... they did a lot of stuff wrong lol...
I wonder if it's the same with RMVX actually?...
 
PD: Thanks to trebor777, who notified me that for strings the << method is faster than the + method.
Spend a few time working on Benchmark, if you don't know it.
You can easily compare method1 to method2, and it's very usefull for optimizations.

________

About your code, I don't agree the way you've done it.
I read somewhere that Table was created because arrays with a lot of elements are bad managed within Ruby.
(and because the def [] (x,y,z) method is more intuitive)
So, that is why Table should be a multidimensionnal array.

But maybe I'm wrong, I'm not able to find the document I read this.
If you have some informations about that, I think I will change my mind. :)
 
well what do you mean by multi dim array..? lol, cause that's what is used here... :p
it's just that a multi dim array, is just simple array (instead of intricated ones( like [ [] ] ) with better indexing system. ( basically putting all the dimensions into one single dimensions)
handling 1 array is better than handling severals :p IMO.

It's written in the Help file, that the table class was created to handle large size arrays ( like 500*500*3 items = 750000 items ( largest map possible with all 3 layers used ))
 
These classes are really useful, good job! ;)
However, I tried them both inside and outside RPG Maker and they didn't seem to work properly.

In RPG Maker:
- For the Color class, the color was always pure black
- For the Tone class, tone change didn't work
- For the Table class, the game freezes at startup before the title screen

Outside RPG Maker, everything works fine with the Table class until I save an RPG class with this.
For example, when I change RPG::Class using this Table class, when I go back in RPG Maker and try to open my project, it says Could not read Classes data. I often see that with corrupted data.

Any idea on what's wrong?
 

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